Test Failed
Push — master ( 4c2e75...b67bff )
by
unknown
22:57 queued 12s
created
server/includes/mapi/class.meetingrequest.php 2 patches
Indentation   +3783 added lines, -3783 removed lines patch added patch discarded remove patch
@@ -1,7 +1,7 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 
3 3
 class Meetingrequest {
4
-	/*
4
+    /*
5 5
 	 * NOTE
6 6
 	 *
7 7
 	 * This class is designed to modify and update meeting request properties
@@ -17,7 +17,7 @@  discard block
 block discarded – undo
17 17
 	 *
18 18
 	 */
19 19
 
20
-	/*
20
+    /*
21 21
 	 * How to use
22 22
 	 * ----------
23 23
 	 *
@@ -68,654 +68,654 @@  discard block
 block discarded – undo
68 68
 	 *     meeting object from calendar
69 69
 	 */
70 70
 
71
-	// All properties for a recipient that are interesting
72
-	public $recipprops = [
73
-		PR_ENTRYID,
74
-		PR_DISPLAY_NAME,
75
-		PR_EMAIL_ADDRESS,
76
-		PR_RECIPIENT_ENTRYID,
77
-		PR_RECIPIENT_TYPE,
78
-		PR_SEND_INTERNET_ENCODING,
79
-		PR_SEND_RICH_INFO,
80
-		PR_RECIPIENT_DISPLAY_NAME,
81
-		PR_ADDRTYPE,
82
-		PR_DISPLAY_TYPE,
83
-		PR_DISPLAY_TYPE_EX,
84
-		PR_RECIPIENT_TRACKSTATUS,
85
-		PR_RECIPIENT_TRACKSTATUS_TIME,
86
-		PR_RECIPIENT_FLAGS,
87
-		PR_ROWID,
88
-		PR_OBJECT_TYPE,
89
-		PR_SEARCH_KEY,
90
-	];
91
-
92
-	/**
93
-	 * Indication whether the setting of resources in a Meeting Request is success (false) or if it
94
-	 * has failed (integer).
95
-	 */
96
-	public $errorSetResource;
97
-
98
-	/**
99
-	 * Constructor.
100
-	 *
101
-	 * Takes a store and a message. The message is an appointment item
102
-	 * that should be converted into a meeting request or an incoming
103
-	 * e-mail message that is a meeting request.
104
-	 *
105
-	 * The $session variable is optional, but required if the following features
106
-	 * are to be used:
107
-	 *
108
-	 * - Sending meeting requests for meetings that are not in your own store
109
-	 * - Sending meeting requests to resources, resource availability checking and resource freebusy updates
110
-	 *
111
-	 * @param mixed $store
112
-	 * @param mixed $message
113
-	 * @param mixed $session
114
-	 * @param mixed $enableDirectBooking
115
-	 */
116
-	public function __construct($store, $message, $session = false, $enableDirectBooking = true) {
117
-		$this->store = $store;
118
-		$this->message = $message;
119
-		$this->session = $session;
120
-		// This variable string saves time information for the MR.
121
-		$this->meetingTimeInfo = false;
122
-		$this->enableDirectBooking = $enableDirectBooking;
123
-
124
-		$properties['goid'] = 'PT_BINARY:PSETID_Meeting:0x3';
125
-		$properties['goid2'] = 'PT_BINARY:PSETID_Meeting:0x23';
126
-		$properties['type'] = 'PT_STRING8:PSETID_Meeting:0x24';
127
-		$properties['meetingrecurring'] = 'PT_BOOLEAN:PSETID_Meeting:0x5';
128
-		$properties['attendee_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1';
129
-		$properties['owner_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1a';
130
-		$properties['meetingstatus'] = 'PT_LONG:PSETID_Appointment:0x8217';
131
-		$properties['responsestatus'] = 'PT_LONG:PSETID_Appointment:0x8218';
132
-		$properties['replytime'] = 'PT_SYSTIME:PSETID_Appointment:0x8220';
133
-		$properties['recurrence_data'] = 'PT_BINARY:PSETID_Appointment:0x8216';
134
-		$properties['reminderminutes'] = 'PT_LONG:PSETID_Common:0x8501';
135
-		$properties['reminderset'] = 'PT_BOOLEAN:PSETID_Common:0x8503';
136
-		$properties['updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8201';					// AppointmentSequenceNumber
137
-		$properties['last_updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8203';			// AppointmentLastSequence
138
-		$properties['busystatus'] = 'PT_LONG:PSETID_Appointment:0x8205';
139
-		$properties['intendedbusystatus'] = 'PT_LONG:PSETID_Appointment:0x8224';
140
-		$properties['start'] = 'PT_SYSTIME:PSETID_Appointment:0x820d';
141
-		$properties['responselocation'] = 'PT_STRING8:PSETID_Meeting:0x2';
142
-		$properties['location'] = 'PT_STRING8:PSETID_Appointment:0x8208';
143
-		$properties['requestsent'] = 'PT_BOOLEAN:PSETID_Appointment:0x8229';		// PidLidFInvited, MeetingRequestWasSent
144
-		$properties['startdate'] = 'PT_SYSTIME:PSETID_Appointment:0x820d';
145
-		$properties['duedate'] = 'PT_SYSTIME:PSETID_Appointment:0x820e';
146
-		$properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
147
-		$properties['commonstart'] = 'PT_SYSTIME:PSETID_Common:0x8516';
148
-		$properties['commonend'] = 'PT_SYSTIME:PSETID_Common:0x8517';
149
-		$properties['recurring'] = 'PT_BOOLEAN:PSETID_Appointment:0x8223';
150
-		$properties['clipstart'] = 'PT_SYSTIME:PSETID_Appointment:0x8235';
151
-		$properties['clipend'] = 'PT_SYSTIME:PSETID_Appointment:0x8236';
152
-		$properties['start_recur_date'] = 'PT_LONG:PSETID_Meeting:0xD';				// StartRecurTime
153
-		$properties['start_recur_time'] = 'PT_LONG:PSETID_Meeting:0xE';				// StartRecurTime
154
-		$properties['end_recur_date'] = 'PT_LONG:PSETID_Meeting:0xF';				// EndRecurDate
155
-		$properties['end_recur_time'] = 'PT_LONG:PSETID_Meeting:0x10';				// EndRecurTime
156
-		$properties['is_exception'] = 'PT_BOOLEAN:PSETID_Meeting:0xA';				// LID_IS_EXCEPTION
157
-		$properties['apptreplyname'] = 'PT_STRING8:PSETID_Appointment:0x8230';
158
-		// Propose new time properties
159
-		$properties['proposed_start_whole'] = 'PT_SYSTIME:PSETID_Appointment:0x8250';
160
-		$properties['proposed_end_whole'] = 'PT_SYSTIME:PSETID_Appointment:0x8251';
161
-		$properties['proposed_duration'] = 'PT_LONG:PSETID_Appointment:0x8256';
162
-		$properties['counter_proposal'] = 'PT_BOOLEAN:PSETID_Appointment:0x8257';
163
-		$properties['recurring_pattern'] = 'PT_STRING8:PSETID_Appointment:0x8232';
164
-		$properties['basedate'] = 'PT_SYSTIME:PSETID_Appointment:0x8228';
165
-		$properties['meetingtype'] = 'PT_LONG:PSETID_Meeting:0x26';
166
-		$properties['timezone_data'] = 'PT_BINARY:PSETID_Appointment:0x8233';
167
-		$properties['timezone'] = 'PT_STRING8:PSETID_Appointment:0x8234';
168
-		$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
169
-		$properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
170
-		$properties["alldayevent"] = "PT_BOOLEAN:PSETID_Appointment:0x8215";
171
-
172
-		$this->proptags = getPropIdsFromStrings($store, $properties);
173
-	}
174
-
175
-	/**
176
-	 * Sets the direct booking property. This is an alternative to the setting of the direct booking
177
-	 * property through the constructor. However, setting it in the constructor is preferred.
178
-	 *
179
-	 * @param bool $directBookingSetting
180
-	 */
181
-	public function setDirectBooking($directBookingSetting) {
182
-		$this->enableDirectBooking = $directBookingSetting;
183
-	}
184
-
185
-	/**
186
-	 * Returns TRUE if the message pointed to is an incoming meeting request and should
187
-	 * therefore be replied to with doAccept or doDecline().
188
-	 *
189
-	 * @param string $messageClass message class to use for checking
190
-	 *
191
-	 * @return bool returns true if this is a meeting request else false
192
-	 */
193
-	public function isMeetingRequest($messageClass = false) {
194
-		if ($messageClass === false) {
195
-			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
196
-			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
197
-		}
198
-
199
-		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.request') === 0) {
200
-			return true;
201
-		}
202
-
203
-		return false;
204
-	}
205
-
206
-	/**
207
-	 * Returns TRUE if the message pointed to is a returning meeting request response.
208
-	 *
209
-	 * @param string $messageClass message class to use for checking
210
-	 *
211
-	 * @return bool returns true if this is a meeting request else false
212
-	 */
213
-	public function isMeetingRequestResponse($messageClass = false) {
214
-		if ($messageClass === false) {
215
-			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
216
-			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
217
-		}
218
-
219
-		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.resp') === 0) {
220
-			return true;
221
-		}
222
-
223
-		return false;
224
-	}
225
-
226
-	/**
227
-	 * Returns TRUE if the message pointed to is a cancellation request.
228
-	 *
229
-	 * @param string $messageClass message class to use for checking
230
-	 *
231
-	 * @return bool returns true if this is a meeting request else false
232
-	 */
233
-	public function isMeetingCancellation($messageClass = false) {
234
-		if ($messageClass === false) {
235
-			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
236
-			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
237
-		}
238
-
239
-		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.canceled') === 0) {
240
-			return true;
241
-		}
242
-
243
-		return false;
244
-	}
245
-
246
-	/**
247
-	 * Function is used to get the last update counter of meeting request.
248
-	 *
249
-	 * @return bool|Number false when last_updatecounter not found else return last_updatecounter
250
-	 */
251
-	public function getLastUpdateCounter() {
252
-		$calendarItemProps = mapi_getprops($this->message, [$this->proptags['last_updatecounter']]);
253
-		if (isset($calendarItemProps) && !empty($calendarItemProps)) {
254
-			return $calendarItemProps[$this->proptags['last_updatecounter']];
255
-		}
256
-
257
-		return false;
258
-	}
259
-
260
-	/**
261
-	 * Process an incoming meeting request response. This updates the appointment
262
-	 * in your calendar to show whether the user has accepted or declined.
263
-	 */
264
-	public function processMeetingRequestResponse() {
265
-		if (!$this->isMeetingRequestResponse()) {
266
-			return;
267
-		}
268
-
269
-		if (!$this->isLocalOrganiser()) {
270
-			return;
271
-		}
272
-
273
-		// Get information we need from the response message
274
-		$messageprops = mapi_getprops($this->message, [
275
-			$this->proptags['goid'],
276
-			$this->proptags['goid2'],
277
-			PR_OWNER_APPT_ID,
278
-			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
279
-			PR_SENT_REPRESENTING_NAME,
280
-			PR_SENT_REPRESENTING_ADDRTYPE,
281
-			PR_SENT_REPRESENTING_ENTRYID,
282
-			PR_SENT_REPRESENTING_SEARCH_KEY,
283
-			PR_MESSAGE_DELIVERY_TIME,
284
-			PR_MESSAGE_CLASS,
285
-			PR_PROCESSED,
286
-			PR_RCVD_REPRESENTING_ENTRYID,
287
-			$this->proptags['proposed_start_whole'],
288
-			$this->proptags['proposed_end_whole'],
289
-			$this->proptags['proposed_duration'],
290
-			$this->proptags['counter_proposal'],
291
-			$this->proptags['attendee_critical_change'],
292
-		]);
293
-
294
-		$goid2 = $messageprops[$this->proptags['goid2']];
295
-
296
-		if (!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS])) {
297
-			return;
298
-		}
299
-
300
-		// Find basedate in GlobalID(0x3), this can be a response for an occurrence
301
-		$basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
302
-
303
-		// check if delegate is processing the response
304
-		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
305
-			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
306
-			$userStore = $delegatorStore['store'];
307
-		}
308
-		else {
309
-			$userStore = $this->store;
310
-		}
311
-
312
-		// check for calendar access
313
-		if ($this->checkCalendarWriteAccess($userStore) !== true) {
314
-			// Throw an exception that we don't have write permissions on calendar folder,
315
-			// allow caller to fill the error message
316
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
317
-		}
318
-
319
-		$calendarItem = $this->getCorrespondentCalendarItem(true);
320
-
321
-		// Open the calendar items, and update all the recipients of the calendar item that match
322
-		// the email address of the response.
323
-		if ($calendarItem !== false) {
324
-			$this->processResponse($userStore, $calendarItem, $basedate, $messageprops);
325
-		}
326
-	}
327
-
328
-	/**
329
-	 * Process every incoming MeetingRequest response.This updates the appointment
330
-	 * in your calendar to show whether the user has accepted or declined.
331
-	 *
332
-	 * @param resource    $store        contains the userStore in which the meeting is created
333
-	 * @param MAPIMessage $calendarItem resource of the calendar item for which this response has arrived
334
-	 * @param bool        $basedate     if present the create an exception
335
-	 * @param array       $messageprops contains message properties
336
-	 */
337
-	public function processResponse($store, $calendarItem, $basedate, $messageprops) {
338
-		$senderentryid = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
339
-		$messageclass = $messageprops[PR_MESSAGE_CLASS];
340
-		$deliverytime = $messageprops[PR_MESSAGE_DELIVERY_TIME];
341
-
342
-		// Open the calendar item, find the sender in the recipient table and update all the recipients of the calendar item that match
343
-		// the email address of the response.
344
-		$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter']]);
345
-
346
-		// check if meeting response is already processed
347
-		if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
348
-			// meeting is already processed
349
-			return;
350
-		}
351
-		mapi_setprops($this->message, [PR_PROCESSED => true]);
352
-		mapi_savechanges($this->message);
353
-
354
-		// if meeting is updated in organizer's calendar then we don't need to process
355
-		// old response
356
-		if ($this->isMeetingUpdated($basedate)) {
357
-			return;
358
-		}
359
-
360
-		// If basedate is found, then create/modify exception msg and do processing
361
-		if ($basedate && isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] === true) {
362
-			$recurr = new Recurrence($store, $calendarItem);
363
-
364
-			// Copy properties from meeting request
365
-			$exception_props = mapi_getprops($this->message, [
366
-				PR_OWNER_APPT_ID,
367
-				$this->proptags['proposed_start_whole'],
368
-				$this->proptags['proposed_end_whole'],
369
-				$this->proptags['proposed_duration'],
370
-				$this->proptags['counter_proposal'],
371
-			]);
372
-
373
-			// Create/modify exception
374
-			if ($recurr->isException($basedate)) {
375
-				$recurr->modifyException($exception_props, $basedate);
376
-			}
377
-			else {
378
-				// When we are creating an exception we need copy recipients from main recurring item
379
-				$recipTable = mapi_message_getrecipienttable($calendarItem);
380
-				$recips = mapi_table_queryallrows($recipTable, $this->recipprops);
381
-
382
-				// Retrieve actual start/due dates from calendar item.
383
-				$exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
384
-				$exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
385
-
386
-				$recurr->createException($exception_props, $basedate, false, $recips);
387
-			}
388
-
389
-			mapi_savechanges($calendarItem);
390
-
391
-			$attach = $recurr->getExceptionAttachment($basedate);
392
-			if ($attach) {
393
-				$recurringItem = $calendarItem;
394
-				$calendarItem = mapi_attach_openobj($attach, MAPI_MODIFY);
395
-			}
396
-			else {
397
-				return false;
398
-			}
399
-		}
400
-
401
-		// Get the recipients of the calendar item
402
-		$reciptable = mapi_message_getrecipienttable($calendarItem);
403
-		$recipients = mapi_table_queryallrows($reciptable, $this->recipprops);
404
-
405
-		// FIXME we should look at the updatecounter property and compare it
406
-		// to the counter in the recipient to see if this update is actually
407
-		// newer than the status in the calendar item
408
-		$found = false;
409
-
410
-		$totalrecips = 0;
411
-		$acceptedrecips = 0;
412
-		foreach ($recipients as $recipient) {
413
-			++$totalrecips;
414
-			if (isset($recipient[PR_ENTRYID]) && $this->compareABEntryIDs($recipient[PR_ENTRYID], $senderentryid)) {
415
-				$found = true;
416
-
417
-				/*
71
+    // All properties for a recipient that are interesting
72
+    public $recipprops = [
73
+        PR_ENTRYID,
74
+        PR_DISPLAY_NAME,
75
+        PR_EMAIL_ADDRESS,
76
+        PR_RECIPIENT_ENTRYID,
77
+        PR_RECIPIENT_TYPE,
78
+        PR_SEND_INTERNET_ENCODING,
79
+        PR_SEND_RICH_INFO,
80
+        PR_RECIPIENT_DISPLAY_NAME,
81
+        PR_ADDRTYPE,
82
+        PR_DISPLAY_TYPE,
83
+        PR_DISPLAY_TYPE_EX,
84
+        PR_RECIPIENT_TRACKSTATUS,
85
+        PR_RECIPIENT_TRACKSTATUS_TIME,
86
+        PR_RECIPIENT_FLAGS,
87
+        PR_ROWID,
88
+        PR_OBJECT_TYPE,
89
+        PR_SEARCH_KEY,
90
+    ];
91
+
92
+    /**
93
+     * Indication whether the setting of resources in a Meeting Request is success (false) or if it
94
+     * has failed (integer).
95
+     */
96
+    public $errorSetResource;
97
+
98
+    /**
99
+     * Constructor.
100
+     *
101
+     * Takes a store and a message. The message is an appointment item
102
+     * that should be converted into a meeting request or an incoming
103
+     * e-mail message that is a meeting request.
104
+     *
105
+     * The $session variable is optional, but required if the following features
106
+     * are to be used:
107
+     *
108
+     * - Sending meeting requests for meetings that are not in your own store
109
+     * - Sending meeting requests to resources, resource availability checking and resource freebusy updates
110
+     *
111
+     * @param mixed $store
112
+     * @param mixed $message
113
+     * @param mixed $session
114
+     * @param mixed $enableDirectBooking
115
+     */
116
+    public function __construct($store, $message, $session = false, $enableDirectBooking = true) {
117
+        $this->store = $store;
118
+        $this->message = $message;
119
+        $this->session = $session;
120
+        // This variable string saves time information for the MR.
121
+        $this->meetingTimeInfo = false;
122
+        $this->enableDirectBooking = $enableDirectBooking;
123
+
124
+        $properties['goid'] = 'PT_BINARY:PSETID_Meeting:0x3';
125
+        $properties['goid2'] = 'PT_BINARY:PSETID_Meeting:0x23';
126
+        $properties['type'] = 'PT_STRING8:PSETID_Meeting:0x24';
127
+        $properties['meetingrecurring'] = 'PT_BOOLEAN:PSETID_Meeting:0x5';
128
+        $properties['attendee_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1';
129
+        $properties['owner_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1a';
130
+        $properties['meetingstatus'] = 'PT_LONG:PSETID_Appointment:0x8217';
131
+        $properties['responsestatus'] = 'PT_LONG:PSETID_Appointment:0x8218';
132
+        $properties['replytime'] = 'PT_SYSTIME:PSETID_Appointment:0x8220';
133
+        $properties['recurrence_data'] = 'PT_BINARY:PSETID_Appointment:0x8216';
134
+        $properties['reminderminutes'] = 'PT_LONG:PSETID_Common:0x8501';
135
+        $properties['reminderset'] = 'PT_BOOLEAN:PSETID_Common:0x8503';
136
+        $properties['updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8201';					// AppointmentSequenceNumber
137
+        $properties['last_updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8203';			// AppointmentLastSequence
138
+        $properties['busystatus'] = 'PT_LONG:PSETID_Appointment:0x8205';
139
+        $properties['intendedbusystatus'] = 'PT_LONG:PSETID_Appointment:0x8224';
140
+        $properties['start'] = 'PT_SYSTIME:PSETID_Appointment:0x820d';
141
+        $properties['responselocation'] = 'PT_STRING8:PSETID_Meeting:0x2';
142
+        $properties['location'] = 'PT_STRING8:PSETID_Appointment:0x8208';
143
+        $properties['requestsent'] = 'PT_BOOLEAN:PSETID_Appointment:0x8229';		// PidLidFInvited, MeetingRequestWasSent
144
+        $properties['startdate'] = 'PT_SYSTIME:PSETID_Appointment:0x820d';
145
+        $properties['duedate'] = 'PT_SYSTIME:PSETID_Appointment:0x820e';
146
+        $properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560";
147
+        $properties['commonstart'] = 'PT_SYSTIME:PSETID_Common:0x8516';
148
+        $properties['commonend'] = 'PT_SYSTIME:PSETID_Common:0x8517';
149
+        $properties['recurring'] = 'PT_BOOLEAN:PSETID_Appointment:0x8223';
150
+        $properties['clipstart'] = 'PT_SYSTIME:PSETID_Appointment:0x8235';
151
+        $properties['clipend'] = 'PT_SYSTIME:PSETID_Appointment:0x8236';
152
+        $properties['start_recur_date'] = 'PT_LONG:PSETID_Meeting:0xD';				// StartRecurTime
153
+        $properties['start_recur_time'] = 'PT_LONG:PSETID_Meeting:0xE';				// StartRecurTime
154
+        $properties['end_recur_date'] = 'PT_LONG:PSETID_Meeting:0xF';				// EndRecurDate
155
+        $properties['end_recur_time'] = 'PT_LONG:PSETID_Meeting:0x10';				// EndRecurTime
156
+        $properties['is_exception'] = 'PT_BOOLEAN:PSETID_Meeting:0xA';				// LID_IS_EXCEPTION
157
+        $properties['apptreplyname'] = 'PT_STRING8:PSETID_Appointment:0x8230';
158
+        // Propose new time properties
159
+        $properties['proposed_start_whole'] = 'PT_SYSTIME:PSETID_Appointment:0x8250';
160
+        $properties['proposed_end_whole'] = 'PT_SYSTIME:PSETID_Appointment:0x8251';
161
+        $properties['proposed_duration'] = 'PT_LONG:PSETID_Appointment:0x8256';
162
+        $properties['counter_proposal'] = 'PT_BOOLEAN:PSETID_Appointment:0x8257';
163
+        $properties['recurring_pattern'] = 'PT_STRING8:PSETID_Appointment:0x8232';
164
+        $properties['basedate'] = 'PT_SYSTIME:PSETID_Appointment:0x8228';
165
+        $properties['meetingtype'] = 'PT_LONG:PSETID_Meeting:0x26';
166
+        $properties['timezone_data'] = 'PT_BINARY:PSETID_Appointment:0x8233';
167
+        $properties['timezone'] = 'PT_STRING8:PSETID_Appointment:0x8234';
168
+        $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
169
+        $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506";
170
+        $properties["alldayevent"] = "PT_BOOLEAN:PSETID_Appointment:0x8215";
171
+
172
+        $this->proptags = getPropIdsFromStrings($store, $properties);
173
+    }
174
+
175
+    /**
176
+     * Sets the direct booking property. This is an alternative to the setting of the direct booking
177
+     * property through the constructor. However, setting it in the constructor is preferred.
178
+     *
179
+     * @param bool $directBookingSetting
180
+     */
181
+    public function setDirectBooking($directBookingSetting) {
182
+        $this->enableDirectBooking = $directBookingSetting;
183
+    }
184
+
185
+    /**
186
+     * Returns TRUE if the message pointed to is an incoming meeting request and should
187
+     * therefore be replied to with doAccept or doDecline().
188
+     *
189
+     * @param string $messageClass message class to use for checking
190
+     *
191
+     * @return bool returns true if this is a meeting request else false
192
+     */
193
+    public function isMeetingRequest($messageClass = false) {
194
+        if ($messageClass === false) {
195
+            $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
196
+            $messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
197
+        }
198
+
199
+        if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.request') === 0) {
200
+            return true;
201
+        }
202
+
203
+        return false;
204
+    }
205
+
206
+    /**
207
+     * Returns TRUE if the message pointed to is a returning meeting request response.
208
+     *
209
+     * @param string $messageClass message class to use for checking
210
+     *
211
+     * @return bool returns true if this is a meeting request else false
212
+     */
213
+    public function isMeetingRequestResponse($messageClass = false) {
214
+        if ($messageClass === false) {
215
+            $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
216
+            $messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
217
+        }
218
+
219
+        if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.resp') === 0) {
220
+            return true;
221
+        }
222
+
223
+        return false;
224
+    }
225
+
226
+    /**
227
+     * Returns TRUE if the message pointed to is a cancellation request.
228
+     *
229
+     * @param string $messageClass message class to use for checking
230
+     *
231
+     * @return bool returns true if this is a meeting request else false
232
+     */
233
+    public function isMeetingCancellation($messageClass = false) {
234
+        if ($messageClass === false) {
235
+            $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
236
+            $messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
237
+        }
238
+
239
+        if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.canceled') === 0) {
240
+            return true;
241
+        }
242
+
243
+        return false;
244
+    }
245
+
246
+    /**
247
+     * Function is used to get the last update counter of meeting request.
248
+     *
249
+     * @return bool|Number false when last_updatecounter not found else return last_updatecounter
250
+     */
251
+    public function getLastUpdateCounter() {
252
+        $calendarItemProps = mapi_getprops($this->message, [$this->proptags['last_updatecounter']]);
253
+        if (isset($calendarItemProps) && !empty($calendarItemProps)) {
254
+            return $calendarItemProps[$this->proptags['last_updatecounter']];
255
+        }
256
+
257
+        return false;
258
+    }
259
+
260
+    /**
261
+     * Process an incoming meeting request response. This updates the appointment
262
+     * in your calendar to show whether the user has accepted or declined.
263
+     */
264
+    public function processMeetingRequestResponse() {
265
+        if (!$this->isMeetingRequestResponse()) {
266
+            return;
267
+        }
268
+
269
+        if (!$this->isLocalOrganiser()) {
270
+            return;
271
+        }
272
+
273
+        // Get information we need from the response message
274
+        $messageprops = mapi_getprops($this->message, [
275
+            $this->proptags['goid'],
276
+            $this->proptags['goid2'],
277
+            PR_OWNER_APPT_ID,
278
+            PR_SENT_REPRESENTING_EMAIL_ADDRESS,
279
+            PR_SENT_REPRESENTING_NAME,
280
+            PR_SENT_REPRESENTING_ADDRTYPE,
281
+            PR_SENT_REPRESENTING_ENTRYID,
282
+            PR_SENT_REPRESENTING_SEARCH_KEY,
283
+            PR_MESSAGE_DELIVERY_TIME,
284
+            PR_MESSAGE_CLASS,
285
+            PR_PROCESSED,
286
+            PR_RCVD_REPRESENTING_ENTRYID,
287
+            $this->proptags['proposed_start_whole'],
288
+            $this->proptags['proposed_end_whole'],
289
+            $this->proptags['proposed_duration'],
290
+            $this->proptags['counter_proposal'],
291
+            $this->proptags['attendee_critical_change'],
292
+        ]);
293
+
294
+        $goid2 = $messageprops[$this->proptags['goid2']];
295
+
296
+        if (!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS])) {
297
+            return;
298
+        }
299
+
300
+        // Find basedate in GlobalID(0x3), this can be a response for an occurrence
301
+        $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
302
+
303
+        // check if delegate is processing the response
304
+        if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
305
+            $delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
306
+            $userStore = $delegatorStore['store'];
307
+        }
308
+        else {
309
+            $userStore = $this->store;
310
+        }
311
+
312
+        // check for calendar access
313
+        if ($this->checkCalendarWriteAccess($userStore) !== true) {
314
+            // Throw an exception that we don't have write permissions on calendar folder,
315
+            // allow caller to fill the error message
316
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
317
+        }
318
+
319
+        $calendarItem = $this->getCorrespondentCalendarItem(true);
320
+
321
+        // Open the calendar items, and update all the recipients of the calendar item that match
322
+        // the email address of the response.
323
+        if ($calendarItem !== false) {
324
+            $this->processResponse($userStore, $calendarItem, $basedate, $messageprops);
325
+        }
326
+    }
327
+
328
+    /**
329
+     * Process every incoming MeetingRequest response.This updates the appointment
330
+     * in your calendar to show whether the user has accepted or declined.
331
+     *
332
+     * @param resource    $store        contains the userStore in which the meeting is created
333
+     * @param MAPIMessage $calendarItem resource of the calendar item for which this response has arrived
334
+     * @param bool        $basedate     if present the create an exception
335
+     * @param array       $messageprops contains message properties
336
+     */
337
+    public function processResponse($store, $calendarItem, $basedate, $messageprops) {
338
+        $senderentryid = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
339
+        $messageclass = $messageprops[PR_MESSAGE_CLASS];
340
+        $deliverytime = $messageprops[PR_MESSAGE_DELIVERY_TIME];
341
+
342
+        // Open the calendar item, find the sender in the recipient table and update all the recipients of the calendar item that match
343
+        // the email address of the response.
344
+        $calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter']]);
345
+
346
+        // check if meeting response is already processed
347
+        if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
348
+            // meeting is already processed
349
+            return;
350
+        }
351
+        mapi_setprops($this->message, [PR_PROCESSED => true]);
352
+        mapi_savechanges($this->message);
353
+
354
+        // if meeting is updated in organizer's calendar then we don't need to process
355
+        // old response
356
+        if ($this->isMeetingUpdated($basedate)) {
357
+            return;
358
+        }
359
+
360
+        // If basedate is found, then create/modify exception msg and do processing
361
+        if ($basedate && isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] === true) {
362
+            $recurr = new Recurrence($store, $calendarItem);
363
+
364
+            // Copy properties from meeting request
365
+            $exception_props = mapi_getprops($this->message, [
366
+                PR_OWNER_APPT_ID,
367
+                $this->proptags['proposed_start_whole'],
368
+                $this->proptags['proposed_end_whole'],
369
+                $this->proptags['proposed_duration'],
370
+                $this->proptags['counter_proposal'],
371
+            ]);
372
+
373
+            // Create/modify exception
374
+            if ($recurr->isException($basedate)) {
375
+                $recurr->modifyException($exception_props, $basedate);
376
+            }
377
+            else {
378
+                // When we are creating an exception we need copy recipients from main recurring item
379
+                $recipTable = mapi_message_getrecipienttable($calendarItem);
380
+                $recips = mapi_table_queryallrows($recipTable, $this->recipprops);
381
+
382
+                // Retrieve actual start/due dates from calendar item.
383
+                $exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
384
+                $exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
385
+
386
+                $recurr->createException($exception_props, $basedate, false, $recips);
387
+            }
388
+
389
+            mapi_savechanges($calendarItem);
390
+
391
+            $attach = $recurr->getExceptionAttachment($basedate);
392
+            if ($attach) {
393
+                $recurringItem = $calendarItem;
394
+                $calendarItem = mapi_attach_openobj($attach, MAPI_MODIFY);
395
+            }
396
+            else {
397
+                return false;
398
+            }
399
+        }
400
+
401
+        // Get the recipients of the calendar item
402
+        $reciptable = mapi_message_getrecipienttable($calendarItem);
403
+        $recipients = mapi_table_queryallrows($reciptable, $this->recipprops);
404
+
405
+        // FIXME we should look at the updatecounter property and compare it
406
+        // to the counter in the recipient to see if this update is actually
407
+        // newer than the status in the calendar item
408
+        $found = false;
409
+
410
+        $totalrecips = 0;
411
+        $acceptedrecips = 0;
412
+        foreach ($recipients as $recipient) {
413
+            ++$totalrecips;
414
+            if (isset($recipient[PR_ENTRYID]) && $this->compareABEntryIDs($recipient[PR_ENTRYID], $senderentryid)) {
415
+                $found = true;
416
+
417
+                /*
418 418
 				 * If value of attendee_critical_change on meeting response mail is less than PR_RECIPIENT_TRACKSTATUS_TIME
419 419
 				 * on the corresponding recipientRow of meeting then we ignore this response mail.
420 420
 				 */
421
-				if (isset($recipient[PR_RECIPIENT_TRACKSTATUS_TIME]) && ($messageprops[$this->proptags['attendee_critical_change']] < $recipient[PR_RECIPIENT_TRACKSTATUS_TIME])) {
422
-					continue;
423
-				}
424
-
425
-				// The email address matches, update the row
426
-				$recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
427
-				if (isset($messageprops[$this->proptags['attendee_critical_change']])) {
428
-					$recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $messageprops[$this->proptags['attendee_critical_change']];
429
-				}
430
-
431
-				// If this is a counter proposal, set the proposal properties in the recipient row
432
-				if (isset($messageprops[$this->proptags['counter_proposal']]) && $messageprops[$this->proptags['counter_proposal']]) {
433
-					$recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
434
-					$recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
435
-					$recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
436
-				}
437
-
438
-				// Update the recipient information
439
-				mapi_message_modifyrecipients($calendarItem, MODRECIP_REMOVE, [$recipient]);
440
-				mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
441
-			}
442
-			if (isset($recipient[PR_RECIPIENT_TRACKSTATUS]) && $recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
443
-				++$acceptedrecips;
444
-			}
445
-		}
446
-
447
-		// If the recipient was not found in the original calendar item,
448
-		// then add the recpient as a new optional recipient
449
-		if (!$found) {
450
-			$recipient = [];
451
-			$recipient[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
452
-			$recipient[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
453
-			$recipient[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
454
-			$recipient[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
455
-			$recipient[PR_RECIPIENT_TYPE] = MAPI_CC;
456
-			$recipient[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
457
-			$recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
458
-			$recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $deliverytime;
459
-
460
-			// If this is a counter proposal, set the proposal properties in the recipient row
461
-			if (isset($messageprops[$this->proptags['counter_proposal']])) {
462
-				$recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
463
-				$recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
464
-				$recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
465
-			}
466
-
467
-			mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
468
-			++$totalrecips;
469
-			if ($recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
470
-				++$acceptedrecips;
471
-			}
472
-		}
473
-
474
-		// TODO: Update counter proposal number property on message
475
-		/*
421
+                if (isset($recipient[PR_RECIPIENT_TRACKSTATUS_TIME]) && ($messageprops[$this->proptags['attendee_critical_change']] < $recipient[PR_RECIPIENT_TRACKSTATUS_TIME])) {
422
+                    continue;
423
+                }
424
+
425
+                // The email address matches, update the row
426
+                $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
427
+                if (isset($messageprops[$this->proptags['attendee_critical_change']])) {
428
+                    $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $messageprops[$this->proptags['attendee_critical_change']];
429
+                }
430
+
431
+                // If this is a counter proposal, set the proposal properties in the recipient row
432
+                if (isset($messageprops[$this->proptags['counter_proposal']]) && $messageprops[$this->proptags['counter_proposal']]) {
433
+                    $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
434
+                    $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
435
+                    $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
436
+                }
437
+
438
+                // Update the recipient information
439
+                mapi_message_modifyrecipients($calendarItem, MODRECIP_REMOVE, [$recipient]);
440
+                mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
441
+            }
442
+            if (isset($recipient[PR_RECIPIENT_TRACKSTATUS]) && $recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
443
+                ++$acceptedrecips;
444
+            }
445
+        }
446
+
447
+        // If the recipient was not found in the original calendar item,
448
+        // then add the recpient as a new optional recipient
449
+        if (!$found) {
450
+            $recipient = [];
451
+            $recipient[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
452
+            $recipient[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
453
+            $recipient[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
454
+            $recipient[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
455
+            $recipient[PR_RECIPIENT_TYPE] = MAPI_CC;
456
+            $recipient[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
457
+            $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
458
+            $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $deliverytime;
459
+
460
+            // If this is a counter proposal, set the proposal properties in the recipient row
461
+            if (isset($messageprops[$this->proptags['counter_proposal']])) {
462
+                $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']];
463
+                $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']];
464
+                $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']];
465
+            }
466
+
467
+            mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
468
+            ++$totalrecips;
469
+            if ($recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
470
+                ++$acceptedrecips;
471
+            }
472
+        }
473
+
474
+        // TODO: Update counter proposal number property on message
475
+        /*
476 476
 		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.
477 477
 		*/
478
-		// If this is a counter proposal, set the counter proposal indicator boolean
479
-		if (isset($messageprops[$this->proptags['counter_proposal']])) {
480
-			$props = [];
481
-			if ($messageprops[$this->proptags['counter_proposal']]) {
482
-				$props[$this->proptags['counter_proposal']] = true;
483
-			}
484
-			else {
485
-				$props[$this->proptags['counter_proposal']] = false;
486
-			}
487
-
488
-			mapi_setprops($calendarItem, $props);
489
-		}
490
-
491
-		mapi_savechanges($calendarItem);
492
-		if (isset($attach)) {
493
-			mapi_savechanges($attach);
494
-			mapi_savechanges($recurringItem);
495
-		}
496
-	}
497
-
498
-	/**
499
-	 * Process an incoming meeting request cancellation. This updates the
500
-	 * appointment in your calendar to show that the meeting has been cancelled.
501
-	 */
502
-	public function processMeetingCancellation() {
503
-		if (!$this->isMeetingCancellation()) {
504
-			return;
505
-		}
506
-
507
-		if ($this->isLocalOrganiser()) {
508
-			return;
509
-		}
510
-
511
-		if (!$this->isInCalendar()) {
512
-			return;
513
-		}
514
-
515
-		$listProperties = $this->proptags;
516
-		$listProperties['subject'] = PR_SUBJECT;
517
-		$listProperties['sent_representing_name'] = PR_SENT_REPRESENTING_NAME;
518
-		$listProperties['sent_representing_address_type'] = PR_SENT_REPRESENTING_ADDRTYPE;
519
-		$listProperties['sent_representing_email_address'] = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
520
-		$listProperties['sent_representing_entryid'] = PR_SENT_REPRESENTING_ENTRYID;
521
-		$listProperties['sent_representing_search_key'] = PR_SENT_REPRESENTING_SEARCH_KEY;
522
-		$listProperties['rcvd_representing_name'] = PR_RCVD_REPRESENTING_NAME;
523
-		$listProperties['rcvd_representing_address_type'] = PR_RCVD_REPRESENTING_ADDRTYPE;
524
-		$listProperties['rcvd_representing_email_address'] = PR_RCVD_REPRESENTING_EMAIL_ADDRESS;
525
-		$listProperties['rcvd_representing_entryid'] = PR_RCVD_REPRESENTING_ENTRYID;
526
-		$listProperties['rcvd_representing_search_key'] = PR_RCVD_REPRESENTING_SEARCH_KEY;
527
-		$messageProps = mapi_getprops($this->message, $listProperties);
528
-
529
-		$goid = $messageProps[$this->proptags['goid']];	// GlobalID (0x3)
530
-		if (!isset($goid)) {
531
-			return;
532
-		}
533
-
534
-		// get delegator store, if delegate is processing this cancellation
535
-		if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
536
-			$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
537
-
538
-			$store = $delegatorStore['store'];
539
-			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
540
-		}
541
-		else {
542
-			$store = $this->store;
543
-			$calFolder = $this->openDefaultCalendar();
544
-		}
545
-
546
-		// check for calendar access
547
-		if ($this->checkCalendarWriteAccess($store) !== true) {
548
-			// Throw an exception that we don't have write permissions on calendar folder,
549
-			// allow caller to fill the error message
550
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
551
-		}
552
-
553
-		$calendarItem = $this->getCorrespondentCalendarItem(true);
554
-		$basedate = $this->getBasedateFromGlobalID($goid);
555
-
556
-		if ($calendarItem !== false) {
557
-			// if basedate is provided and we could not find the item then it could be that we are processing
558
-			// an exception so get the exception and process it
559
-			if ($basedate) {
560
-				$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring']]);
561
-				if ($calendarItemProps[$this->proptags['recurring']] === true) {
562
-					$recurr = new Recurrence($store, $calendarItem);
563
-
564
-					// Set message class
565
-					$messageProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
566
-
567
-					if ($recurr->isException($basedate)) {
568
-						$recurr->modifyException($messageProps, $basedate);
569
-					}
570
-					else {
571
-						$recurr->createException($messageProps, $basedate);
572
-					}
573
-				}
574
-			}
575
-			else {
576
-				// set the properties of the cancellation object
577
-				mapi_setprops($calendarItem, $messageProps);
578
-			}
579
-
580
-			mapi_savechanges($calendarItem);
581
-		}
582
-	}
583
-
584
-	/**
585
-	 * Returns true if the corresponding calendar items exists in the celendar folder for this
586
-	 * meeting request/response/cancellation.
587
-	 */
588
-	public function isInCalendar() {
589
-		// @TODO check for deleted exceptions
590
-		return $this->getCorrespondentCalendarItem(false) !== false;
591
-	}
592
-
593
-	/**
594
-	 * Accepts the meeting request by moving the item to the calendar
595
-	 * and sending a confirmation message back to the sender. If $tentative
596
-	 * is TRUE, then the item is accepted tentatively. After accepting, you
597
-	 * can't use this class instance any more. The message is closed. If you
598
-	 * specify TRUE for 'move', then the item is actually moved (from your
599
-	 * inbox probably) to the calendar. If you don't, it is copied into
600
-	 * your calendar.
601
-	 *
602
-	 * @param bool   $tentative            true if user as tentative accepted the meeting
603
-	 * @param bool   $sendresponse         true if a response has to be send to organizer
604
-	 * @param bool   $move                 true if the meeting request should be moved to the deleted items after processing
605
-	 * @param string $newProposedStartTime contains starttime if user has proposed other time
606
-	 * @param string $newProposedEndTime   contains endtime if user has proposed other time
607
-	 * @param string $basedate             start of day of occurrence for which user has accepted the recurrent meeting
608
-	 * @param bool   $isImported           true to indicate that MR is imported from .ics or .vcs file else it false.
609
-	 * @param mixed  $body
610
-	 * @param mixed  $userAction
611
-	 * @param mixed  $store
612
-	 *
613
-	 * @return string $entryid entryid of item which created/updated in calendar
614
-	 */
615
-	public function doAccept($tentative, $sendresponse, $move, $newProposedStartTime = false, $newProposedEndTime = false, $body = false, $userAction = false, $store = false, $basedate = false, $isImported = false) {
616
-		if ($this->isLocalOrganiser()) {
617
-			return false;
618
-		}
619
-
620
-		// Remove any previous calendar items with this goid and appt id
621
-		$messageprops = mapi_getprops($this->message, [PR_ENTRYID, PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['updatecounter'], PR_PROCESSED, PR_RCVD_REPRESENTING_ENTRYID, PR_SENDER_ENTRYID, PR_SENT_REPRESENTING_ENTRYID, PR_RECEIVED_BY_ENTRYID]);
622
-
623
-		// If this meeting request is received by a delegate then open delegator's store.
624
-		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
625
-			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
626
-
627
-			$store = $delegatorStore['store'];
628
-			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
629
-		}
630
-		else {
631
-			$calFolder = $this->openDefaultCalendar();
632
-			$store = $this->store;
633
-		}
634
-
635
-		// check for calendar access
636
-		if ($this->checkCalendarWriteAccess($store) !== true) {
637
-			// Throw an exception that we don't have write permissions on calendar folder,
638
-			// allow caller to fill the error message
639
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
640
-		}
641
-
642
-		// if meeting is out dated then don't process it
643
-		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $this->isMeetingOutOfDate()) {
644
-			return false;
645
-		}
646
-
647
-		/*
478
+        // If this is a counter proposal, set the counter proposal indicator boolean
479
+        if (isset($messageprops[$this->proptags['counter_proposal']])) {
480
+            $props = [];
481
+            if ($messageprops[$this->proptags['counter_proposal']]) {
482
+                $props[$this->proptags['counter_proposal']] = true;
483
+            }
484
+            else {
485
+                $props[$this->proptags['counter_proposal']] = false;
486
+            }
487
+
488
+            mapi_setprops($calendarItem, $props);
489
+        }
490
+
491
+        mapi_savechanges($calendarItem);
492
+        if (isset($attach)) {
493
+            mapi_savechanges($attach);
494
+            mapi_savechanges($recurringItem);
495
+        }
496
+    }
497
+
498
+    /**
499
+     * Process an incoming meeting request cancellation. This updates the
500
+     * appointment in your calendar to show that the meeting has been cancelled.
501
+     */
502
+    public function processMeetingCancellation() {
503
+        if (!$this->isMeetingCancellation()) {
504
+            return;
505
+        }
506
+
507
+        if ($this->isLocalOrganiser()) {
508
+            return;
509
+        }
510
+
511
+        if (!$this->isInCalendar()) {
512
+            return;
513
+        }
514
+
515
+        $listProperties = $this->proptags;
516
+        $listProperties['subject'] = PR_SUBJECT;
517
+        $listProperties['sent_representing_name'] = PR_SENT_REPRESENTING_NAME;
518
+        $listProperties['sent_representing_address_type'] = PR_SENT_REPRESENTING_ADDRTYPE;
519
+        $listProperties['sent_representing_email_address'] = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
520
+        $listProperties['sent_representing_entryid'] = PR_SENT_REPRESENTING_ENTRYID;
521
+        $listProperties['sent_representing_search_key'] = PR_SENT_REPRESENTING_SEARCH_KEY;
522
+        $listProperties['rcvd_representing_name'] = PR_RCVD_REPRESENTING_NAME;
523
+        $listProperties['rcvd_representing_address_type'] = PR_RCVD_REPRESENTING_ADDRTYPE;
524
+        $listProperties['rcvd_representing_email_address'] = PR_RCVD_REPRESENTING_EMAIL_ADDRESS;
525
+        $listProperties['rcvd_representing_entryid'] = PR_RCVD_REPRESENTING_ENTRYID;
526
+        $listProperties['rcvd_representing_search_key'] = PR_RCVD_REPRESENTING_SEARCH_KEY;
527
+        $messageProps = mapi_getprops($this->message, $listProperties);
528
+
529
+        $goid = $messageProps[$this->proptags['goid']];	// GlobalID (0x3)
530
+        if (!isset($goid)) {
531
+            return;
532
+        }
533
+
534
+        // get delegator store, if delegate is processing this cancellation
535
+        if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
536
+            $delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
537
+
538
+            $store = $delegatorStore['store'];
539
+            $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
540
+        }
541
+        else {
542
+            $store = $this->store;
543
+            $calFolder = $this->openDefaultCalendar();
544
+        }
545
+
546
+        // check for calendar access
547
+        if ($this->checkCalendarWriteAccess($store) !== true) {
548
+            // Throw an exception that we don't have write permissions on calendar folder,
549
+            // allow caller to fill the error message
550
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
551
+        }
552
+
553
+        $calendarItem = $this->getCorrespondentCalendarItem(true);
554
+        $basedate = $this->getBasedateFromGlobalID($goid);
555
+
556
+        if ($calendarItem !== false) {
557
+            // if basedate is provided and we could not find the item then it could be that we are processing
558
+            // an exception so get the exception and process it
559
+            if ($basedate) {
560
+                $calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring']]);
561
+                if ($calendarItemProps[$this->proptags['recurring']] === true) {
562
+                    $recurr = new Recurrence($store, $calendarItem);
563
+
564
+                    // Set message class
565
+                    $messageProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
566
+
567
+                    if ($recurr->isException($basedate)) {
568
+                        $recurr->modifyException($messageProps, $basedate);
569
+                    }
570
+                    else {
571
+                        $recurr->createException($messageProps, $basedate);
572
+                    }
573
+                }
574
+            }
575
+            else {
576
+                // set the properties of the cancellation object
577
+                mapi_setprops($calendarItem, $messageProps);
578
+            }
579
+
580
+            mapi_savechanges($calendarItem);
581
+        }
582
+    }
583
+
584
+    /**
585
+     * Returns true if the corresponding calendar items exists in the celendar folder for this
586
+     * meeting request/response/cancellation.
587
+     */
588
+    public function isInCalendar() {
589
+        // @TODO check for deleted exceptions
590
+        return $this->getCorrespondentCalendarItem(false) !== false;
591
+    }
592
+
593
+    /**
594
+     * Accepts the meeting request by moving the item to the calendar
595
+     * and sending a confirmation message back to the sender. If $tentative
596
+     * is TRUE, then the item is accepted tentatively. After accepting, you
597
+     * can't use this class instance any more. The message is closed. If you
598
+     * specify TRUE for 'move', then the item is actually moved (from your
599
+     * inbox probably) to the calendar. If you don't, it is copied into
600
+     * your calendar.
601
+     *
602
+     * @param bool   $tentative            true if user as tentative accepted the meeting
603
+     * @param bool   $sendresponse         true if a response has to be send to organizer
604
+     * @param bool   $move                 true if the meeting request should be moved to the deleted items after processing
605
+     * @param string $newProposedStartTime contains starttime if user has proposed other time
606
+     * @param string $newProposedEndTime   contains endtime if user has proposed other time
607
+     * @param string $basedate             start of day of occurrence for which user has accepted the recurrent meeting
608
+     * @param bool   $isImported           true to indicate that MR is imported from .ics or .vcs file else it false.
609
+     * @param mixed  $body
610
+     * @param mixed  $userAction
611
+     * @param mixed  $store
612
+     *
613
+     * @return string $entryid entryid of item which created/updated in calendar
614
+     */
615
+    public function doAccept($tentative, $sendresponse, $move, $newProposedStartTime = false, $newProposedEndTime = false, $body = false, $userAction = false, $store = false, $basedate = false, $isImported = false) {
616
+        if ($this->isLocalOrganiser()) {
617
+            return false;
618
+        }
619
+
620
+        // Remove any previous calendar items with this goid and appt id
621
+        $messageprops = mapi_getprops($this->message, [PR_ENTRYID, PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['updatecounter'], PR_PROCESSED, PR_RCVD_REPRESENTING_ENTRYID, PR_SENDER_ENTRYID, PR_SENT_REPRESENTING_ENTRYID, PR_RECEIVED_BY_ENTRYID]);
622
+
623
+        // If this meeting request is received by a delegate then open delegator's store.
624
+        if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
625
+            $delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
626
+
627
+            $store = $delegatorStore['store'];
628
+            $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
629
+        }
630
+        else {
631
+            $calFolder = $this->openDefaultCalendar();
632
+            $store = $this->store;
633
+        }
634
+
635
+        // check for calendar access
636
+        if ($this->checkCalendarWriteAccess($store) !== true) {
637
+            // Throw an exception that we don't have write permissions on calendar folder,
638
+            // allow caller to fill the error message
639
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
640
+        }
641
+
642
+        // if meeting is out dated then don't process it
643
+        if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $this->isMeetingOutOfDate()) {
644
+            return false;
645
+        }
646
+
647
+        /*
648 648
 		 *	if this function is called automatically with meeting request object then there will be
649 649
 		 *	two possibilitites
650 650
 		 *	1) meeting request is opened first time, in this case make a tentative appointment in
651 651
 		 *		recipient's calendar
652 652
 		 *	2) after this every subsequent request to open meeting request will not do any processing
653 653
 		 */
654
-		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction == false) {
655
-			if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
656
-				// if meeting request is already processed then don't do anything
657
-				return false;
658
-			}
659
-
660
-			// if correspondent calendar item is already processed then don't do anything
661
-			$calendarItem = $this->getCorrespondentCalendarItem();
662
-			if ($calendarItem) {
663
-				$calendarItemProps = mapi_getprops($calendarItem, [PR_PROCESSED]);
664
-				if (isset($calendarItemProps[PR_PROCESSED]) && $calendarItemProps[PR_PROCESSED] == true) {
665
-					// mark meeting-request mail as processed as well
666
-					mapi_setprops($this->message, [PR_PROCESSED => true]);
667
-					mapi_savechanges($this->message);
668
-
669
-					return false;
670
-				}
671
-			}
672
-		}
673
-
674
-		// Retrieve basedate from globalID, if it is not received as argument
675
-		if (!$basedate) {
676
-			$basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
677
-		}
678
-
679
-		// set counter proposal properties in calendar item when proposing new time
680
-		$proposeNewTimeProps = [];
681
-		if ($newProposedStartTime && $newProposedEndTime) {
682
-			$proposeNewTimeProps[$this->proptags['proposed_start_whole']] = $newProposedStartTime;
683
-			$proposeNewTimeProps[$this->proptags['proposed_end_whole']] = $newProposedEndTime;
684
-			$proposeNewTimeProps[$this->proptags['proposed_duration']] = round($newProposedEndTime - $newProposedStartTime) / 60;
685
-			$proposeNewTimeProps[$this->proptags['counter_proposal']] = true;
686
-		}
687
-
688
-		// While sender is receiver then we have to process the meeting request as per the intended busy status
689
-		// instead of tentative, and accept the same as per the intended busystatus.
690
-		$senderEntryId = isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) ? $messageprops[PR_SENT_REPRESENTING_ENTRYID] : $messageprops[PR_SENDER_ENTRYID];
691
-		if (isset($messageprops[PR_RECEIVED_BY_ENTRYID]) && $GLOBALS["entryid"]->compareEntryIds($senderEntryId, $messageprops[PR_RECEIVED_BY_ENTRYID])) {
692
-			$entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $body, true, $store, $calFolder, $basedate);
693
-		}
694
-		else {
695
-			$entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $body, $userAction, $store, $calFolder, $basedate);
696
-		}
697
-
698
-		// if we have first time processed this meeting then set PR_PROCESSED property
699
-		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction === false && $isImported === false) {
700
-			if (!isset($messageprops[PR_PROCESSED]) || $messageprops[PR_PROCESSED] != true) {
701
-				// set processed flag
702
-				mapi_setprops($this->message, [PR_PROCESSED => true]);
703
-				mapi_savechanges($this->message);
704
-			}
705
-		}
706
-
707
-		return $entryid;
708
-	}
709
-
710
-	public function accept($tentative, $sendresponse, $move, $proposeNewTimeProps = [], $body = false, $userAction = false, $store, $calFolder, $basedate = false) {
711
-		$messageprops = mapi_getprops($this->message);
712
-		$isDelegate = isset($messageprops[PR_RCVD_REPRESENTING_NAME]);
713
-
714
-		if ($sendresponse) {
715
-			$this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
716
-		}
717
-
718
-		/*
654
+        if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction == false) {
655
+            if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
656
+                // if meeting request is already processed then don't do anything
657
+                return false;
658
+            }
659
+
660
+            // if correspondent calendar item is already processed then don't do anything
661
+            $calendarItem = $this->getCorrespondentCalendarItem();
662
+            if ($calendarItem) {
663
+                $calendarItemProps = mapi_getprops($calendarItem, [PR_PROCESSED]);
664
+                if (isset($calendarItemProps[PR_PROCESSED]) && $calendarItemProps[PR_PROCESSED] == true) {
665
+                    // mark meeting-request mail as processed as well
666
+                    mapi_setprops($this->message, [PR_PROCESSED => true]);
667
+                    mapi_savechanges($this->message);
668
+
669
+                    return false;
670
+                }
671
+            }
672
+        }
673
+
674
+        // Retrieve basedate from globalID, if it is not received as argument
675
+        if (!$basedate) {
676
+            $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
677
+        }
678
+
679
+        // set counter proposal properties in calendar item when proposing new time
680
+        $proposeNewTimeProps = [];
681
+        if ($newProposedStartTime && $newProposedEndTime) {
682
+            $proposeNewTimeProps[$this->proptags['proposed_start_whole']] = $newProposedStartTime;
683
+            $proposeNewTimeProps[$this->proptags['proposed_end_whole']] = $newProposedEndTime;
684
+            $proposeNewTimeProps[$this->proptags['proposed_duration']] = round($newProposedEndTime - $newProposedStartTime) / 60;
685
+            $proposeNewTimeProps[$this->proptags['counter_proposal']] = true;
686
+        }
687
+
688
+        // While sender is receiver then we have to process the meeting request as per the intended busy status
689
+        // instead of tentative, and accept the same as per the intended busystatus.
690
+        $senderEntryId = isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) ? $messageprops[PR_SENT_REPRESENTING_ENTRYID] : $messageprops[PR_SENDER_ENTRYID];
691
+        if (isset($messageprops[PR_RECEIVED_BY_ENTRYID]) && $GLOBALS["entryid"]->compareEntryIds($senderEntryId, $messageprops[PR_RECEIVED_BY_ENTRYID])) {
692
+            $entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $body, true, $store, $calFolder, $basedate);
693
+        }
694
+        else {
695
+            $entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $body, $userAction, $store, $calFolder, $basedate);
696
+        }
697
+
698
+        // if we have first time processed this meeting then set PR_PROCESSED property
699
+        if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction === false && $isImported === false) {
700
+            if (!isset($messageprops[PR_PROCESSED]) || $messageprops[PR_PROCESSED] != true) {
701
+                // set processed flag
702
+                mapi_setprops($this->message, [PR_PROCESSED => true]);
703
+                mapi_savechanges($this->message);
704
+            }
705
+        }
706
+
707
+        return $entryid;
708
+    }
709
+
710
+    public function accept($tentative, $sendresponse, $move, $proposeNewTimeProps = [], $body = false, $userAction = false, $store, $calFolder, $basedate = false) {
711
+        $messageprops = mapi_getprops($this->message);
712
+        $isDelegate = isset($messageprops[PR_RCVD_REPRESENTING_NAME]);
713
+
714
+        if ($sendresponse) {
715
+            $this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
716
+        }
717
+
718
+        /*
719 719
 		 * Further processing depends on what user is receiving. User can receive recurring item, a single occurrence or a normal meeting.
720 720
 		 * 1) If meeting req is of recurrence then we find all the occurrence in calendar because in past user might have received one or few occurrences.
721 721
 		 * 2) If single occurrence then find occurrence itself using globalID and if item is not found then use cleanGlobalID to find main recurring item
@@ -725,712 +725,712 @@  discard block
 block discarded – undo
725 725
 		 * and that item is not found in calendar then item is move else item is opened and all properties, attachments and recipient are copied from meeting request.
726 726
 		 * If user is responding from calendar then item is opened and properties are set such as meetingstatus, responsestatus, busystatus etc.
727 727
 		 */
728
-		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
729
-			// While processing the item mark it as read.
730
-			mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT);
731
-
732
-			// This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
733
-			if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] == true) {
734
-				$calendarItem = false;
735
-
736
-				// Find main recurring item based on GlobalID (0x3)
737
-				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
738
-				if (is_array($items)) {
739
-					foreach ($items as $key => $entryid) {
740
-						$calendarItem = mapi_msgstore_openentry($store, $entryid);
741
-					}
742
-				}
743
-
744
-				$processed = false;
745
-				if (!$calendarItem) {
746
-					// Recurring item not found, so create new meeting in Calendar
747
-					$calendarItem = mapi_folder_createmessage($calFolder);
748
-				}
749
-				else {
750
-					// we have found the main recurring item, check if this meeting request is already processed
751
-					if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
752
-						// only set required properties, other properties are already copied when processing this meeting request
753
-						// for the first time
754
-						$processed = true;
755
-					}
756
-				}
757
-
758
-				if (!$processed) {
759
-					// get all the properties and copy that to calendar item
760
-					$props = mapi_getprops($this->message);
761
-					// reset the PidLidMeetingType to Unspecified for outlook display the item
762
-					$props[$this->proptags['meetingtype']] = mtgEmpty;
763
-					/*
728
+        if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
729
+            // While processing the item mark it as read.
730
+            mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT);
731
+
732
+            // This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
733
+            if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] == true) {
734
+                $calendarItem = false;
735
+
736
+                // Find main recurring item based on GlobalID (0x3)
737
+                $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
738
+                if (is_array($items)) {
739
+                    foreach ($items as $key => $entryid) {
740
+                        $calendarItem = mapi_msgstore_openentry($store, $entryid);
741
+                    }
742
+                }
743
+
744
+                $processed = false;
745
+                if (!$calendarItem) {
746
+                    // Recurring item not found, so create new meeting in Calendar
747
+                    $calendarItem = mapi_folder_createmessage($calFolder);
748
+                }
749
+                else {
750
+                    // we have found the main recurring item, check if this meeting request is already processed
751
+                    if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
752
+                        // only set required properties, other properties are already copied when processing this meeting request
753
+                        // for the first time
754
+                        $processed = true;
755
+                    }
756
+                }
757
+
758
+                if (!$processed) {
759
+                    // get all the properties and copy that to calendar item
760
+                    $props = mapi_getprops($this->message);
761
+                    // reset the PidLidMeetingType to Unspecified for outlook display the item
762
+                    $props[$this->proptags['meetingtype']] = mtgEmpty;
763
+                    /*
764 764
 					 * the client which has sent this meeting request can generate wrong flagdueby
765 765
 					 * time (mainly OL), so regenerate that property so we will always show reminder
766 766
 					 * on right time
767 767
 					 */
768
-					if (isset($props[$this->proptags['reminderminutes']])) {
769
-						$props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
770
-					}
771
-				}
772
-				else {
773
-					// only get required properties so we will not overwrite existing updated properties from calendar
774
-					$props = mapi_getprops($this->message, [PR_ENTRYID]);
775
-				}
776
-
777
-				// While we applying updates of MR then all local categories will be removed,
778
-				// So get the local categories of all occurrence before applying update from organiser.
779
-				$localCategories = $this->getLocalCategories($calendarItem, $store, $calFolder);
780
-
781
-				$props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
782
-				// When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
783
-				if (!isset($props[$this->proptags['updatecounter']])) {
784
-					$props[$this->proptags['updatecounter']] = 0;
785
-				}
786
-				$props[$this->proptags['meetingstatus']] = olMeetingReceived;
787
-				// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
788
-				$props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
789
-
790
-				if (isset($props[$this->proptags['intendedbusystatus']])) {
791
-					if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
792
-						$props[$this->proptags['busystatus']] = fbTentative;
793
-					}
794
-					else {
795
-						$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
796
-					}
797
-					// we already have intendedbusystatus value in $props so no need to copy it
798
-				}
799
-				else {
800
-					$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
801
-				}
802
-
803
-				if ($userAction) {
804
-					$addrInfo = $this->getOwnerAddress($this->store);
805
-
806
-					// if user has responded then set replytime and name
807
-					$props[$this->proptags['replytime']] = time();
808
-					if (!empty($addrInfo)) {
809
-						// @FIXME conditionally set this property only for delegation case
810
-						$props[$this->proptags['apptreplyname']] = $addrInfo[0];
811
-					}
812
-				}
813
-
814
-				mapi_setprops($calendarItem, $props);
815
-
816
-				// we have already processed attachments and recipients, so no need to do it again
817
-				if (!$processed) {
818
-					// Copy attachments too
819
-					$this->replaceAttachments($this->message, $calendarItem);
820
-					// Copy recipients too
821
-					$this->replaceRecipients($this->message, $calendarItem, $isDelegate);
822
-				}
823
-
824
-				// Find all occurrences based on CleanGlobalID (0x23)
825
-				// there will be no exceptions left if $processed is true, but even if it doesn't hurt to recheck
826
-				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
827
-				if (is_array($items)) {
828
-					// Save all existing occurrence as exceptions
829
-					foreach ($items as $entryid) {
830
-						// Open occurrence
831
-						$occurrenceItem = mapi_msgstore_openentry($store, $entryid);
832
-
833
-						// Save occurrence into main recurring item as exception
834
-						if ($occurrenceItem) {
835
-							$occurrenceItemProps = mapi_getprops($occurrenceItem, [$this->proptags['goid'], $this->proptags['recurring']]);
836
-
837
-							// Find basedate of occurrence item
838
-							$basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]);
839
-							if ($basedate && $occurrenceItemProps[$this->proptags['recurring']] != true) {
840
-								$this->mergeException($calendarItem, $occurrenceItem, $basedate, $store);
841
-							}
842
-						}
843
-					}
844
-				}
845
-
846
-				mapi_savechanges($calendarItem);
847
-
848
-				// After applying update of organiser all local categories of occurrence was removed,
849
-				// So if local categories exist then apply it on respective occurrence.
850
-				if (!empty($localCategories)) {
851
-					$this->applyLocalCategories($calendarItem, $store, $localCategories);
852
-				}
853
-
854
-				if ($move) {
855
-					// open wastebasket of currently logged in user and move the meeting request to it
856
-					// for delegates this will be delegate's wastebasket folder
857
-					$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
858
-					mapi_folder_copymessages($calFolder, [$props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
859
-				}
860
-
861
-				$entryid = $props[PR_ENTRYID];
862
-			}
863
-			else {
864
-				/**
865
-				 * This meeting request is not recurring, so can be an exception or normal meeting.
866
-				 * If exception then find main recurring item and update exception
867
-				 * If main recurring item is not found then put exception into Calendar as normal meeting.
868
-				 */
869
-				$calendarItem = false;
870
-
871
-				// We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence.
872
-				if ($basedate) {
873
-					// Find main recurring item from CleanGlobalID of this meeting request
874
-					$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
875
-					if (is_array($items)) {
876
-						foreach ($items as $key => $entryid) {
877
-							$calendarItem = mapi_msgstore_openentry($store, $entryid);
878
-						}
879
-					}
880
-
881
-					// Main recurring item is found, so now update exception
882
-					if ($calendarItem) {
883
-						$this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
884
-						$calendarItemProps = mapi_getprops($calendarItem, [PR_ENTRYID]);
885
-						$entryid = $calendarItemProps[PR_ENTRYID];
886
-					}
887
-				}
888
-
889
-				if (!$calendarItem) {
890
-					$items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
891
-					if (is_array($items)) {
892
-						// Get local categories before deleting MR.
893
-						$message = mapi_msgstore_openentry($store, $items[0]);
894
-						$localCategories = mapi_getprops($message, [$this->proptags['categories']]);
895
-						mapi_folder_deletemessages($calFolder, $items);
896
-					}
897
-
898
-					if ($move) {
899
-						// All we have to do is open the default calendar,
900
-						// set the message class correctly to be an appointment item
901
-						// and move it to the calendar folder
902
-						$sourcefolder = $this->openParentFolder();
903
-
904
-						// create a new calendar message, and copy the message to there,
905
-						// since we want to delete (move to wastebasket) the original message
906
-						$old_entryid = mapi_getprops($this->message, [PR_ENTRYID]);
907
-						$calmsg = mapi_folder_createmessage($calFolder);
908
-						mapi_copyto($this->message, [], [], $calmsg); /* includes attachments and recipients */
909
-						// reset the PidLidMeetingType to Unspecified for outlook display the item
910
-						$tmp_props = [];
911
-						$tmp_props[$this->proptags['meetingtype']] = mtgEmpty;
912
-						// OL needs this field always being set, or it will not display item
913
-						$tmp_props[$this->proptags['recurring']] = false;
914
-						mapi_setprops($calmsg, $tmp_props);
915
-
916
-						// After creating new MR, If local categories exist then apply it on new MR.
917
-						if (!empty($localCategories)) {
918
-							mapi_setprops($calmsg, $localCategories);
919
-						}
920
-
921
-						$calItemProps = [];
922
-						$calItemProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
923
-
924
-						/*
768
+                    if (isset($props[$this->proptags['reminderminutes']])) {
769
+                        $props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
770
+                    }
771
+                }
772
+                else {
773
+                    // only get required properties so we will not overwrite existing updated properties from calendar
774
+                    $props = mapi_getprops($this->message, [PR_ENTRYID]);
775
+                }
776
+
777
+                // While we applying updates of MR then all local categories will be removed,
778
+                // So get the local categories of all occurrence before applying update from organiser.
779
+                $localCategories = $this->getLocalCategories($calendarItem, $store, $calFolder);
780
+
781
+                $props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
782
+                // When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
783
+                if (!isset($props[$this->proptags['updatecounter']])) {
784
+                    $props[$this->proptags['updatecounter']] = 0;
785
+                }
786
+                $props[$this->proptags['meetingstatus']] = olMeetingReceived;
787
+                // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
788
+                $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
789
+
790
+                if (isset($props[$this->proptags['intendedbusystatus']])) {
791
+                    if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
792
+                        $props[$this->proptags['busystatus']] = fbTentative;
793
+                    }
794
+                    else {
795
+                        $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
796
+                    }
797
+                    // we already have intendedbusystatus value in $props so no need to copy it
798
+                }
799
+                else {
800
+                    $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
801
+                }
802
+
803
+                if ($userAction) {
804
+                    $addrInfo = $this->getOwnerAddress($this->store);
805
+
806
+                    // if user has responded then set replytime and name
807
+                    $props[$this->proptags['replytime']] = time();
808
+                    if (!empty($addrInfo)) {
809
+                        // @FIXME conditionally set this property only for delegation case
810
+                        $props[$this->proptags['apptreplyname']] = $addrInfo[0];
811
+                    }
812
+                }
813
+
814
+                mapi_setprops($calendarItem, $props);
815
+
816
+                // we have already processed attachments and recipients, so no need to do it again
817
+                if (!$processed) {
818
+                    // Copy attachments too
819
+                    $this->replaceAttachments($this->message, $calendarItem);
820
+                    // Copy recipients too
821
+                    $this->replaceRecipients($this->message, $calendarItem, $isDelegate);
822
+                }
823
+
824
+                // Find all occurrences based on CleanGlobalID (0x23)
825
+                // there will be no exceptions left if $processed is true, but even if it doesn't hurt to recheck
826
+                $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
827
+                if (is_array($items)) {
828
+                    // Save all existing occurrence as exceptions
829
+                    foreach ($items as $entryid) {
830
+                        // Open occurrence
831
+                        $occurrenceItem = mapi_msgstore_openentry($store, $entryid);
832
+
833
+                        // Save occurrence into main recurring item as exception
834
+                        if ($occurrenceItem) {
835
+                            $occurrenceItemProps = mapi_getprops($occurrenceItem, [$this->proptags['goid'], $this->proptags['recurring']]);
836
+
837
+                            // Find basedate of occurrence item
838
+                            $basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]);
839
+                            if ($basedate && $occurrenceItemProps[$this->proptags['recurring']] != true) {
840
+                                $this->mergeException($calendarItem, $occurrenceItem, $basedate, $store);
841
+                            }
842
+                        }
843
+                    }
844
+                }
845
+
846
+                mapi_savechanges($calendarItem);
847
+
848
+                // After applying update of organiser all local categories of occurrence was removed,
849
+                // So if local categories exist then apply it on respective occurrence.
850
+                if (!empty($localCategories)) {
851
+                    $this->applyLocalCategories($calendarItem, $store, $localCategories);
852
+                }
853
+
854
+                if ($move) {
855
+                    // open wastebasket of currently logged in user and move the meeting request to it
856
+                    // for delegates this will be delegate's wastebasket folder
857
+                    $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
858
+                    mapi_folder_copymessages($calFolder, [$props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
859
+                }
860
+
861
+                $entryid = $props[PR_ENTRYID];
862
+            }
863
+            else {
864
+                /**
865
+                 * This meeting request is not recurring, so can be an exception or normal meeting.
866
+                 * If exception then find main recurring item and update exception
867
+                 * If main recurring item is not found then put exception into Calendar as normal meeting.
868
+                 */
869
+                $calendarItem = false;
870
+
871
+                // We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence.
872
+                if ($basedate) {
873
+                    // Find main recurring item from CleanGlobalID of this meeting request
874
+                    $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
875
+                    if (is_array($items)) {
876
+                        foreach ($items as $key => $entryid) {
877
+                            $calendarItem = mapi_msgstore_openentry($store, $entryid);
878
+                        }
879
+                    }
880
+
881
+                    // Main recurring item is found, so now update exception
882
+                    if ($calendarItem) {
883
+                        $this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
884
+                        $calendarItemProps = mapi_getprops($calendarItem, [PR_ENTRYID]);
885
+                        $entryid = $calendarItemProps[PR_ENTRYID];
886
+                    }
887
+                }
888
+
889
+                if (!$calendarItem) {
890
+                    $items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
891
+                    if (is_array($items)) {
892
+                        // Get local categories before deleting MR.
893
+                        $message = mapi_msgstore_openentry($store, $items[0]);
894
+                        $localCategories = mapi_getprops($message, [$this->proptags['categories']]);
895
+                        mapi_folder_deletemessages($calFolder, $items);
896
+                    }
897
+
898
+                    if ($move) {
899
+                        // All we have to do is open the default calendar,
900
+                        // set the message class correctly to be an appointment item
901
+                        // and move it to the calendar folder
902
+                        $sourcefolder = $this->openParentFolder();
903
+
904
+                        // create a new calendar message, and copy the message to there,
905
+                        // since we want to delete (move to wastebasket) the original message
906
+                        $old_entryid = mapi_getprops($this->message, [PR_ENTRYID]);
907
+                        $calmsg = mapi_folder_createmessage($calFolder);
908
+                        mapi_copyto($this->message, [], [], $calmsg); /* includes attachments and recipients */
909
+                        // reset the PidLidMeetingType to Unspecified for outlook display the item
910
+                        $tmp_props = [];
911
+                        $tmp_props[$this->proptags['meetingtype']] = mtgEmpty;
912
+                        // OL needs this field always being set, or it will not display item
913
+                        $tmp_props[$this->proptags['recurring']] = false;
914
+                        mapi_setprops($calmsg, $tmp_props);
915
+
916
+                        // After creating new MR, If local categories exist then apply it on new MR.
917
+                        if (!empty($localCategories)) {
918
+                            mapi_setprops($calmsg, $localCategories);
919
+                        }
920
+
921
+                        $calItemProps = [];
922
+                        $calItemProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
923
+
924
+                        /*
925 925
 						 * the client which has sent this meeting request can generate wrong flagdueby
926 926
 						 * time (mainly OL), so regenerate that property so we will always show reminder
927 927
 						 * on right time
928 928
 						 */
929
-						if (isset($messageprops[$this->proptags['reminderminutes']])) {
930
-							$calItemProps[$this->proptags['flagdueby']] = $messageprops[$this->proptags['startdate']] - ($messageprops[$this->proptags['reminderminutes']] * 60);
931
-						}
932
-
933
-						if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
934
-							if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
935
-								$calItemProps[$this->proptags['busystatus']] = fbTentative;
936
-							}
937
-							else {
938
-								$calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
939
-							}
940
-							$calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
941
-						}
942
-						else {
943
-							$calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
944
-						}
945
-
946
-						// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
947
-						$calItemProps[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
948
-						if ($userAction) {
949
-							$addrInfo = $this->getOwnerAddress($this->store);
950
-
951
-							// if user has responded then set replytime and name
952
-							$calItemProps[$this->proptags['replytime']] = time();
953
-							if (!empty($addrInfo)) {
954
-								$calItemProps[$this->proptags['apptreplyname']] = $addrInfo[0];
955
-							}
956
-						}
957
-
958
-						$calItemProps[$this->proptags['recurring_pattern']] = '';
959
-						$calItemProps[$this->proptags['alldayevent']] = $calItemProps[$this->proptags['alldayevent']] ?? false;
960
-						$calItemProps[$this->proptags['private']] = $calItemProps[$this->proptags['private']] ?? false;
961
-						$calItemProps[$this->proptags['meetingstatus']] = $calItemProps[$this->proptags['meetingstatus']] ?? olMeetingReceived;
962
-						if (isset($calItemProps[$this->proptags['startdate']])) {
963
-							$calItemProps[$this->proptags['commonstart']] = $calItemProps[$this->proptags['startdate']];
964
-						}
965
-						if (isset($calItemProps[$this->proptags['duedate']])) {
966
-							$calItemProps[$this->proptags['commonend']] = $calItemProps[$this->proptags['duedate']];
967
-						}
968
-
969
-						mapi_setprops($calmsg, $proposeNewTimeProps + $calItemProps);
970
-
971
-						// get properties which stores owner information in meeting request mails
972
-						$props = mapi_getprops($calmsg, [
973
-							PR_SENT_REPRESENTING_ENTRYID,
974
-							PR_SENT_REPRESENTING_NAME,
975
-							PR_SENT_REPRESENTING_EMAIL_ADDRESS,
976
-							PR_SENT_REPRESENTING_ADDRTYPE,
977
-							PR_SENT_REPRESENTING_SEARCH_KEY,
978
-						]);
979
-
980
-						// add owner to recipient table
981
-						$recips = [];
982
-						$this->addOrganizer($props, $recips);
983
-						mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips);
984
-						mapi_savechanges($calmsg);
985
-
986
-						// Move the message to the wastebasket
987
-						$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
988
-						mapi_folder_copymessages($sourcefolder, [$old_entryid[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
989
-
990
-						$messageprops = mapi_getprops($calmsg, [PR_ENTRYID]);
991
-						$entryid = $messageprops[PR_ENTRYID];
992
-					}
993
-					else {
994
-						// Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment
995
-						$new = mapi_folder_createmessage($calFolder);
996
-						$props = mapi_getprops($this->message);
997
-
998
-						$props[$this->proptags['recurring_pattern']] = '';
999
-						$props[$this->proptags['alldayevent']] = $props[$this->proptags['alldayevent']] ?? false;
1000
-						$props[$this->proptags['private']] = $props[$this->proptags['private']] ?? false;
1001
-						$props[$this->proptags['meetingstatus']] = $props[$this->proptags['meetingstatus']] ?? olMeetingReceived;
1002
-						if (isset($props[$this->proptags['startdate']])) {
1003
-							$props[$this->proptags['commonstart']] = $props[$this->proptags['startdate']];
1004
-						}
1005
-						if (isset($props[$this->proptags['duedate']])) {
1006
-							$props[$this->proptags['commonend']] = $props[$this->proptags['duedate']];
1007
-						}
1008
-
1009
-						$props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
1010
-						// reset the PidLidMeetingType to Unspecified for outlook display the item
1011
-						$props[$this->proptags['meetingtype']] = mtgEmpty;
1012
-						// OL needs this field always being set, or it will not display item
1013
-						$props[$this->proptags['recurring']] = false;
1014
-
1015
-						// After creating new MR, If local categories exist then apply it on new MR.
1016
-						if (!empty($localCategories)) {
1017
-							mapi_setprops($new, $localCategories);
1018
-						}
1019
-
1020
-						/*
929
+                        if (isset($messageprops[$this->proptags['reminderminutes']])) {
930
+                            $calItemProps[$this->proptags['flagdueby']] = $messageprops[$this->proptags['startdate']] - ($messageprops[$this->proptags['reminderminutes']] * 60);
931
+                        }
932
+
933
+                        if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
934
+                            if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
935
+                                $calItemProps[$this->proptags['busystatus']] = fbTentative;
936
+                            }
937
+                            else {
938
+                                $calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
939
+                            }
940
+                            $calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
941
+                        }
942
+                        else {
943
+                            $calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
944
+                        }
945
+
946
+                        // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
947
+                        $calItemProps[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
948
+                        if ($userAction) {
949
+                            $addrInfo = $this->getOwnerAddress($this->store);
950
+
951
+                            // if user has responded then set replytime and name
952
+                            $calItemProps[$this->proptags['replytime']] = time();
953
+                            if (!empty($addrInfo)) {
954
+                                $calItemProps[$this->proptags['apptreplyname']] = $addrInfo[0];
955
+                            }
956
+                        }
957
+
958
+                        $calItemProps[$this->proptags['recurring_pattern']] = '';
959
+                        $calItemProps[$this->proptags['alldayevent']] = $calItemProps[$this->proptags['alldayevent']] ?? false;
960
+                        $calItemProps[$this->proptags['private']] = $calItemProps[$this->proptags['private']] ?? false;
961
+                        $calItemProps[$this->proptags['meetingstatus']] = $calItemProps[$this->proptags['meetingstatus']] ?? olMeetingReceived;
962
+                        if (isset($calItemProps[$this->proptags['startdate']])) {
963
+                            $calItemProps[$this->proptags['commonstart']] = $calItemProps[$this->proptags['startdate']];
964
+                        }
965
+                        if (isset($calItemProps[$this->proptags['duedate']])) {
966
+                            $calItemProps[$this->proptags['commonend']] = $calItemProps[$this->proptags['duedate']];
967
+                        }
968
+
969
+                        mapi_setprops($calmsg, $proposeNewTimeProps + $calItemProps);
970
+
971
+                        // get properties which stores owner information in meeting request mails
972
+                        $props = mapi_getprops($calmsg, [
973
+                            PR_SENT_REPRESENTING_ENTRYID,
974
+                            PR_SENT_REPRESENTING_NAME,
975
+                            PR_SENT_REPRESENTING_EMAIL_ADDRESS,
976
+                            PR_SENT_REPRESENTING_ADDRTYPE,
977
+                            PR_SENT_REPRESENTING_SEARCH_KEY,
978
+                        ]);
979
+
980
+                        // add owner to recipient table
981
+                        $recips = [];
982
+                        $this->addOrganizer($props, $recips);
983
+                        mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips);
984
+                        mapi_savechanges($calmsg);
985
+
986
+                        // Move the message to the wastebasket
987
+                        $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
988
+                        mapi_folder_copymessages($sourcefolder, [$old_entryid[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
989
+
990
+                        $messageprops = mapi_getprops($calmsg, [PR_ENTRYID]);
991
+                        $entryid = $messageprops[PR_ENTRYID];
992
+                    }
993
+                    else {
994
+                        // Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment
995
+                        $new = mapi_folder_createmessage($calFolder);
996
+                        $props = mapi_getprops($this->message);
997
+
998
+                        $props[$this->proptags['recurring_pattern']] = '';
999
+                        $props[$this->proptags['alldayevent']] = $props[$this->proptags['alldayevent']] ?? false;
1000
+                        $props[$this->proptags['private']] = $props[$this->proptags['private']] ?? false;
1001
+                        $props[$this->proptags['meetingstatus']] = $props[$this->proptags['meetingstatus']] ?? olMeetingReceived;
1002
+                        if (isset($props[$this->proptags['startdate']])) {
1003
+                            $props[$this->proptags['commonstart']] = $props[$this->proptags['startdate']];
1004
+                        }
1005
+                        if (isset($props[$this->proptags['duedate']])) {
1006
+                            $props[$this->proptags['commonend']] = $props[$this->proptags['duedate']];
1007
+                        }
1008
+
1009
+                        $props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
1010
+                        // reset the PidLidMeetingType to Unspecified for outlook display the item
1011
+                        $props[$this->proptags['meetingtype']] = mtgEmpty;
1012
+                        // OL needs this field always being set, or it will not display item
1013
+                        $props[$this->proptags['recurring']] = false;
1014
+
1015
+                        // After creating new MR, If local categories exist then apply it on new MR.
1016
+                        if (!empty($localCategories)) {
1017
+                            mapi_setprops($new, $localCategories);
1018
+                        }
1019
+
1020
+                        /*
1021 1021
 						 * the client which has sent this meeting request can generate wrong flagdueby
1022 1022
 						 * time (mainly OL), so regenerate that property so we will always show reminder
1023 1023
 						 * on right time
1024 1024
 						 */
1025
-						if (isset($props[$this->proptags['reminderminutes']])) {
1026
-							$props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
1027
-						}
1028
-
1029
-						// When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
1030
-						if (!isset($props[$this->proptags['updatecounter']])) {
1031
-							$props[$this->proptags['updatecounter']] = 0;
1032
-						}
1033
-						// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
1034
-						$props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
1035
-
1036
-						if (isset($props[$this->proptags['intendedbusystatus']])) {
1037
-							if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
1038
-								$props[$this->proptags['busystatus']] = fbTentative;
1039
-							}
1040
-							else {
1041
-								$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
1042
-							}
1043
-							// we already have intendedbusystatus value in $props so no need to copy it
1044
-						}
1045
-						else {
1046
-							$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1047
-						}
1048
-
1049
-						if ($userAction) {
1050
-							$addrInfo = $this->getOwnerAddress($this->store);
1051
-
1052
-							// if user has responded then set replytime and name
1053
-							$props[$this->proptags['replytime']] = time();
1054
-							if (!empty($addrInfo)) {
1055
-								$props[$this->proptags['apptreplyname']] = $addrInfo[0];
1056
-							}
1057
-						}
1058
-
1059
-						mapi_setprops($new, $proposeNewTimeProps + $props);
1060
-
1061
-						$reciptable = mapi_message_getrecipienttable($this->message);
1062
-
1063
-						$recips = [];
1064
-						// If delegate, then do not add the delegate in recipients
1065
-						if ($isDelegate) {
1066
-							$delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
1067
-							$res = [
1068
-								RES_PROPERTY, [
1069
-									RELOP => RELOP_NE,
1070
-									ULPROPTAG => PR_EMAIL_ADDRESS,
1071
-									VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
1072
-								],
1073
-							];
1074
-							$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
1075
-						}
1076
-						else {
1077
-							$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1078
-						}
1079
-
1080
-						$this->addOrganizer($props, $recips);
1081
-						mapi_message_modifyrecipients($new, MODRECIP_ADD, $recips);
1082
-						mapi_savechanges($new);
1083
-
1084
-						$props = mapi_getprops($new, [PR_ENTRYID]);
1085
-						$entryid = $props[PR_ENTRYID];
1086
-					}
1087
-				}
1088
-			}
1089
-		}
1090
-		else {
1091
-			// Here only properties are set on calendaritem, because user is responding from calendar.
1092
-			$props = [];
1093
-			$props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted;
1094
-
1095
-			if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
1096
-				if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
1097
-					$props[$this->proptags['busystatus']] = fbTentative;
1098
-				}
1099
-				else {
1100
-					$props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1101
-				}
1102
-				$props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1103
-			}
1104
-			else {
1105
-				$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1106
-			}
1107
-
1108
-			$props[$this->proptags['meetingstatus']] = olMeetingReceived;
1109
-
1110
-			$addrInfo = $this->getOwnerAddress($this->store);
1111
-
1112
-			// if user has responded then set replytime and name
1113
-			$props[$this->proptags['replytime']] = time();
1114
-			if (!empty($addrInfo)) {
1115
-				$props[$this->proptags['apptreplyname']] = $addrInfo[0];
1116
-			}
1117
-
1118
-			if ($basedate) {
1119
-				$recurr = new Recurrence($store, $this->message);
1120
-
1121
-				// Copy recipients list
1122
-				$reciptable = mapi_message_getrecipienttable($this->message);
1123
-				$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1124
-
1125
-				if ($recurr->isException($basedate)) {
1126
-					$recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips);
1127
-				}
1128
-				else {
1129
-					$props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
1130
-					$props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
1131
-
1132
-					$props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
1133
-					$props[PR_SENT_REPRESENTING_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
1134
-					$props[PR_SENT_REPRESENTING_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
1135
-					$props[PR_SENT_REPRESENTING_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
1136
-					$props[PR_SENT_REPRESENTING_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
1137
-
1138
-					$recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips);
1139
-				}
1140
-			}
1141
-			else {
1142
-				mapi_setprops($this->message, $proposeNewTimeProps + $props);
1143
-			}
1144
-			mapi_savechanges($this->message);
1145
-
1146
-			$entryid = $messageprops[PR_ENTRYID];
1147
-		}
1148
-
1149
-		return $entryid;
1150
-	}
1151
-
1152
-	/**
1153
-	 * Declines the meeting request by moving the item to the deleted
1154
-	 * items folder and sending a decline message. After declining, you
1155
-	 * can't use this class instance any more. The message is closed.
1156
-	 * When an occurrence is decline then false is returned because that
1157
-	 * occurrence is deleted not the recurring item.
1158
-	 *
1159
-	 * @param bool   $sendresponse true if a response has to be sent to organizer
1160
-	 * @param string $basedate     if specified contains starttime of day of an occurrence
1161
-	 * @param mixed  $body
1162
-	 *
1163
-	 * @return bool true if item is deleted from Calendar else false
1164
-	 */
1165
-	public function doDecline($sendresponse, $basedate = false, $body = false) {
1166
-		if ($this->isLocalOrganiser()) {
1167
-			return false;
1168
-		}
1169
-
1170
-		$result = false;
1171
-		$calendaritem = false;
1172
-
1173
-		// Remove any previous calendar items with this goid and appt id
1174
-		$messageprops = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
1175
-
1176
-		// If this meeting request is received by a delegate then open delegator's store.
1177
-		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1178
-			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
1179
-
1180
-			$store = $delegatorStore['store'];
1181
-			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1182
-		}
1183
-		else {
1184
-			$calFolder = $this->openDefaultCalendar();
1185
-			$store = $this->store;
1186
-		}
1187
-
1188
-		// check for calendar access before deleting the calendar item
1189
-		if ($this->checkCalendarWriteAccess($store) !== true) {
1190
-			// Throw an exception that we don't have write permissions on calendar folder,
1191
-			// allow caller to fill the error message
1192
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
1193
-		}
1194
-
1195
-		$goid = $messageprops[$this->proptags['goid']];
1196
-
1197
-		// First, find the items in the calendar by GlobalObjid (0x3)
1198
-		$entryids = $this->findCalendarItems($goid, $calFolder);
1199
-
1200
-		if (!$basedate) {
1201
-			$basedate = $this->getBasedateFromGlobalID($goid);
1202
-		}
1203
-
1204
-		if ($sendresponse) {
1205
-			$this->createResponse(olResponseDeclined, [], $body, $store, $basedate, $calFolder);
1206
-		}
1207
-
1208
-		if ($basedate) {
1209
-			// use CleanGlobalObjid (0x23)
1210
-			$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
1211
-
1212
-			if (is_array($calendaritems)) {
1213
-				foreach ($calendaritems as $entryid) {
1214
-					// Open each calendar item and set the properties of the cancellation object
1215
-					$calendaritem = mapi_msgstore_openentry($store, $entryid);
1216
-
1217
-					// Recurring item is found, now delete exception
1218
-					if ($calendaritem) {
1219
-						$this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
1220
-						$result = true;
1221
-					}
1222
-				}
1223
-			}
1224
-
1225
-			if ($this->isMeetingRequest()) {
1226
-				$calendaritem = false;
1227
-			}
1228
-		}
1229
-
1230
-		if (!$calendaritem) {
1231
-			$calendar = $this->openDefaultCalendar($store);
1232
-
1233
-			if (!empty($entryids)) {
1234
-				mapi_folder_deletemessages($calendar, $entryids);
1235
-			}
1236
-
1237
-			// All we have to do to decline, is to move the item to the waste basket
1238
-			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1239
-			$sourcefolder = $this->openParentFolder();
1240
-
1241
-			$messageprops = mapi_getprops($this->message, [PR_ENTRYID]);
1242
-
1243
-			// Release the message
1244
-			$this->message = null;
1245
-
1246
-			// Move the message to the waste basket
1247
-			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1248
-
1249
-			$result = true;
1250
-		}
1251
-
1252
-		return $result;
1253
-	}
1254
-
1255
-	/**
1256
-	 * Removes a meeting request from the calendar when the user presses the
1257
-	 * 'remove from calendar' button in response to a meeting cancellation.
1258
-	 *
1259
-	 * @param string $basedate if specified contains starttime of day of an occurrence
1260
-	 */
1261
-	public function doRemoveFromCalendar($basedate) {
1262
-		if ($this->isLocalOrganiser()) {
1263
-			return false;
1264
-		}
1265
-
1266
-		$messageprops = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_ENTRYID, PR_MESSAGE_CLASS]);
1267
-
1268
-		$goid = $messageprops[$this->proptags['goid']];
1269
-
1270
-		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1271
-			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
1272
-
1273
-			$store = $delegatorStore['store'];
1274
-			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1275
-		}
1276
-		else {
1277
-			$store = $this->store;
1278
-			$calFolder = $this->openDefaultCalendar();
1279
-		}
1280
-
1281
-		// check for calendar access before deleting the calendar item
1282
-		if ($this->checkCalendarWriteAccess($store) !== true) {
1283
-			// Throw an exception that we don't have write permissions on calendar folder,
1284
-			// allow caller to fill the error message
1285
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
1286
-		}
1287
-
1288
-		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1289
-		// get the source folder of the meeting message
1290
-		$sourcefolder = $this->openParentFolder();
1291
-
1292
-		// Check if the message is a meeting request in the inbox or a calendaritem by checking the message class
1293
-		if ($this->isMeetingCancellation($messageprops[PR_MESSAGE_CLASS])) {
1294
-			// get the basedate to check for exception
1295
-			$basedate = $this->getBasedateFromGlobalID($goid);
1296
-
1297
-			$calendarItem = $this->getCorrespondentCalendarItem(true);
1298
-
1299
-			if ($calendarItem !== false) {
1300
-				// basedate is provided so open exception
1301
-				if ($basedate) {
1302
-					$exception = $this->getExceptionItem($calendarItem, $basedate);
1303
-
1304
-					if ($exception !== false) {
1305
-						// exception found, remove it from calendar
1306
-						$this->doRemoveExceptionFromCalendar($basedate, $calendarItem, $store);
1307
-					}
1308
-				}
1309
-				else {
1310
-					// remove normal / recurring series from calendar
1311
-					$entryids = mapi_getprops($calendarItem, [PR_ENTRYID]);
1312
-
1313
-					$entryids = [$entryids[PR_ENTRYID]];
1314
-
1315
-					mapi_folder_copymessages($calFolder, $entryids, $wastebasket, MESSAGE_MOVE);
1316
-				}
1317
-			}
1318
-
1319
-			// Release the message, because we are going to move it to wastebasket
1320
-			$this->message = null;
1321
-
1322
-			// Move the cancellation mail to wastebasket
1323
-			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1324
-		}
1325
-		else {
1326
-			// Here only properties are set on calendaritem, because user is responding from calendar.
1327
-			if ($basedate) {
1328
-				// remove the occurrence
1329
-				$this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
1330
-			}
1331
-			else {
1332
-				// remove normal/recurring meeting item.
1333
-				// Move the message to the waste basket
1334
-				mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1335
-			}
1336
-		}
1337
-	}
1338
-
1339
-	/**
1340
-	 * Function can be used to cancel any existing meeting and send cancellation mails to attendees.
1341
-	 * Should only be called from meeting object from calendar.
1342
-	 *
1343
-	 * @param string $basedate (optional) basedate of occurrence which should be cancelled
1344
-	 * @FIXME cancellation mail is also sent to attendee which has declined the meeting
1345
-	 * @FIXME don't send canellation mail when cancelling meeting from past
1346
-	 */
1347
-	public function doCancelInvitation($basedate = false) {
1348
-		if (!$this->isLocalOrganiser()) {
1349
-			return;
1350
-		}
1351
-
1352
-		// check write access for delegate
1353
-		if ($this->checkCalendarWriteAccess($this->store) !== true) {
1354
-			// Throw an exception that we don't have write permissions on calendar folder,
1355
-			// error message will be filled by module
1356
-			throw new MAPIException(null, MAPI_E_NO_ACCESS);
1357
-		}
1358
-
1359
-		$messageProps = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['recurring']]);
1360
-
1361
-		if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
1362
-			// cancellation of recurring series or one occurrence
1363
-			$recurrence = new Recurrence($this->store, $this->message);
1364
-
1365
-			// if basedate is specified then we are cancelling only one occurrence, so create exception for that occurrence
1366
-			if ($basedate) {
1367
-				$recurrence->createException([], $basedate, true);
1368
-			}
1369
-
1370
-			// update the meeting request
1371
-			$this->updateMeetingRequest();
1372
-
1373
-			// send cancellation mails
1374
-			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ', $basedate);
1375
-
1376
-			// save changes in the message
1377
-			mapi_savechanges($this->message);
1378
-		}
1379
-		else {
1380
-			// cancellation of normal meeting request
1381
-			// Send the cancellation
1382
-			$this->updateMeetingRequest();
1383
-			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ');
1384
-
1385
-			// save changes in the message
1386
-			mapi_savechanges($this->message);
1387
-		}
1388
-
1389
-		// if basedate is specified then we have already created exception of it so nothing should be done now
1390
-		// but when cancelling normal / recurring meeting request we need to remove meeting from calendar
1391
-		if ($basedate === false) {
1392
-			// get the wastebasket folder, for delegate this will give wastebasket of delegate
1393
-			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1394
-
1395
-			// get the source folder of the meeting message
1396
-			$sourcefolder = $this->openParentFolder();
1397
-
1398
-			// Move the message to the deleted items
1399
-			mapi_folder_copymessages($sourcefolder, [$messageProps[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1400
-		}
1401
-	}
1402
-
1403
-	/**
1404
-	 * Convert epoch to MAPI FileTime, number of 100-nanosecond units since
1405
-	 * the start of January 1, 1601.
1406
-	 * https://msdn.microsoft.com/en-us/library/office/cc765906.aspx.
1407
-	 *
1408
-	 * @param int the current epoch
1409
-	 * @param mixed $epoch
1410
-	 *
1411
-	 * @return the MAPI FileTime equalevent to the given epoch time
1412
-	 */
1413
-	public function epochToMapiFileTime($epoch) {
1414
-		$nanoseconds_between_epoch = 116444736000000000;
1415
-
1416
-		return ($epoch * 10000000) + $nanoseconds_between_epoch;
1417
-	}
1418
-
1419
-	/**
1420
-	 * Sets the properties in the message so that is can be sent
1421
-	 * as a meeting request. The caller has to submit the message. This
1422
-	 * is only used for new MeetingRequests. Pass the appointment item as $message
1423
-	 * in the constructor to do this.
1424
-	 *
1425
-	 * @param mixed $basedate
1426
-	 */
1427
-	public function setMeetingRequest($basedate = false) {
1428
-		$props = mapi_getprops($this->message, [$this->proptags['updatecounter']]);
1429
-
1430
-		// Create a new global id for this item
1431
-		// https://msdn.microsoft.com/en-us/library/ee160198(v=exchg.80).aspx
1432
-		$goid = pack('H*', '040000008200E00074C5B7101A82E00800000000');
1433
-		/*
1025
+                        if (isset($props[$this->proptags['reminderminutes']])) {
1026
+                            $props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
1027
+                        }
1028
+
1029
+                        // When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
1030
+                        if (!isset($props[$this->proptags['updatecounter']])) {
1031
+                            $props[$this->proptags['updatecounter']] = 0;
1032
+                        }
1033
+                        // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
1034
+                        $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
1035
+
1036
+                        if (isset($props[$this->proptags['intendedbusystatus']])) {
1037
+                            if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
1038
+                                $props[$this->proptags['busystatus']] = fbTentative;
1039
+                            }
1040
+                            else {
1041
+                                $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
1042
+                            }
1043
+                            // we already have intendedbusystatus value in $props so no need to copy it
1044
+                        }
1045
+                        else {
1046
+                            $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1047
+                        }
1048
+
1049
+                        if ($userAction) {
1050
+                            $addrInfo = $this->getOwnerAddress($this->store);
1051
+
1052
+                            // if user has responded then set replytime and name
1053
+                            $props[$this->proptags['replytime']] = time();
1054
+                            if (!empty($addrInfo)) {
1055
+                                $props[$this->proptags['apptreplyname']] = $addrInfo[0];
1056
+                            }
1057
+                        }
1058
+
1059
+                        mapi_setprops($new, $proposeNewTimeProps + $props);
1060
+
1061
+                        $reciptable = mapi_message_getrecipienttable($this->message);
1062
+
1063
+                        $recips = [];
1064
+                        // If delegate, then do not add the delegate in recipients
1065
+                        if ($isDelegate) {
1066
+                            $delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
1067
+                            $res = [
1068
+                                RES_PROPERTY, [
1069
+                                    RELOP => RELOP_NE,
1070
+                                    ULPROPTAG => PR_EMAIL_ADDRESS,
1071
+                                    VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
1072
+                                ],
1073
+                            ];
1074
+                            $recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
1075
+                        }
1076
+                        else {
1077
+                            $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1078
+                        }
1079
+
1080
+                        $this->addOrganizer($props, $recips);
1081
+                        mapi_message_modifyrecipients($new, MODRECIP_ADD, $recips);
1082
+                        mapi_savechanges($new);
1083
+
1084
+                        $props = mapi_getprops($new, [PR_ENTRYID]);
1085
+                        $entryid = $props[PR_ENTRYID];
1086
+                    }
1087
+                }
1088
+            }
1089
+        }
1090
+        else {
1091
+            // Here only properties are set on calendaritem, because user is responding from calendar.
1092
+            $props = [];
1093
+            $props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted;
1094
+
1095
+            if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
1096
+                if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
1097
+                    $props[$this->proptags['busystatus']] = fbTentative;
1098
+                }
1099
+                else {
1100
+                    $props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1101
+                }
1102
+                $props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1103
+            }
1104
+            else {
1105
+                $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1106
+            }
1107
+
1108
+            $props[$this->proptags['meetingstatus']] = olMeetingReceived;
1109
+
1110
+            $addrInfo = $this->getOwnerAddress($this->store);
1111
+
1112
+            // if user has responded then set replytime and name
1113
+            $props[$this->proptags['replytime']] = time();
1114
+            if (!empty($addrInfo)) {
1115
+                $props[$this->proptags['apptreplyname']] = $addrInfo[0];
1116
+            }
1117
+
1118
+            if ($basedate) {
1119
+                $recurr = new Recurrence($store, $this->message);
1120
+
1121
+                // Copy recipients list
1122
+                $reciptable = mapi_message_getrecipienttable($this->message);
1123
+                $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1124
+
1125
+                if ($recurr->isException($basedate)) {
1126
+                    $recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips);
1127
+                }
1128
+                else {
1129
+                    $props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
1130
+                    $props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
1131
+
1132
+                    $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
1133
+                    $props[PR_SENT_REPRESENTING_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
1134
+                    $props[PR_SENT_REPRESENTING_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
1135
+                    $props[PR_SENT_REPRESENTING_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
1136
+                    $props[PR_SENT_REPRESENTING_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
1137
+
1138
+                    $recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips);
1139
+                }
1140
+            }
1141
+            else {
1142
+                mapi_setprops($this->message, $proposeNewTimeProps + $props);
1143
+            }
1144
+            mapi_savechanges($this->message);
1145
+
1146
+            $entryid = $messageprops[PR_ENTRYID];
1147
+        }
1148
+
1149
+        return $entryid;
1150
+    }
1151
+
1152
+    /**
1153
+     * Declines the meeting request by moving the item to the deleted
1154
+     * items folder and sending a decline message. After declining, you
1155
+     * can't use this class instance any more. The message is closed.
1156
+     * When an occurrence is decline then false is returned because that
1157
+     * occurrence is deleted not the recurring item.
1158
+     *
1159
+     * @param bool   $sendresponse true if a response has to be sent to organizer
1160
+     * @param string $basedate     if specified contains starttime of day of an occurrence
1161
+     * @param mixed  $body
1162
+     *
1163
+     * @return bool true if item is deleted from Calendar else false
1164
+     */
1165
+    public function doDecline($sendresponse, $basedate = false, $body = false) {
1166
+        if ($this->isLocalOrganiser()) {
1167
+            return false;
1168
+        }
1169
+
1170
+        $result = false;
1171
+        $calendaritem = false;
1172
+
1173
+        // Remove any previous calendar items with this goid and appt id
1174
+        $messageprops = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
1175
+
1176
+        // If this meeting request is received by a delegate then open delegator's store.
1177
+        if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1178
+            $delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
1179
+
1180
+            $store = $delegatorStore['store'];
1181
+            $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1182
+        }
1183
+        else {
1184
+            $calFolder = $this->openDefaultCalendar();
1185
+            $store = $this->store;
1186
+        }
1187
+
1188
+        // check for calendar access before deleting the calendar item
1189
+        if ($this->checkCalendarWriteAccess($store) !== true) {
1190
+            // Throw an exception that we don't have write permissions on calendar folder,
1191
+            // allow caller to fill the error message
1192
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
1193
+        }
1194
+
1195
+        $goid = $messageprops[$this->proptags['goid']];
1196
+
1197
+        // First, find the items in the calendar by GlobalObjid (0x3)
1198
+        $entryids = $this->findCalendarItems($goid, $calFolder);
1199
+
1200
+        if (!$basedate) {
1201
+            $basedate = $this->getBasedateFromGlobalID($goid);
1202
+        }
1203
+
1204
+        if ($sendresponse) {
1205
+            $this->createResponse(olResponseDeclined, [], $body, $store, $basedate, $calFolder);
1206
+        }
1207
+
1208
+        if ($basedate) {
1209
+            // use CleanGlobalObjid (0x23)
1210
+            $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
1211
+
1212
+            if (is_array($calendaritems)) {
1213
+                foreach ($calendaritems as $entryid) {
1214
+                    // Open each calendar item and set the properties of the cancellation object
1215
+                    $calendaritem = mapi_msgstore_openentry($store, $entryid);
1216
+
1217
+                    // Recurring item is found, now delete exception
1218
+                    if ($calendaritem) {
1219
+                        $this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
1220
+                        $result = true;
1221
+                    }
1222
+                }
1223
+            }
1224
+
1225
+            if ($this->isMeetingRequest()) {
1226
+                $calendaritem = false;
1227
+            }
1228
+        }
1229
+
1230
+        if (!$calendaritem) {
1231
+            $calendar = $this->openDefaultCalendar($store);
1232
+
1233
+            if (!empty($entryids)) {
1234
+                mapi_folder_deletemessages($calendar, $entryids);
1235
+            }
1236
+
1237
+            // All we have to do to decline, is to move the item to the waste basket
1238
+            $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1239
+            $sourcefolder = $this->openParentFolder();
1240
+
1241
+            $messageprops = mapi_getprops($this->message, [PR_ENTRYID]);
1242
+
1243
+            // Release the message
1244
+            $this->message = null;
1245
+
1246
+            // Move the message to the waste basket
1247
+            mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1248
+
1249
+            $result = true;
1250
+        }
1251
+
1252
+        return $result;
1253
+    }
1254
+
1255
+    /**
1256
+     * Removes a meeting request from the calendar when the user presses the
1257
+     * 'remove from calendar' button in response to a meeting cancellation.
1258
+     *
1259
+     * @param string $basedate if specified contains starttime of day of an occurrence
1260
+     */
1261
+    public function doRemoveFromCalendar($basedate) {
1262
+        if ($this->isLocalOrganiser()) {
1263
+            return false;
1264
+        }
1265
+
1266
+        $messageprops = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_ENTRYID, PR_MESSAGE_CLASS]);
1267
+
1268
+        $goid = $messageprops[$this->proptags['goid']];
1269
+
1270
+        if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1271
+            $delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
1272
+
1273
+            $store = $delegatorStore['store'];
1274
+            $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1275
+        }
1276
+        else {
1277
+            $store = $this->store;
1278
+            $calFolder = $this->openDefaultCalendar();
1279
+        }
1280
+
1281
+        // check for calendar access before deleting the calendar item
1282
+        if ($this->checkCalendarWriteAccess($store) !== true) {
1283
+            // Throw an exception that we don't have write permissions on calendar folder,
1284
+            // allow caller to fill the error message
1285
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
1286
+        }
1287
+
1288
+        $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1289
+        // get the source folder of the meeting message
1290
+        $sourcefolder = $this->openParentFolder();
1291
+
1292
+        // Check if the message is a meeting request in the inbox or a calendaritem by checking the message class
1293
+        if ($this->isMeetingCancellation($messageprops[PR_MESSAGE_CLASS])) {
1294
+            // get the basedate to check for exception
1295
+            $basedate = $this->getBasedateFromGlobalID($goid);
1296
+
1297
+            $calendarItem = $this->getCorrespondentCalendarItem(true);
1298
+
1299
+            if ($calendarItem !== false) {
1300
+                // basedate is provided so open exception
1301
+                if ($basedate) {
1302
+                    $exception = $this->getExceptionItem($calendarItem, $basedate);
1303
+
1304
+                    if ($exception !== false) {
1305
+                        // exception found, remove it from calendar
1306
+                        $this->doRemoveExceptionFromCalendar($basedate, $calendarItem, $store);
1307
+                    }
1308
+                }
1309
+                else {
1310
+                    // remove normal / recurring series from calendar
1311
+                    $entryids = mapi_getprops($calendarItem, [PR_ENTRYID]);
1312
+
1313
+                    $entryids = [$entryids[PR_ENTRYID]];
1314
+
1315
+                    mapi_folder_copymessages($calFolder, $entryids, $wastebasket, MESSAGE_MOVE);
1316
+                }
1317
+            }
1318
+
1319
+            // Release the message, because we are going to move it to wastebasket
1320
+            $this->message = null;
1321
+
1322
+            // Move the cancellation mail to wastebasket
1323
+            mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1324
+        }
1325
+        else {
1326
+            // Here only properties are set on calendaritem, because user is responding from calendar.
1327
+            if ($basedate) {
1328
+                // remove the occurrence
1329
+                $this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
1330
+            }
1331
+            else {
1332
+                // remove normal/recurring meeting item.
1333
+                // Move the message to the waste basket
1334
+                mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1335
+            }
1336
+        }
1337
+    }
1338
+
1339
+    /**
1340
+     * Function can be used to cancel any existing meeting and send cancellation mails to attendees.
1341
+     * Should only be called from meeting object from calendar.
1342
+     *
1343
+     * @param string $basedate (optional) basedate of occurrence which should be cancelled
1344
+     * @FIXME cancellation mail is also sent to attendee which has declined the meeting
1345
+     * @FIXME don't send canellation mail when cancelling meeting from past
1346
+     */
1347
+    public function doCancelInvitation($basedate = false) {
1348
+        if (!$this->isLocalOrganiser()) {
1349
+            return;
1350
+        }
1351
+
1352
+        // check write access for delegate
1353
+        if ($this->checkCalendarWriteAccess($this->store) !== true) {
1354
+            // Throw an exception that we don't have write permissions on calendar folder,
1355
+            // error message will be filled by module
1356
+            throw new MAPIException(null, MAPI_E_NO_ACCESS);
1357
+        }
1358
+
1359
+        $messageProps = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['recurring']]);
1360
+
1361
+        if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
1362
+            // cancellation of recurring series or one occurrence
1363
+            $recurrence = new Recurrence($this->store, $this->message);
1364
+
1365
+            // if basedate is specified then we are cancelling only one occurrence, so create exception for that occurrence
1366
+            if ($basedate) {
1367
+                $recurrence->createException([], $basedate, true);
1368
+            }
1369
+
1370
+            // update the meeting request
1371
+            $this->updateMeetingRequest();
1372
+
1373
+            // send cancellation mails
1374
+            $this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ', $basedate);
1375
+
1376
+            // save changes in the message
1377
+            mapi_savechanges($this->message);
1378
+        }
1379
+        else {
1380
+            // cancellation of normal meeting request
1381
+            // Send the cancellation
1382
+            $this->updateMeetingRequest();
1383
+            $this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ');
1384
+
1385
+            // save changes in the message
1386
+            mapi_savechanges($this->message);
1387
+        }
1388
+
1389
+        // if basedate is specified then we have already created exception of it so nothing should be done now
1390
+        // but when cancelling normal / recurring meeting request we need to remove meeting from calendar
1391
+        if ($basedate === false) {
1392
+            // get the wastebasket folder, for delegate this will give wastebasket of delegate
1393
+            $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1394
+
1395
+            // get the source folder of the meeting message
1396
+            $sourcefolder = $this->openParentFolder();
1397
+
1398
+            // Move the message to the deleted items
1399
+            mapi_folder_copymessages($sourcefolder, [$messageProps[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1400
+        }
1401
+    }
1402
+
1403
+    /**
1404
+     * Convert epoch to MAPI FileTime, number of 100-nanosecond units since
1405
+     * the start of January 1, 1601.
1406
+     * https://msdn.microsoft.com/en-us/library/office/cc765906.aspx.
1407
+     *
1408
+     * @param int the current epoch
1409
+     * @param mixed $epoch
1410
+     *
1411
+     * @return the MAPI FileTime equalevent to the given epoch time
1412
+     */
1413
+    public function epochToMapiFileTime($epoch) {
1414
+        $nanoseconds_between_epoch = 116444736000000000;
1415
+
1416
+        return ($epoch * 10000000) + $nanoseconds_between_epoch;
1417
+    }
1418
+
1419
+    /**
1420
+     * Sets the properties in the message so that is can be sent
1421
+     * as a meeting request. The caller has to submit the message. This
1422
+     * is only used for new MeetingRequests. Pass the appointment item as $message
1423
+     * in the constructor to do this.
1424
+     *
1425
+     * @param mixed $basedate
1426
+     */
1427
+    public function setMeetingRequest($basedate = false) {
1428
+        $props = mapi_getprops($this->message, [$this->proptags['updatecounter']]);
1429
+
1430
+        // Create a new global id for this item
1431
+        // https://msdn.microsoft.com/en-us/library/ee160198(v=exchg.80).aspx
1432
+        $goid = pack('H*', '040000008200E00074C5B7101A82E00800000000');
1433
+        /*
1434 1434
 		$year = gmdate("Y");
1435 1435
 		$month = gmdate("n");
1436 1436
 		$day = gmdate("j");
@@ -1438,1692 +1438,1692 @@  discard block
 block discarded – undo
1438 1438
 		$goid .= pack('C', $month);
1439 1439
 		$goid .= pack('C', $day);
1440 1440
 		*/
1441
-		// Creation Time
1442
-		$time = $this->epochToMapiFileTime(time());
1443
-		$goid .= pack('V', $time & 0xFFFFFFFF);
1444
-		$goid .= pack('V', $time >> 32);
1445
-		// 8 Zeros
1446
-		$goid .= pack('H*', '0000000000000000');
1447
-		// Length of the random data
1448
-		$goid .= pack('V', 16);
1449
-		// Random data.
1450
-		for ($i = 0; $i < 16; ++$i) {
1451
-			$goid .= chr(rand(0, 255));
1452
-		}
1453
-
1454
-		// Create a new appointment id for this item
1455
-		$apptid = rand();
1456
-
1457
-		$props[PR_OWNER_APPT_ID] = $apptid;
1458
-		$props[PR_ICON_INDEX] = 1026;
1459
-		$props[$this->proptags['goid']] = $goid;
1460
-		$props[$this->proptags['goid2']] = $goid;
1461
-
1462
-		if (!isset($props[$this->proptags['updatecounter']])) {
1463
-			$props[$this->proptags['updatecounter']] = 0;			// OL also starts sequence no with zero.
1464
-			$props[$this->proptags['last_updatecounter']] = 0;
1465
-		}
1466
-
1467
-		mapi_setprops($this->message, $props);
1468
-	}
1469
-
1470
-	/**
1471
-	 * Sends a meeting request by copying it to the outbox, converting
1472
-	 * the message class, adding some properties that are required only
1473
-	 * for sending the message and submitting the message. Set cancel to
1474
-	 * true if you wish to completely cancel the meeting request. You can
1475
-	 * specify an optional 'prefix' to prefix the sent message, which is normally
1476
-	 * 'Canceled: '.
1477
-	 *
1478
-	 * @param mixed $cancel
1479
-	 * @param mixed $prefix
1480
-	 * @param mixed $basedate
1481
-	 * @param mixed $modifiedRecips
1482
-	 * @param mixed $deletedRecips
1483
-	 */
1484
-	public function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $modifiedRecips = false, $deletedRecips = false) {
1485
-		$this->includesResources = false;
1486
-		$this->nonAcceptingResources = [];
1487
-
1488
-		// Get the properties of the message
1489
-		$messageprops = mapi_getprops($this->message, [$this->proptags['recurring']]);
1490
-
1491
-		/*
1441
+        // Creation Time
1442
+        $time = $this->epochToMapiFileTime(time());
1443
+        $goid .= pack('V', $time & 0xFFFFFFFF);
1444
+        $goid .= pack('V', $time >> 32);
1445
+        // 8 Zeros
1446
+        $goid .= pack('H*', '0000000000000000');
1447
+        // Length of the random data
1448
+        $goid .= pack('V', 16);
1449
+        // Random data.
1450
+        for ($i = 0; $i < 16; ++$i) {
1451
+            $goid .= chr(rand(0, 255));
1452
+        }
1453
+
1454
+        // Create a new appointment id for this item
1455
+        $apptid = rand();
1456
+
1457
+        $props[PR_OWNER_APPT_ID] = $apptid;
1458
+        $props[PR_ICON_INDEX] = 1026;
1459
+        $props[$this->proptags['goid']] = $goid;
1460
+        $props[$this->proptags['goid2']] = $goid;
1461
+
1462
+        if (!isset($props[$this->proptags['updatecounter']])) {
1463
+            $props[$this->proptags['updatecounter']] = 0;			// OL also starts sequence no with zero.
1464
+            $props[$this->proptags['last_updatecounter']] = 0;
1465
+        }
1466
+
1467
+        mapi_setprops($this->message, $props);
1468
+    }
1469
+
1470
+    /**
1471
+     * Sends a meeting request by copying it to the outbox, converting
1472
+     * the message class, adding some properties that are required only
1473
+     * for sending the message and submitting the message. Set cancel to
1474
+     * true if you wish to completely cancel the meeting request. You can
1475
+     * specify an optional 'prefix' to prefix the sent message, which is normally
1476
+     * 'Canceled: '.
1477
+     *
1478
+     * @param mixed $cancel
1479
+     * @param mixed $prefix
1480
+     * @param mixed $basedate
1481
+     * @param mixed $modifiedRecips
1482
+     * @param mixed $deletedRecips
1483
+     */
1484
+    public function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $modifiedRecips = false, $deletedRecips = false) {
1485
+        $this->includesResources = false;
1486
+        $this->nonAcceptingResources = [];
1487
+
1488
+        // Get the properties of the message
1489
+        $messageprops = mapi_getprops($this->message, [$this->proptags['recurring']]);
1490
+
1491
+        /*
1492 1492
 		 * Submit message to non-resource recipients
1493 1493
 		 */
1494
-		// Set BusyStatus to olTentative (1)
1495
-		// Set MeetingStatus to olMeetingReceived
1496
-		// Set ResponseStatus to olResponseNotResponded
1494
+        // Set BusyStatus to olTentative (1)
1495
+        // Set MeetingStatus to olMeetingReceived
1496
+        // Set ResponseStatus to olResponseNotResponded
1497 1497
 
1498
-		/*
1498
+        /*
1499 1499
 		 * While sending recurrence meeting exceptions are not send as attachments
1500 1500
 		 * because first all exceptions are send and then recurrence meeting is sent.
1501 1501
 		 */
1502
-		if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) {
1503
-			// Book resource
1504
-			$resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1505
-
1506
-			if (!$this->errorSetResource) {
1507
-				$recurr = new Recurrence($this->openDefaultStore(), $this->message);
1508
-
1509
-				// First send meetingrequest for recurring item
1510
-				$this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $modifiedRecips, $deletedRecips);
1511
-
1512
-				// Then send all meeting request for all exceptions
1513
-				$exceptions = $recurr->getAllExceptions();
1514
-				if ($exceptions) {
1515
-					foreach ($exceptions as $exceptionBasedate) {
1516
-						$attach = $recurr->getExceptionAttachment($exceptionBasedate);
1517
-
1518
-						if ($attach) {
1519
-							$occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
1520
-							$this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $modifiedRecips, $deletedRecips);
1521
-							mapi_savechanges($attach);
1522
-						}
1523
-					}
1524
-				}
1525
-			}
1526
-		}
1527
-		else {
1528
-			// Basedate found, an exception is to be send
1529
-			if ($basedate) {
1530
-				$recurr = new Recurrence($this->openDefaultStore(), $this->message);
1531
-
1532
-				if ($cancel) {
1533
-					// @TODO: remove occurrence from Resource's Calendar if resource was booked for whole series
1534
-					$this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false);
1535
-				}
1536
-				else {
1537
-					$attach = $recurr->getExceptionAttachment($basedate);
1538
-
1539
-					if ($attach) {
1540
-						$occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
1541
-
1542
-						// Book resource for this occurrence
1543
-						$resourceRecipData = $this->bookResources($occurrenceItem, $cancel, $prefix, $basedate);
1544
-
1545
-						if (!$this->errorSetResource) {
1546
-							// Save all previous changes
1547
-							mapi_savechanges($this->message);
1548
-
1549
-							$this->submitMeetingRequest($occurrenceItem, $cancel, $prefix, $basedate, $recurr, true, $modifiedRecips, $deletedRecips);
1550
-							mapi_savechanges($occurrenceItem);
1551
-							mapi_savechanges($attach);
1552
-						}
1553
-					}
1554
-				}
1555
-			}
1556
-			else {
1557
-				// This is normal meeting
1558
-				$resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1559
-
1560
-				if (!$this->errorSetResource) {
1561
-					$this->submitMeetingRequest($this->message, $cancel, $prefix, false, false, false, $modifiedRecips, $deletedRecips);
1562
-				}
1563
-			}
1564
-		}
1565
-
1566
-		if (isset($this->errorSetResource) && $this->errorSetResource) {
1567
-			return [
1568
-				'error' => $this->errorSetResource,
1569
-				'displayname' => $this->recipientDisplayname,
1570
-			];
1571
-		}
1572
-
1573
-		return true;
1574
-	}
1575
-
1576
-	/**
1577
-	 * This function will get freebusy data for user based on the timeframe passed in arguments.
1578
-	 *
1579
-	 * @param {HexString} $entryID Entryid of the user for which we need to get freebusy data
1580
-	 * @param {Number} $start start offset for freebusy publish range
1581
-	 * @param {Number} $end end offset for freebusy publish range
1582
-	 *
1583
-	 * @return {Array} freebusy blocks for passed publish range
1584
-	 */
1585
-	public function getFreeBusyInfo($entryID, $start, $end) {
1586
-		$result = [];
1587
-
1588
-		$retval = mapi_getuseravailability($GLOBALS['mapisession']->getSession(), $entryID, $start, $end);
1589
-		if (empty($retval)) {
1590
-			return $result;
1591
-		}
1592
-		$freebusy = json_decode($retval, true);
1593
-		if (strcasecmp($freebusy['permission'], 'none') == 0) {
1594
-			return $result;
1595
-		}
1596
-		$last_end = $start;
1597
-		foreach ($freebusy['events'] as $event) {
1598
-			$blockItem = [];
1599
-			$blockItem['start'] = $event['StartTime'];
1600
-			$blockItem['end'] = $event['EndTime'];
1601
-			if ($event['BusyType'] == 'Free') {
1602
-				$blockItem['status'] = 0;
1603
-			}
1604
-			elseif ($event['BusyType'] == 'Tentative') {
1605
-				$blockItem['status'] = 1;
1606
-			}
1607
-			elseif ($event['BusyType'] == 'Busy') {
1608
-				$blockItem['status'] = 2;
1609
-			}
1610
-			elseif ($event['BusyType'] == 'OOF') {
1611
-				$blockItem['status'] = 3;
1612
-			}
1613
-			elseif ($event['BusyType'] == 'WorkingElsewhere') {
1614
-				$blockItem['status'] = 4;
1615
-			}
1616
-			else {
1617
-				$blockItem['status'] = -1;
1618
-			}
1619
-			$last_end = $event['EndTime'];
1620
-			$result[] = $blockItem;
1621
-		}
1622
-
1623
-		return $result;
1624
-	}
1625
-
1626
-	/**
1627
-	 * Updates the message after an update has been performed (for example,
1628
-	 * changing the time of the meeting). This must be called before re-sending
1629
-	 * the meeting request. You can also call this function instead of 'setMeetingRequest()'
1630
-	 * as it will automatically call setMeetingRequest on this object if it is the first
1631
-	 * call to this function.
1632
-	 *
1633
-	 * @param mixed $basedate
1634
-	 */
1635
-	public function updateMeetingRequest($basedate = false) {
1636
-		$messageprops = mapi_getprops($this->message, [$this->proptags['last_updatecounter'], $this->proptags['goid']]);
1637
-
1638
-		if (!isset($messageprops[$this->proptags['goid']])) {
1639
-			$this->setMeetingRequest($basedate);
1640
-		}
1641
-		else {
1642
-			$counter = $messageprops[$this->proptags['last_updatecounter']] + 1;
1643
-
1644
-			// increment value of last_updatecounter, last_updatecounter will be common for recurring series
1645
-			// so even if you sending an exception only you need to update the last_updatecounter in the recurring series message
1646
-			// this way we can make sure that every time we will be using a uniwue number for every operation
1647
-			mapi_setprops($this->message, [$this->proptags['last_updatecounter'] => $counter]);
1648
-		}
1649
-	}
1650
-
1651
-	/**
1652
-	 * Returns TRUE if we are the organiser of the meeting. Can be used with any type of meeting object.
1653
-	 */
1654
-	public function isLocalOrganiser() {
1655
-		$props = mapi_getprops($this->message, [$this->proptags['goid'], PR_MESSAGE_CLASS]);
1656
-
1657
-		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
1658
-			// we are checking with calendar item
1659
-			$calendarItem = $this->message;
1660
-		}
1661
-		else {
1662
-			// we are checking with meeting request / response / cancellation mail
1663
-			// get calendar items
1664
-			$calendarItem = $this->getCorrespondentCalendarItem(true);
1665
-		}
1666
-
1667
-		// even if we have received request/response for exception/occurrence then also
1668
-		// we can check recurring series for organizer, no need to check with exception/occurrence
1669
-
1670
-		if ($calendarItem !== false) {
1671
-			$messageProps = mapi_getprops($calendarItem, [$this->proptags['responsestatus']]);
1672
-
1673
-			if (isset($messageProps[$this->proptags['responsestatus']]) && $messageProps[$this->proptags['responsestatus']] === olResponseOrganized) {
1674
-				return true;
1675
-			}
1676
-		}
1677
-
1678
-		return false;
1679
-	}
1680
-
1681
-	/*
1502
+        if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) {
1503
+            // Book resource
1504
+            $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1505
+
1506
+            if (!$this->errorSetResource) {
1507
+                $recurr = new Recurrence($this->openDefaultStore(), $this->message);
1508
+
1509
+                // First send meetingrequest for recurring item
1510
+                $this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $modifiedRecips, $deletedRecips);
1511
+
1512
+                // Then send all meeting request for all exceptions
1513
+                $exceptions = $recurr->getAllExceptions();
1514
+                if ($exceptions) {
1515
+                    foreach ($exceptions as $exceptionBasedate) {
1516
+                        $attach = $recurr->getExceptionAttachment($exceptionBasedate);
1517
+
1518
+                        if ($attach) {
1519
+                            $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
1520
+                            $this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $modifiedRecips, $deletedRecips);
1521
+                            mapi_savechanges($attach);
1522
+                        }
1523
+                    }
1524
+                }
1525
+            }
1526
+        }
1527
+        else {
1528
+            // Basedate found, an exception is to be send
1529
+            if ($basedate) {
1530
+                $recurr = new Recurrence($this->openDefaultStore(), $this->message);
1531
+
1532
+                if ($cancel) {
1533
+                    // @TODO: remove occurrence from Resource's Calendar if resource was booked for whole series
1534
+                    $this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false);
1535
+                }
1536
+                else {
1537
+                    $attach = $recurr->getExceptionAttachment($basedate);
1538
+
1539
+                    if ($attach) {
1540
+                        $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
1541
+
1542
+                        // Book resource for this occurrence
1543
+                        $resourceRecipData = $this->bookResources($occurrenceItem, $cancel, $prefix, $basedate);
1544
+
1545
+                        if (!$this->errorSetResource) {
1546
+                            // Save all previous changes
1547
+                            mapi_savechanges($this->message);
1548
+
1549
+                            $this->submitMeetingRequest($occurrenceItem, $cancel, $prefix, $basedate, $recurr, true, $modifiedRecips, $deletedRecips);
1550
+                            mapi_savechanges($occurrenceItem);
1551
+                            mapi_savechanges($attach);
1552
+                        }
1553
+                    }
1554
+                }
1555
+            }
1556
+            else {
1557
+                // This is normal meeting
1558
+                $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1559
+
1560
+                if (!$this->errorSetResource) {
1561
+                    $this->submitMeetingRequest($this->message, $cancel, $prefix, false, false, false, $modifiedRecips, $deletedRecips);
1562
+                }
1563
+            }
1564
+        }
1565
+
1566
+        if (isset($this->errorSetResource) && $this->errorSetResource) {
1567
+            return [
1568
+                'error' => $this->errorSetResource,
1569
+                'displayname' => $this->recipientDisplayname,
1570
+            ];
1571
+        }
1572
+
1573
+        return true;
1574
+    }
1575
+
1576
+    /**
1577
+     * This function will get freebusy data for user based on the timeframe passed in arguments.
1578
+     *
1579
+     * @param {HexString} $entryID Entryid of the user for which we need to get freebusy data
1580
+     * @param {Number} $start start offset for freebusy publish range
1581
+     * @param {Number} $end end offset for freebusy publish range
1582
+     *
1583
+     * @return {Array} freebusy blocks for passed publish range
1584
+     */
1585
+    public function getFreeBusyInfo($entryID, $start, $end) {
1586
+        $result = [];
1587
+
1588
+        $retval = mapi_getuseravailability($GLOBALS['mapisession']->getSession(), $entryID, $start, $end);
1589
+        if (empty($retval)) {
1590
+            return $result;
1591
+        }
1592
+        $freebusy = json_decode($retval, true);
1593
+        if (strcasecmp($freebusy['permission'], 'none') == 0) {
1594
+            return $result;
1595
+        }
1596
+        $last_end = $start;
1597
+        foreach ($freebusy['events'] as $event) {
1598
+            $blockItem = [];
1599
+            $blockItem['start'] = $event['StartTime'];
1600
+            $blockItem['end'] = $event['EndTime'];
1601
+            if ($event['BusyType'] == 'Free') {
1602
+                $blockItem['status'] = 0;
1603
+            }
1604
+            elseif ($event['BusyType'] == 'Tentative') {
1605
+                $blockItem['status'] = 1;
1606
+            }
1607
+            elseif ($event['BusyType'] == 'Busy') {
1608
+                $blockItem['status'] = 2;
1609
+            }
1610
+            elseif ($event['BusyType'] == 'OOF') {
1611
+                $blockItem['status'] = 3;
1612
+            }
1613
+            elseif ($event['BusyType'] == 'WorkingElsewhere') {
1614
+                $blockItem['status'] = 4;
1615
+            }
1616
+            else {
1617
+                $blockItem['status'] = -1;
1618
+            }
1619
+            $last_end = $event['EndTime'];
1620
+            $result[] = $blockItem;
1621
+        }
1622
+
1623
+        return $result;
1624
+    }
1625
+
1626
+    /**
1627
+     * Updates the message after an update has been performed (for example,
1628
+     * changing the time of the meeting). This must be called before re-sending
1629
+     * the meeting request. You can also call this function instead of 'setMeetingRequest()'
1630
+     * as it will automatically call setMeetingRequest on this object if it is the first
1631
+     * call to this function.
1632
+     *
1633
+     * @param mixed $basedate
1634
+     */
1635
+    public function updateMeetingRequest($basedate = false) {
1636
+        $messageprops = mapi_getprops($this->message, [$this->proptags['last_updatecounter'], $this->proptags['goid']]);
1637
+
1638
+        if (!isset($messageprops[$this->proptags['goid']])) {
1639
+            $this->setMeetingRequest($basedate);
1640
+        }
1641
+        else {
1642
+            $counter = $messageprops[$this->proptags['last_updatecounter']] + 1;
1643
+
1644
+            // increment value of last_updatecounter, last_updatecounter will be common for recurring series
1645
+            // so even if you sending an exception only you need to update the last_updatecounter in the recurring series message
1646
+            // this way we can make sure that every time we will be using a uniwue number for every operation
1647
+            mapi_setprops($this->message, [$this->proptags['last_updatecounter'] => $counter]);
1648
+        }
1649
+    }
1650
+
1651
+    /**
1652
+     * Returns TRUE if we are the organiser of the meeting. Can be used with any type of meeting object.
1653
+     */
1654
+    public function isLocalOrganiser() {
1655
+        $props = mapi_getprops($this->message, [$this->proptags['goid'], PR_MESSAGE_CLASS]);
1656
+
1657
+        if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
1658
+            // we are checking with calendar item
1659
+            $calendarItem = $this->message;
1660
+        }
1661
+        else {
1662
+            // we are checking with meeting request / response / cancellation mail
1663
+            // get calendar items
1664
+            $calendarItem = $this->getCorrespondentCalendarItem(true);
1665
+        }
1666
+
1667
+        // even if we have received request/response for exception/occurrence then also
1668
+        // we can check recurring series for organizer, no need to check with exception/occurrence
1669
+
1670
+        if ($calendarItem !== false) {
1671
+            $messageProps = mapi_getprops($calendarItem, [$this->proptags['responsestatus']]);
1672
+
1673
+            if (isset($messageProps[$this->proptags['responsestatus']]) && $messageProps[$this->proptags['responsestatus']] === olResponseOrganized) {
1674
+                return true;
1675
+            }
1676
+        }
1677
+
1678
+        return false;
1679
+    }
1680
+
1681
+    /*
1682 1682
 	 * Support functions - INTERNAL ONLY
1683 1683
 	 ***************************************************************************************************
1684 1684
 	 */
1685 1685
 
1686
-	/**
1687
-	 * Return the tracking status of a recipient based on the IPM class (passed).
1688
-	 *
1689
-	 * @param mixed $class
1690
-	 */
1691
-	public function getTrackStatus($class) {
1692
-		$status = olRecipientTrackStatusNone;
1693
-
1694
-		switch ($class) {
1695
-			case 'IPM.Schedule.Meeting.Resp.Pos':
1696
-				$status = olRecipientTrackStatusAccepted;
1697
-				break;
1698
-
1699
-			case 'IPM.Schedule.Meeting.Resp.Tent':
1700
-				$status = olRecipientTrackStatusTentative;
1701
-				break;
1702
-
1703
-			case 'IPM.Schedule.Meeting.Resp.Neg':
1704
-				$status = olRecipientTrackStatusDeclined;
1705
-				break;
1706
-		}
1707
-
1708
-		return $status;
1709
-	}
1710
-
1711
-	/**
1712
-	 * Function returns MAPIFolder resource of the folder that currently holds this meeting/meeting request
1713
-	 * object.
1714
-	 */
1715
-	public function openParentFolder() {
1716
-		$messageprops = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
1717
-
1718
-		return mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
1719
-	}
1720
-
1721
-	/**
1722
-	 * Function will return resource of the default calendar folder of store.
1723
-	 *
1724
-	 * @param MAPIStore $store {optional} user store whose default calendar should be opened
1725
-	 *
1726
-	 * @return MAPIFolder default calendar folder of store
1727
-	 */
1728
-	public function openDefaultCalendar($store = false) {
1729
-		return $this->openDefaultFolder(PR_IPM_APPOINTMENT_ENTRYID, $store);
1730
-	}
1731
-
1732
-	/**
1733
-	 * Function will return resource of the default outbox folder of store.
1734
-	 *
1735
-	 * @param MAPIStore $store {optional} user store whose default outbox should be opened
1736
-	 *
1737
-	 * @return MAPIFolder default outbox folder of store
1738
-	 */
1739
-	public function openDefaultOutbox($store = false) {
1740
-		return $this->openBaseFolder(PR_IPM_OUTBOX_ENTRYID, $store);
1741
-	}
1742
-
1743
-	/**
1744
-	 * Function will return resource of the default wastebasket folder of store.
1745
-	 *
1746
-	 * @param MAPIStore $store {optional} user store whose default wastebasket should be opened
1747
-	 *
1748
-	 * @return MAPIFolder default wastebasket folder of store
1749
-	 */
1750
-	public function openDefaultWastebasket($store = false) {
1751
-		return $this->openBaseFolder(PR_IPM_WASTEBASKET_ENTRYID, $store);
1752
-	}
1753
-
1754
-	/**
1755
-	 * Function will return resource of the default calendar folder of store.
1756
-	 *
1757
-	 * @param MAPIStore $store {optional} user store whose default calendar should be opened
1758
-	 *
1759
-	 * @return MAPIFolder default calendar folder of store
1760
-	 */
1761
-	public function getDefaultWastebasketEntryID($store = false) {
1762
-		return $this->getBaseEntryID(PR_IPM_WASTEBASKET_ENTRYID, $store);
1763
-	}
1764
-
1765
-	/**
1766
-	 * Function will return resource of the default sent mail folder of store.
1767
-	 *
1768
-	 * @param MAPIStore $store {optional} user store whose default sent mail should be opened
1769
-	 *
1770
-	 * @return MAPIFolder default sent mail folder of store
1771
-	 */
1772
-	public function getDefaultSentmailEntryID($store = false) {
1773
-		return $this->getBaseEntryID(PR_IPM_SENTMAIL_ENTRYID, $store);
1774
-	}
1775
-
1776
-	/**
1777
-	 * Function will return entryid of any default folder of store. This method is useful when you want
1778
-	 * to get entryid of folder which is stored as properties of inbox folder
1779
-	 * (PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID).
1780
-	 *
1781
-	 * @param PropTag   $prop  proptag of the folder for which we want to get entryid
1782
-	 * @param MAPIStore $store {optional} user store from which we need to get entryid of default folder
1783
-	 *
1784
-	 * @return BinString entryid of folder pointed by $prop
1785
-	 */
1786
-	public function getDefaultFolderEntryID($prop, $store = false) {
1787
-		try {
1788
-			$inbox = mapi_msgstore_getreceivefolder($store ? $store : $this->store);
1789
-			$inboxprops = mapi_getprops($inbox, [$prop]);
1790
-			if (isset($inboxprops[$prop])) {
1791
-				return $inboxprops[$prop];
1792
-			}
1793
-		}
1794
-		catch (MAPIException $e) {
1795
-			// public store doesn't support this method
1796
-			if ($e->getCode() == MAPI_E_NO_SUPPORT) {
1797
-				// don't propagate this error to parent handlers, if store doesn't support it
1798
-				$e->setHandled();
1799
-			}
1800
-		}
1801
-
1802
-		return false;
1803
-	}
1804
-
1805
-	/**
1806
-	 * Function will return resource of any default folder of store.
1807
-	 *
1808
-	 * @param PropTag   $prop  proptag of the folder that we want to open
1809
-	 * @param MAPIStore $store {optional} user store from which we need to open default folder
1810
-	 *
1811
-	 * @return MAPIFolder default folder of store
1812
-	 */
1813
-	public function openDefaultFolder($prop, $store = false) {
1814
-		$folder = false;
1815
-		$entryid = $this->getDefaultFolderEntryID($prop, $store);
1816
-
1817
-		if ($entryid !== false) {
1818
-			$folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
1819
-		}
1820
-
1821
-		return $folder;
1822
-	}
1823
-
1824
-	/**
1825
-	 * Function will return entryid of default folder from store. This method is useful when you want
1826
-	 * to get entryid of folder which is stored as store properties
1827
-	 * (PR_IPM_FAVORITES_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID).
1828
-	 *
1829
-	 * @param PropTag   $prop  proptag of the folder whose entryid we want to get
1830
-	 * @param MAPIStore $store {optional} user store from which we need to get entryid of default folder
1831
-	 *
1832
-	 * @return BinString entryid of default folder from store
1833
-	 */
1834
-	public function getBaseEntryID($prop, $store = false) {
1835
-		$storeprops = mapi_getprops($store ? $store : $this->store, [$prop]);
1836
-		if (!isset($storeprops[$prop])) {
1837
-			return false;
1838
-		}
1839
-
1840
-		return $storeprops[$prop];
1841
-	}
1842
-
1843
-	/**
1844
-	 * Function will return resource of any default folder of store.
1845
-	 *
1846
-	 * @param PropTag   $prop  proptag of the folder that we want to open
1847
-	 * @param MAPIStore $store {optional} user store from which we need to open default folder
1848
-	 *
1849
-	 * @return MAPIFolder default folder of store
1850
-	 */
1851
-	public function openBaseFolder($prop, $store = false) {
1852
-		$folder = false;
1853
-		$entryid = $this->getBaseEntryID($prop, $store);
1854
-
1855
-		if ($entryid !== false) {
1856
-			$folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
1857
-		}
1858
-
1859
-		return $folder;
1860
-	}
1861
-
1862
-	/**
1863
-	 * Function checks whether user has access over the specified folder or not.
1864
-	 *
1865
-	 * @param Binary    $entryid entryid The entryid of the folder to check
1866
-	 * @param MAPIStore $store   (optional) store from which folder should be opened
1867
-	 *
1868
-	 * @return bool true if user has an access over the folder, false if not
1869
-	 */
1870
-	public function checkFolderWriteAccess($entryid, $store = false) {
1871
-		$accessToFolder = false;
1872
-
1873
-		if (!empty($entryid)) {
1874
-			if ($store === false) {
1875
-				$store = $this->store;
1876
-			}
1877
-
1878
-			try {
1879
-				$folder = mapi_msgstore_openentry($store, $entryid);
1880
-				$folderProps = mapi_getprops($folder, [PR_ACCESS]);
1881
-				if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1882
-					$accessToFolder = true;
1883
-				}
1884
-			}
1885
-			catch (MAPIException $e) {
1886
-				// we don't have rights to open folder, so return false
1887
-				if ($e->getCode() == MAPI_E_NO_ACCESS) {
1888
-					return $accessToFolder;
1889
-				}
1890
-
1891
-				// rethrow other errors
1892
-				throw $e;
1893
-			}
1894
-		}
1895
-
1896
-		return $accessToFolder;
1897
-	}
1898
-
1899
-	/**
1900
-	 * Function checks whether user has access over the specified folder or not.
1901
-	 *
1902
-	 * @param object MAPI Message Store Object
1903
-	 * @param mixed $store
1904
-	 *
1905
-	 * @return bool true if user has an access over the folder, false if not
1906
-	 */
1907
-	public function checkCalendarWriteAccess($store = false) {
1908
-		if ($store === false) {
1909
-			// If this meeting request is received by a delegate then open delegator's store.
1910
-			$messageProps = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
1911
-			if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
1912
-				$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID]);
1913
-
1914
-				$store = $delegatorStore['store'];
1915
-			}
1916
-			else {
1917
-				$store = $this->store;
1918
-			}
1919
-		}
1920
-
1921
-		// If the store is a public folder, the calendar folder is the PARENT_ENTRYID of the calendar item
1922
-		$provider = mapi_getprops($store, [PR_MDB_PROVIDER]);
1923
-		if (isset($provider[PR_MDB_PROVIDER]) && $provider[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1924
-			$entryid = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
1925
-			$entryid = $entryid[PR_PARENT_ENTRYID];
1926
-		}
1927
-		else {
1928
-			$entryid = $this->getDefaultFolderEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1929
-			if ($entryid === false) {
1930
-				$entryid = $this->getBaseEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1931
-			}
1932
-
1933
-			if ($entryid === false) {
1934
-				return false;
1935
-			}
1936
-		}
1937
-
1938
-		return $this->checkFolderWriteAccess($entryid, $store);
1939
-	}
1940
-
1941
-	/**
1942
-	 * Function will resolve the user and open its store.
1943
-	 *
1944
-	 * @param string $ownerentryid the entryid of the user
1945
-	 *
1946
-	 * @return MAPIStore store of the user
1947
-	 */
1948
-	public function openCustomUserStore($ownerentryid) {
1949
-		$ab = mapi_openaddressbook($this->session);
1950
-
1951
-		try {
1952
-			$mailuser = mapi_ab_openentry($ab, $ownerentryid);
1953
-		}
1954
-		catch (MAPIException $e) {
1955
-			return;
1956
-		}
1957
-
1958
-		$mailuserprops = mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
1959
-		$storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
1960
-
1961
-		return mapi_openmsgstore($this->session, $storeid);
1962
-	}
1963
-
1964
-	/**
1965
-	 * Function which sends response to organizer when attendee accepts, declines or proposes new time to a received meeting request.
1966
-	 *
1967
-	 * @param int   $status              response status of attendee
1968
-	 * @param array $proposeNewTimeProps properties of attendee's proposal
1969
-	 * @param int   $basedate            date of occurrence which attendee has responded
1970
-	 * @param mixed $body
1971
-	 * @param mixed $store
1972
-	 * @param mixed $calFolder
1973
-	 */
1974
-	public function createResponse($status, $proposeNewTimeProps = [], $body = false, $store, $basedate = false, $calFolder) {
1975
-		$messageprops = mapi_getprops($this->message, [PR_SENT_REPRESENTING_ENTRYID,
1976
-			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
1977
-			PR_SENT_REPRESENTING_ADDRTYPE,
1978
-			PR_SENT_REPRESENTING_NAME,
1979
-			PR_SENT_REPRESENTING_SEARCH_KEY,
1980
-			$this->proptags['goid'],
1981
-			$this->proptags['goid2'],
1982
-			$this->proptags['location'],
1983
-			$this->proptags['startdate'],
1984
-			$this->proptags['duedate'],
1985
-			$this->proptags['recurring'],
1986
-			$this->proptags['recurring_pattern'],
1987
-			$this->proptags['recurrence_data'],
1988
-			$this->proptags['timezone_data'],
1989
-			$this->proptags['timezone'],
1990
-			$this->proptags['updatecounter'],
1991
-			PR_SUBJECT,
1992
-			PR_MESSAGE_CLASS,
1993
-			PR_OWNER_APPT_ID,
1994
-			$this->proptags['is_exception'],
1995
-		]);
1996
-
1997
-		if ($basedate && !$this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
1998
-			// we are creating response from a recurring calendar item object
1999
-			// We found basedate,so opened occurrence and get properties.
2000
-			$recurr = new Recurrence($store, $this->message);
2001
-			$exception = $recurr->getExceptionAttachment($basedate);
2002
-
2003
-			if ($exception) {
2004
-				// Exception found, Now retrieve properties
2005
-				$imessage = mapi_attach_openobj($exception, 0);
2006
-				$imsgprops = mapi_getprops($imessage);
2007
-
2008
-				// If location is provided, copy it to the response
2009
-				if (isset($imsgprops[$this->proptags['location']])) {
2010
-					$messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']];
2011
-				}
2012
-
2013
-				// Update $messageprops with timings of occurrence
2014
-				$messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']];
2015
-				$messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']];
2016
-
2017
-				// Meeting related properties
2018
-				$props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
2019
-				$props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
2020
-				$props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
2021
-			}
2022
-			else {
2023
-				// Exceptions is deleted.
2024
-				// Update $messageprops with timings of occurrence
2025
-				$messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
2026
-				$messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
2027
-
2028
-				$props[$this->proptags['meetingstatus']] = olNonMeeting;
2029
-				$props[$this->proptags['responsestatus']] = olResponseNone;
2030
-			}
2031
-
2032
-			$props[$this->proptags['recurring']] = false;
2033
-			$props[$this->proptags['is_exception']] = true;
2034
-		}
2035
-		else {
2036
-			// we are creating a response from meeting request mail (it could be recurring or non-recurring)
2037
-			// Send all recurrence info in response, if this is a recurrence meeting.
2038
-			$isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
2039
-			$isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']];
2040
-			if ($isRecurring || $isException) {
2041
-				if ($isRecurring) {
2042
-					$props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']];
2043
-				}
2044
-				if ($isException) {
2045
-					$props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']];
2046
-				}
2047
-				$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
2048
-
2049
-				$calendaritem = mapi_msgstore_openentry($store, $calendaritems[0]);
2050
-				$recurr = new Recurrence($store, $calendaritem);
2051
-			}
2052
-		}
2053
-
2054
-		// we are sending a response for recurring meeting request (or exception), so set some required properties
2055
-		if (isset($recurr) && $recurr) {
2056
-			if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
2057
-				$props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
2058
-			}
2059
-
2060
-			if (!empty($messageprops[$this->proptags['recurrence_data']])) {
2061
-				$props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
2062
-			}
2063
-
2064
-			$props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
2065
-			$props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
2066
-
2067
-			$this->generateRecurDates($recurr, $messageprops, $props);
2068
-		}
2069
-
2070
-		// Create a response message
2071
-		$recip = [];
2072
-		$recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
2073
-		$recip[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
2074
-		$recip[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
2075
-		$recip[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
2076
-		$recip[PR_RECIPIENT_TYPE] = MAPI_TO;
2077
-		$recip[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
2078
-
2079
-		switch ($status) {
2080
-			case olResponseAccepted:
2081
-				$classpostfix = 'Pos';
2082
-				$subjectprefix = dgettext('zarafa', 'Accepted');
2083
-				break;
2084
-
2085
-			case olResponseDeclined:
2086
-				$classpostfix = 'Neg';
2087
-				$subjectprefix = dgettext('zarafa', 'Declined');
2088
-				break;
2089
-
2090
-			case olResponseTentative:
2091
-				$classpostfix = 'Tent';
2092
-				$subjectprefix = dgettext('zarafa', 'Tentatively accepted');
2093
-				break;
2094
-		}
2095
-
2096
-		if (!empty($proposeNewTimeProps)) {
2097
-			// if attendee has proposed new time then change subject prefix
2098
-			$subjectprefix = dgettext('zarafa', 'New Time Proposed');
2099
-		}
2100
-
2101
-		$props[PR_SUBJECT] = $subjectprefix . ': ' . $messageprops[PR_SUBJECT];
2102
-
2103
-		$props[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Resp.' . $classpostfix;
2104
-		if (isset($messageprops[PR_OWNER_APPT_ID])) {
2105
-			$props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2106
-		}
2107
-
2108
-		// Set GlobalId AND CleanGlobalId, if exception then also set basedate into GlobalId(0x3).
2109
-		$props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2110
-		$props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2111
-		$props[$this->proptags['updatecounter']] = isset($messageprops[$this->proptags['updatecounter']]) ? $messageprops[$this->proptags['updatecounter']] : 0;
2112
-
2113
-		if (!empty($proposeNewTimeProps)) {
2114
-			// merge proposal properties to message properties which will be sent to organizer
2115
-			$props = $proposeNewTimeProps + $props;
2116
-		}
2117
-
2118
-		// Set body message in Appointment
2119
-		if (isset($body)) {
2120
-			$props[PR_BODY] = $this->getMeetingTimeInfo() ? $this->getMeetingTimeInfo() : $body;
2121
-		}
2122
-
2123
-		// PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message
2124
-		$props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
2125
-		$props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
2126
-
2127
-		// Set startdate and duedate in response mail.
2128
-		$props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']];
2129
-		$props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']];
2130
-
2131
-		// responselocation is used in the UI in Outlook on the response message
2132
-		if (isset($messageprops[$this->proptags['location']])) {
2133
-			$props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']];
2134
-			$props[$this->proptags['location']] = $messageprops[$this->proptags['location']];
2135
-		}
2136
-
2137
-		$message = $this->createOutgoingMessage($store);
2138
-
2139
-		mapi_setprops($message, $props);
2140
-		mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
2141
-		mapi_savechanges($message);
2142
-		mapi_message_submitmessage($message);
2143
-	}
2144
-
2145
-	/**
2146
-	 * Function which finds items in calendar based on globalId and cleanGlobalId.
2147
-	 *
2148
-	 * @param binary     $goid             GlobalID(0x3) of item
2149
-	 * @param MAPIFolder $calendar         MAPI_folder of user (optional)
2150
-	 * @param bool       $useCleanGlobalId if true then search should be performed on cleanGlobalId(0x23) else globalId(0x3)
2151
-	 */
2152
-	public function findCalendarItems($goid, $calendar = false, $useCleanGlobalId = false) {
2153
-		if ($calendar === false) {
2154
-			// Open the Calendar
2155
-			$calendar = $this->openDefaultCalendar();
2156
-		}
2157
-
2158
-		// Find the item by restricting all items to the correct ID
2159
-		$restrict = [RES_AND, [
2160
-			[RES_PROPERTY,
2161
-				[
2162
-					RELOP => RELOP_EQ,
2163
-					ULPROPTAG => ($useCleanGlobalId === true ? $this->proptags['goid2'] : $this->proptags['goid']),
2164
-					VALUE => $goid,
2165
-				],
2166
-			],
2167
-		]];
2168
-
2169
-		$calendarcontents = mapi_folder_getcontentstable($calendar);
2170
-
2171
-		$rows = mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
2172
-
2173
-		if (empty($rows)) {
2174
-			return;
2175
-		}
2176
-
2177
-		$calendaritems = [];
2178
-
2179
-		// In principle, there should only be one row, but we'll handle them all just in case
2180
-		foreach ($rows as $row) {
2181
-			$calendaritems[] = $row[PR_ENTRYID];
2182
-		}
2183
-
2184
-		return $calendaritems;
2185
-	}
2186
-
2187
-	// Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the
2188
-	// same SMTP address when converted to SMTP
2189
-	public function compareABEntryIDs($entryid1, $entryid2) {
2190
-		// If the session was not passed, just do a 'normal' compare.
2191
-		if (!$this->session) {
2192
-			return $entryid1 == $entryid2;
2193
-		}
2194
-
2195
-		$smtp1 = $this->getSMTPAddress($entryid1);
2196
-		$smtp2 = $this->getSMTPAddress($entryid2);
2197
-
2198
-		if ($smtp1 == $smtp2) {
2199
-			return true;
2200
-		}
2201
-
2202
-		return false;
2203
-	}
2204
-
2205
-	// Gets the SMTP address of the passed addressbook entryid
2206
-	public function getSMTPAddress($entryid) {
2207
-		if (!$this->session) {
2208
-			return false;
2209
-		}
2210
-
2211
-		try {
2212
-			$ab = mapi_openaddressbook($this->session);
2213
-			$abitem = mapi_ab_openentry($ab, $entryid);
2214
-
2215
-			if (!$abitem) {
2216
-				return '';
2217
-			}
2218
-		}
2219
-		catch (MAPIException $e) {
2220
-			return '';
2221
-		}
2222
-
2223
-		$props = mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS]);
2224
-
2225
-		if ($props[PR_ADDRTYPE] == 'SMTP') {
2226
-			return $props[PR_EMAIL_ADDRESS];
2227
-		}
2228
-
2229
-		return $props[PR_SMTP_ADDRESS];
2230
-	}
2231
-
2232
-	/**
2233
-	 * Gets the properties associated with the owner of the passed store:
2234
-	 * PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID, PR_SEARCH_KEY.
2235
-	 *
2236
-	 * @param $store message store
2237
-	 * @param $fallbackToLoggedInUser if true then return properties of logged in user instead of mailbox owner
2238
-	 * not used when passed store is public store. for public store we are always returning logged in user's info.
2239
-	 *
2240
-	 * @return properties of logged in user in an array in sequence of display_name, email address, address type,
2241
-	 *                    entryid and search key
2242
-	 */
2243
-	public function getOwnerAddress($store, $fallbackToLoggedInUser = true) {
2244
-		if (!$this->session) {
2245
-			return false;
2246
-		}
2247
-
2248
-		$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
2249
-
2250
-		$ownerEntryId = false;
2251
-		if (isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) {
2252
-			$ownerEntryId = $storeProps[PR_USER_ENTRYID];
2253
-		}
2254
-
2255
-		if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) {
2256
-			$ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
2257
-		}
2258
-
2259
-		if ($ownerEntryId) {
2260
-			$ab = mapi_openaddressbook($this->session);
2261
-
2262
-			$zarafaUser = mapi_ab_openentry($ab, $ownerEntryId);
2263
-			if (!$zarafaUser) {
2264
-				return false;
2265
-			}
2266
-
2267
-			$ownerProps = mapi_getprops($zarafaUser, [PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SEARCH_KEY]);
2268
-
2269
-			$addrType = $ownerProps[PR_ADDRTYPE];
2270
-			$name = $ownerProps[PR_DISPLAY_NAME];
2271
-			$emailAddr = $ownerProps[PR_EMAIL_ADDRESS];
2272
-			$searchKey = $ownerProps[PR_SEARCH_KEY];
2273
-			$entryId = $ownerEntryId;
2274
-
2275
-			return [$name, $emailAddr, $addrType, $entryId, $searchKey];
2276
-		}
2277
-
2278
-		return false;
2279
-	}
2280
-
2281
-	// Opens this session's default message store
2282
-	public function openDefaultStore() {
2283
-		$storestable = mapi_getmsgstorestable($this->session);
2284
-		$rows = mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
2285
-
2286
-		foreach ($rows as $row) {
2287
-			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
2288
-				$entryid = $row[PR_ENTRYID];
2289
-				break;
2290
-			}
2291
-		}
2292
-
2293
-		if (!$entryid) {
2294
-			return false;
2295
-		}
2296
-
2297
-		return mapi_openmsgstore($this->session, $entryid);
2298
-	}
2299
-
2300
-	/**
2301
-	 *  Function which adds organizer to recipient list which is passed.
2302
-	 *  This function also checks if it has organizer.
2303
-	 *
2304
-	 * @param array $messageProps message properties
2305
-	 * @param array $recipients   recipients list of message
2306
-	 * @param bool  $isException  true if we are processing recipient of exception
2307
-	 */
2308
-	public function addOrganizer($messageProps, &$recipients, $isException = false) {
2309
-		$hasOrganizer = false;
2310
-		// Check if meeting already has an organizer.
2311
-		foreach ($recipients as $key => $recipient) {
2312
-			if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) {
2313
-				$hasOrganizer = true;
2314
-			}
2315
-			elseif ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])) {
2316
-				// Recipients for an occurrence
2317
-				$recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse;
2318
-			}
2319
-		}
2320
-
2321
-		if (!$hasOrganizer) {
2322
-			// Create organizer.
2323
-			$organizer = [];
2324
-			$organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID];
2325
-			$organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
2326
-			$organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
2327
-			$organizer[PR_RECIPIENT_TYPE] = MAPI_TO;
2328
-			$organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
2329
-			$organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_SENT_REPRESENTING_ADDRTYPE];
2330
-			$organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
2331
-			$organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer;
2332
-			$organizer[PR_SEARCH_KEY] = $messageProps[PR_SENT_REPRESENTING_SEARCH_KEY];
2333
-
2334
-			// Add organizer to recipients list.
2335
-			array_unshift($recipients, $organizer);
2336
-		}
2337
-	}
2338
-
2339
-	/**
2340
-	 * Function which removes an exception/occurrence from recurrencing meeting
2341
-	 * when a meeting cancellation of an occurrence is processed.
2342
-	 *
2343
-	 * @param string   $basedate basedate of an occurrence
2344
-	 * @param resource $message  recurring item from which occurrence has to be deleted
2345
-	 * @param resource $store    MAPI_MSG_Store which contains the item
2346
-	 */
2347
-	public function doRemoveExceptionFromCalendar($basedate, $message, $store) {
2348
-		$recurr = new Recurrence($store, $message);
2349
-		$recurr->createException([], $basedate, true);
2350
-		mapi_savechanges($message);
2351
-	}
2352
-
2353
-	/**
2354
-	 * Function which returns basedate of an changed occurrence from globalID of meeting request.
2355
-	 *
2356
-	 *@param binary $goid globalID
2357
-	 *
2358
-	 *@return bool true if basedate is found else false it not found
2359
-	 */
2360
-	public function getBasedateFromGlobalID($goid) {
2361
-		$hexguid = bin2hex($goid);
2362
-		$hexbase = substr($hexguid, 32, 8);
2363
-		$day = hexdec(substr($hexbase, 6, 2));
2364
-		$month = hexdec(substr($hexbase, 4, 2));
2365
-		$year = hexdec(substr($hexbase, 0, 4));
2366
-
2367
-		if ($day && $month && $year) {
2368
-			return gmmktime(0, 0, 0, $month, $day, $year);
2369
-		}
2370
-
2371
-		return false;
2372
-	}
2373
-
2374
-	/**
2375
-	 * Function which sets basedate in globalID of changed occurrence which is to be send.
2376
-	 *
2377
-	 *@param binary $goid globalID
2378
-	 *@param string basedate of changed occurrence
2379
-	 * @param mixed $basedate
2380
-	 *
2381
-	 *@return binary globalID with basedate in it
2382
-	 */
2383
-	public function setBasedateInGlobalID($goid, $basedate = false) {
2384
-		$hexguid = bin2hex($goid);
2385
-		$year = $basedate ? sprintf('%04s', dechex(date('Y', $basedate))) : '0000';
2386
-		$month = $basedate ? sprintf('%02s', dechex(date('m', $basedate))) : '00';
2387
-		$day = $basedate ? sprintf('%02s', dechex(date('d', $basedate))) : '00';
2388
-
2389
-		return hex2bin(strtoupper(substr($hexguid, 0, 32) . $year . $month . $day . substr($hexguid, 40)));
2390
-	}
2391
-
2392
-	/**
2393
-	 * Function which replaces attachments with copy_from in copy_to.
2394
-	 *
2395
-	 * @param MAPIMessage $copy_from      MAPI_message from which attachments are to be copied
2396
-	 * @param MAPIMessage $copy_to        MAPI_message to which attachment are to be copied
2397
-	 * @param bool        $copyExceptions if true then all exceptions should also be sent as attachments
2398
-	 * @param mixed       $copyFrom
2399
-	 * @param mixed       $copyTo
2400
-	 */
2401
-	public function replaceAttachments($copyFrom, $copyTo, $copyExceptions = true) {
2402
-		/* remove all old attachments */
2403
-		$attachmentTable = mapi_message_getattachmenttable($copyTo);
2404
-		if ($attachmentTable) {
2405
-			$attachments = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2406
-
2407
-			foreach ($attachments as $attachProps) {
2408
-				/* remove exceptions too? */
2409
-				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2410
-					continue;
2411
-				}
2412
-				mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
2413
-			}
2414
-		}
2415
-		$attachmentTable = false;
2416
-
2417
-		/* copy new attachments */
2418
-		$attachmentTable = mapi_message_getattachmenttable($copyFrom);
2419
-		if ($attachmentTable) {
2420
-			$attachments = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2421
-
2422
-			foreach ($attachments as $attachProps) {
2423
-				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2424
-					continue;
2425
-				}
2426
-
2427
-				$attachOld = mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
2428
-				$attachNewResourceMsg = mapi_message_createattach($copyTo);
2429
-				mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
2430
-				mapi_savechanges($attachNewResourceMsg);
2431
-			}
2432
-		}
2433
-	}
2434
-
2435
-	/**
2436
-	 * Function which replaces recipients in copy_to with recipients from copyFrom.
2437
-	 *
2438
-	 * @param MAPIMessage $copyFrom   MAPI_message from which recipients are to be copied
2439
-	 * @param MAPIMessage $copyTo     MAPI_message to which recipients are to be copied
2440
-	 * @param bool        $isDelegate indicates delegate is processing
2441
-	 *                                so don't copy delegate information to recipient table
2442
-	 */
2443
-	public function replaceRecipients($copyFrom, $copyTo, $isDelegate = false) {
2444
-		$recipientTable = mapi_message_getrecipienttable($copyFrom);
2445
-
2446
-		// If delegate, then do not add the delegate in recipients
2447
-		if ($isDelegate) {
2448
-			$delegate = mapi_getprops($copyFrom, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
2449
-			$res = [RES_PROPERTY, [
2450
-				RELOP => RELOP_NE,
2451
-				ULPROPTAG => PR_EMAIL_ADDRESS,
2452
-				VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2453
-			],
2454
-			];
2455
-			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
2456
-		}
2457
-		else {
2458
-			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops);
2459
-		}
2460
-
2461
-		$copyToRecipientTable = mapi_message_getrecipienttable($copyTo);
2462
-		$copyToRecipientRows = mapi_table_queryallrows($copyToRecipientTable, [PR_ROWID]);
2463
-
2464
-		mapi_message_modifyrecipients($copyTo, MODRECIP_REMOVE, $copyToRecipientRows);
2465
-		mapi_message_modifyrecipients($copyTo, MODRECIP_ADD, $recipients);
2466
-	}
2467
-
2468
-	/**
2469
-	 * Function creates meeting item in resource's calendar.
2470
-	 *
2471
-	 * @param resource $message  MAPI_message which is to create in resource's calendar
2472
-	 * @param bool     $cancel   cancel meeting
2473
-	 * @param string   $prefix   prefix for subject of meeting
2474
-	 * @param mixed    $basedate
2475
-	 */
2476
-	public function bookResources($message, $cancel, $prefix, $basedate = false) {
2477
-		if (!$this->enableDirectBooking) {
2478
-			return [];
2479
-		}
2480
-
2481
-		// Get the properties of the message
2482
-		$messageprops = mapi_getprops($message);
2483
-
2484
-		if ($basedate) {
2485
-			$recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID]);
2486
-
2487
-			$messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate);
2488
-			$messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
2489
-
2490
-			// Delete properties which are not needed.
2491
-			$deleteProps = [$this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD];
2492
-			foreach ($deleteProps as $propID) {
2493
-				if (isset($messageprops[$propID])) {
2494
-					unset($messageprops[$propID]);
2495
-				}
2496
-			}
2497
-
2498
-			if (isset($messageprops[$this->proptags['recurring']])) {
2499
-				$messageprops[$this->proptags['recurring']] = false;
2500
-			}
2501
-
2502
-			// Set Outlook properties
2503
-			$messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']];
2504
-			$messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']];
2505
-			$messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']];
2506
-			$messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']];
2507
-			$messageprops[$this->proptags['attendee_critical_change']] = time();
2508
-			$messageprops[$this->proptags['owner_critical_change']] = time();
2509
-		}
2510
-
2511
-		// Get resource recipients
2512
-		$getResourcesRestriction = [RES_AND,
2513
-			[[RES_PROPERTY,
2514
-				[RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2515
-					ULPROPTAG => PR_RECIPIENT_TYPE,
2516
-					VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2517
-				],
2518
-			]],
2519
-		];
2520
-		$recipienttable = mapi_message_getrecipienttable($message);
2521
-		$resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
2522
-
2523
-		$this->errorSetResource = false;
2524
-		$resourceRecipData = [];
2525
-
2526
-		// Put appointment into store resource users
2527
-		$i = 0;
2528
-		$len = count($resourceRecipients);
2529
-		while (!$this->errorSetResource && $i < $len) {
2530
-			$userStore = $this->openCustomUserStore($resourceRecipients[$i][PR_ENTRYID]);
2531
-
2532
-			// Open root folder
2533
-			$userRoot = mapi_msgstore_openentry($userStore, null);
2534
-
2535
-			// Get calendar entryID
2536
-			$userRootProps = mapi_getprops($userRoot, [PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS]);
2537
-
2538
-			// Open Calendar folder
2539
-			$accessToFolder = false;
2540
-
2541
-			try {
2542
-				// @FIXME this checks delegate has access to resource's calendar folder
2543
-				// but it should use boss' credentials
2544
-
2545
-				$accessToFolder = $this->checkCalendarWriteAccess($this->store);
2546
-				if ($accessToFolder) {
2547
-					$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
2548
-				}
2549
-			}
2550
-			catch (MAPIException $e) {
2551
-				$e->setHandled();
2552
-				$this->errorSetResource = 1; // No access
2553
-			}
2554
-
2555
-			if ($accessToFolder) {
2556
-				/**
2557
-				 * Get the LocalFreebusy message that contains the properties that
2558
-				 * are set to accept or decline resource meeting requests.
2559
-				 */
2560
-				$localFreebusyMsg = freebusy::getLocalFreeBusyMessage($userStore);
2561
-				if ($localFreebusyMsg) {
2562
-					$props = mapi_getprops($localFreebusyMsg, [PR_PROCESS_MEETING_REQUESTS, PR_DECLINE_RECURRING_MEETING_REQUESTS, PR_DECLINE_CONFLICTING_MEETING_REQUESTS]);
2563
-
2564
-					$acceptMeetingRequests = isset($props[PR_PROCESS_MEETING_REQUESTS]) ? $props[PR_PROCESS_MEETING_REQUESTS] : false;
2565
-					$declineRecurringMeetingRequests = isset($props[PR_DECLINE_RECURRING_MEETING_REQUESTS]) ? $props[PR_DECLINE_RECURRING_MEETING_REQUESTS] : false;
2566
-					$declineConflictingMeetingRequests = isset($props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS]) ? $props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS] : false;
2567
-
2568
-					if (!$acceptMeetingRequests) {
2569
-						/*
1686
+    /**
1687
+     * Return the tracking status of a recipient based on the IPM class (passed).
1688
+     *
1689
+     * @param mixed $class
1690
+     */
1691
+    public function getTrackStatus($class) {
1692
+        $status = olRecipientTrackStatusNone;
1693
+
1694
+        switch ($class) {
1695
+            case 'IPM.Schedule.Meeting.Resp.Pos':
1696
+                $status = olRecipientTrackStatusAccepted;
1697
+                break;
1698
+
1699
+            case 'IPM.Schedule.Meeting.Resp.Tent':
1700
+                $status = olRecipientTrackStatusTentative;
1701
+                break;
1702
+
1703
+            case 'IPM.Schedule.Meeting.Resp.Neg':
1704
+                $status = olRecipientTrackStatusDeclined;
1705
+                break;
1706
+        }
1707
+
1708
+        return $status;
1709
+    }
1710
+
1711
+    /**
1712
+     * Function returns MAPIFolder resource of the folder that currently holds this meeting/meeting request
1713
+     * object.
1714
+     */
1715
+    public function openParentFolder() {
1716
+        $messageprops = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
1717
+
1718
+        return mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
1719
+    }
1720
+
1721
+    /**
1722
+     * Function will return resource of the default calendar folder of store.
1723
+     *
1724
+     * @param MAPIStore $store {optional} user store whose default calendar should be opened
1725
+     *
1726
+     * @return MAPIFolder default calendar folder of store
1727
+     */
1728
+    public function openDefaultCalendar($store = false) {
1729
+        return $this->openDefaultFolder(PR_IPM_APPOINTMENT_ENTRYID, $store);
1730
+    }
1731
+
1732
+    /**
1733
+     * Function will return resource of the default outbox folder of store.
1734
+     *
1735
+     * @param MAPIStore $store {optional} user store whose default outbox should be opened
1736
+     *
1737
+     * @return MAPIFolder default outbox folder of store
1738
+     */
1739
+    public function openDefaultOutbox($store = false) {
1740
+        return $this->openBaseFolder(PR_IPM_OUTBOX_ENTRYID, $store);
1741
+    }
1742
+
1743
+    /**
1744
+     * Function will return resource of the default wastebasket folder of store.
1745
+     *
1746
+     * @param MAPIStore $store {optional} user store whose default wastebasket should be opened
1747
+     *
1748
+     * @return MAPIFolder default wastebasket folder of store
1749
+     */
1750
+    public function openDefaultWastebasket($store = false) {
1751
+        return $this->openBaseFolder(PR_IPM_WASTEBASKET_ENTRYID, $store);
1752
+    }
1753
+
1754
+    /**
1755
+     * Function will return resource of the default calendar folder of store.
1756
+     *
1757
+     * @param MAPIStore $store {optional} user store whose default calendar should be opened
1758
+     *
1759
+     * @return MAPIFolder default calendar folder of store
1760
+     */
1761
+    public function getDefaultWastebasketEntryID($store = false) {
1762
+        return $this->getBaseEntryID(PR_IPM_WASTEBASKET_ENTRYID, $store);
1763
+    }
1764
+
1765
+    /**
1766
+     * Function will return resource of the default sent mail folder of store.
1767
+     *
1768
+     * @param MAPIStore $store {optional} user store whose default sent mail should be opened
1769
+     *
1770
+     * @return MAPIFolder default sent mail folder of store
1771
+     */
1772
+    public function getDefaultSentmailEntryID($store = false) {
1773
+        return $this->getBaseEntryID(PR_IPM_SENTMAIL_ENTRYID, $store);
1774
+    }
1775
+
1776
+    /**
1777
+     * Function will return entryid of any default folder of store. This method is useful when you want
1778
+     * to get entryid of folder which is stored as properties of inbox folder
1779
+     * (PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID).
1780
+     *
1781
+     * @param PropTag   $prop  proptag of the folder for which we want to get entryid
1782
+     * @param MAPIStore $store {optional} user store from which we need to get entryid of default folder
1783
+     *
1784
+     * @return BinString entryid of folder pointed by $prop
1785
+     */
1786
+    public function getDefaultFolderEntryID($prop, $store = false) {
1787
+        try {
1788
+            $inbox = mapi_msgstore_getreceivefolder($store ? $store : $this->store);
1789
+            $inboxprops = mapi_getprops($inbox, [$prop]);
1790
+            if (isset($inboxprops[$prop])) {
1791
+                return $inboxprops[$prop];
1792
+            }
1793
+        }
1794
+        catch (MAPIException $e) {
1795
+            // public store doesn't support this method
1796
+            if ($e->getCode() == MAPI_E_NO_SUPPORT) {
1797
+                // don't propagate this error to parent handlers, if store doesn't support it
1798
+                $e->setHandled();
1799
+            }
1800
+        }
1801
+
1802
+        return false;
1803
+    }
1804
+
1805
+    /**
1806
+     * Function will return resource of any default folder of store.
1807
+     *
1808
+     * @param PropTag   $prop  proptag of the folder that we want to open
1809
+     * @param MAPIStore $store {optional} user store from which we need to open default folder
1810
+     *
1811
+     * @return MAPIFolder default folder of store
1812
+     */
1813
+    public function openDefaultFolder($prop, $store = false) {
1814
+        $folder = false;
1815
+        $entryid = $this->getDefaultFolderEntryID($prop, $store);
1816
+
1817
+        if ($entryid !== false) {
1818
+            $folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
1819
+        }
1820
+
1821
+        return $folder;
1822
+    }
1823
+
1824
+    /**
1825
+     * Function will return entryid of default folder from store. This method is useful when you want
1826
+     * to get entryid of folder which is stored as store properties
1827
+     * (PR_IPM_FAVORITES_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID).
1828
+     *
1829
+     * @param PropTag   $prop  proptag of the folder whose entryid we want to get
1830
+     * @param MAPIStore $store {optional} user store from which we need to get entryid of default folder
1831
+     *
1832
+     * @return BinString entryid of default folder from store
1833
+     */
1834
+    public function getBaseEntryID($prop, $store = false) {
1835
+        $storeprops = mapi_getprops($store ? $store : $this->store, [$prop]);
1836
+        if (!isset($storeprops[$prop])) {
1837
+            return false;
1838
+        }
1839
+
1840
+        return $storeprops[$prop];
1841
+    }
1842
+
1843
+    /**
1844
+     * Function will return resource of any default folder of store.
1845
+     *
1846
+     * @param PropTag   $prop  proptag of the folder that we want to open
1847
+     * @param MAPIStore $store {optional} user store from which we need to open default folder
1848
+     *
1849
+     * @return MAPIFolder default folder of store
1850
+     */
1851
+    public function openBaseFolder($prop, $store = false) {
1852
+        $folder = false;
1853
+        $entryid = $this->getBaseEntryID($prop, $store);
1854
+
1855
+        if ($entryid !== false) {
1856
+            $folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
1857
+        }
1858
+
1859
+        return $folder;
1860
+    }
1861
+
1862
+    /**
1863
+     * Function checks whether user has access over the specified folder or not.
1864
+     *
1865
+     * @param Binary    $entryid entryid The entryid of the folder to check
1866
+     * @param MAPIStore $store   (optional) store from which folder should be opened
1867
+     *
1868
+     * @return bool true if user has an access over the folder, false if not
1869
+     */
1870
+    public function checkFolderWriteAccess($entryid, $store = false) {
1871
+        $accessToFolder = false;
1872
+
1873
+        if (!empty($entryid)) {
1874
+            if ($store === false) {
1875
+                $store = $this->store;
1876
+            }
1877
+
1878
+            try {
1879
+                $folder = mapi_msgstore_openentry($store, $entryid);
1880
+                $folderProps = mapi_getprops($folder, [PR_ACCESS]);
1881
+                if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1882
+                    $accessToFolder = true;
1883
+                }
1884
+            }
1885
+            catch (MAPIException $e) {
1886
+                // we don't have rights to open folder, so return false
1887
+                if ($e->getCode() == MAPI_E_NO_ACCESS) {
1888
+                    return $accessToFolder;
1889
+                }
1890
+
1891
+                // rethrow other errors
1892
+                throw $e;
1893
+            }
1894
+        }
1895
+
1896
+        return $accessToFolder;
1897
+    }
1898
+
1899
+    /**
1900
+     * Function checks whether user has access over the specified folder or not.
1901
+     *
1902
+     * @param object MAPI Message Store Object
1903
+     * @param mixed $store
1904
+     *
1905
+     * @return bool true if user has an access over the folder, false if not
1906
+     */
1907
+    public function checkCalendarWriteAccess($store = false) {
1908
+        if ($store === false) {
1909
+            // If this meeting request is received by a delegate then open delegator's store.
1910
+            $messageProps = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
1911
+            if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
1912
+                $delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID]);
1913
+
1914
+                $store = $delegatorStore['store'];
1915
+            }
1916
+            else {
1917
+                $store = $this->store;
1918
+            }
1919
+        }
1920
+
1921
+        // If the store is a public folder, the calendar folder is the PARENT_ENTRYID of the calendar item
1922
+        $provider = mapi_getprops($store, [PR_MDB_PROVIDER]);
1923
+        if (isset($provider[PR_MDB_PROVIDER]) && $provider[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1924
+            $entryid = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
1925
+            $entryid = $entryid[PR_PARENT_ENTRYID];
1926
+        }
1927
+        else {
1928
+            $entryid = $this->getDefaultFolderEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1929
+            if ($entryid === false) {
1930
+                $entryid = $this->getBaseEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1931
+            }
1932
+
1933
+            if ($entryid === false) {
1934
+                return false;
1935
+            }
1936
+        }
1937
+
1938
+        return $this->checkFolderWriteAccess($entryid, $store);
1939
+    }
1940
+
1941
+    /**
1942
+     * Function will resolve the user and open its store.
1943
+     *
1944
+     * @param string $ownerentryid the entryid of the user
1945
+     *
1946
+     * @return MAPIStore store of the user
1947
+     */
1948
+    public function openCustomUserStore($ownerentryid) {
1949
+        $ab = mapi_openaddressbook($this->session);
1950
+
1951
+        try {
1952
+            $mailuser = mapi_ab_openentry($ab, $ownerentryid);
1953
+        }
1954
+        catch (MAPIException $e) {
1955
+            return;
1956
+        }
1957
+
1958
+        $mailuserprops = mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
1959
+        $storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
1960
+
1961
+        return mapi_openmsgstore($this->session, $storeid);
1962
+    }
1963
+
1964
+    /**
1965
+     * Function which sends response to organizer when attendee accepts, declines or proposes new time to a received meeting request.
1966
+     *
1967
+     * @param int   $status              response status of attendee
1968
+     * @param array $proposeNewTimeProps properties of attendee's proposal
1969
+     * @param int   $basedate            date of occurrence which attendee has responded
1970
+     * @param mixed $body
1971
+     * @param mixed $store
1972
+     * @param mixed $calFolder
1973
+     */
1974
+    public function createResponse($status, $proposeNewTimeProps = [], $body = false, $store, $basedate = false, $calFolder) {
1975
+        $messageprops = mapi_getprops($this->message, [PR_SENT_REPRESENTING_ENTRYID,
1976
+            PR_SENT_REPRESENTING_EMAIL_ADDRESS,
1977
+            PR_SENT_REPRESENTING_ADDRTYPE,
1978
+            PR_SENT_REPRESENTING_NAME,
1979
+            PR_SENT_REPRESENTING_SEARCH_KEY,
1980
+            $this->proptags['goid'],
1981
+            $this->proptags['goid2'],
1982
+            $this->proptags['location'],
1983
+            $this->proptags['startdate'],
1984
+            $this->proptags['duedate'],
1985
+            $this->proptags['recurring'],
1986
+            $this->proptags['recurring_pattern'],
1987
+            $this->proptags['recurrence_data'],
1988
+            $this->proptags['timezone_data'],
1989
+            $this->proptags['timezone'],
1990
+            $this->proptags['updatecounter'],
1991
+            PR_SUBJECT,
1992
+            PR_MESSAGE_CLASS,
1993
+            PR_OWNER_APPT_ID,
1994
+            $this->proptags['is_exception'],
1995
+        ]);
1996
+
1997
+        if ($basedate && !$this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
1998
+            // we are creating response from a recurring calendar item object
1999
+            // We found basedate,so opened occurrence and get properties.
2000
+            $recurr = new Recurrence($store, $this->message);
2001
+            $exception = $recurr->getExceptionAttachment($basedate);
2002
+
2003
+            if ($exception) {
2004
+                // Exception found, Now retrieve properties
2005
+                $imessage = mapi_attach_openobj($exception, 0);
2006
+                $imsgprops = mapi_getprops($imessage);
2007
+
2008
+                // If location is provided, copy it to the response
2009
+                if (isset($imsgprops[$this->proptags['location']])) {
2010
+                    $messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']];
2011
+                }
2012
+
2013
+                // Update $messageprops with timings of occurrence
2014
+                $messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']];
2015
+                $messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']];
2016
+
2017
+                // Meeting related properties
2018
+                $props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
2019
+                $props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
2020
+                $props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
2021
+            }
2022
+            else {
2023
+                // Exceptions is deleted.
2024
+                // Update $messageprops with timings of occurrence
2025
+                $messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
2026
+                $messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
2027
+
2028
+                $props[$this->proptags['meetingstatus']] = olNonMeeting;
2029
+                $props[$this->proptags['responsestatus']] = olResponseNone;
2030
+            }
2031
+
2032
+            $props[$this->proptags['recurring']] = false;
2033
+            $props[$this->proptags['is_exception']] = true;
2034
+        }
2035
+        else {
2036
+            // we are creating a response from meeting request mail (it could be recurring or non-recurring)
2037
+            // Send all recurrence info in response, if this is a recurrence meeting.
2038
+            $isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
2039
+            $isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']];
2040
+            if ($isRecurring || $isException) {
2041
+                if ($isRecurring) {
2042
+                    $props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']];
2043
+                }
2044
+                if ($isException) {
2045
+                    $props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']];
2046
+                }
2047
+                $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
2048
+
2049
+                $calendaritem = mapi_msgstore_openentry($store, $calendaritems[0]);
2050
+                $recurr = new Recurrence($store, $calendaritem);
2051
+            }
2052
+        }
2053
+
2054
+        // we are sending a response for recurring meeting request (or exception), so set some required properties
2055
+        if (isset($recurr) && $recurr) {
2056
+            if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
2057
+                $props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
2058
+            }
2059
+
2060
+            if (!empty($messageprops[$this->proptags['recurrence_data']])) {
2061
+                $props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
2062
+            }
2063
+
2064
+            $props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
2065
+            $props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
2066
+
2067
+            $this->generateRecurDates($recurr, $messageprops, $props);
2068
+        }
2069
+
2070
+        // Create a response message
2071
+        $recip = [];
2072
+        $recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
2073
+        $recip[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
2074
+        $recip[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
2075
+        $recip[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
2076
+        $recip[PR_RECIPIENT_TYPE] = MAPI_TO;
2077
+        $recip[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
2078
+
2079
+        switch ($status) {
2080
+            case olResponseAccepted:
2081
+                $classpostfix = 'Pos';
2082
+                $subjectprefix = dgettext('zarafa', 'Accepted');
2083
+                break;
2084
+
2085
+            case olResponseDeclined:
2086
+                $classpostfix = 'Neg';
2087
+                $subjectprefix = dgettext('zarafa', 'Declined');
2088
+                break;
2089
+
2090
+            case olResponseTentative:
2091
+                $classpostfix = 'Tent';
2092
+                $subjectprefix = dgettext('zarafa', 'Tentatively accepted');
2093
+                break;
2094
+        }
2095
+
2096
+        if (!empty($proposeNewTimeProps)) {
2097
+            // if attendee has proposed new time then change subject prefix
2098
+            $subjectprefix = dgettext('zarafa', 'New Time Proposed');
2099
+        }
2100
+
2101
+        $props[PR_SUBJECT] = $subjectprefix . ': ' . $messageprops[PR_SUBJECT];
2102
+
2103
+        $props[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Resp.' . $classpostfix;
2104
+        if (isset($messageprops[PR_OWNER_APPT_ID])) {
2105
+            $props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2106
+        }
2107
+
2108
+        // Set GlobalId AND CleanGlobalId, if exception then also set basedate into GlobalId(0x3).
2109
+        $props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2110
+        $props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2111
+        $props[$this->proptags['updatecounter']] = isset($messageprops[$this->proptags['updatecounter']]) ? $messageprops[$this->proptags['updatecounter']] : 0;
2112
+
2113
+        if (!empty($proposeNewTimeProps)) {
2114
+            // merge proposal properties to message properties which will be sent to organizer
2115
+            $props = $proposeNewTimeProps + $props;
2116
+        }
2117
+
2118
+        // Set body message in Appointment
2119
+        if (isset($body)) {
2120
+            $props[PR_BODY] = $this->getMeetingTimeInfo() ? $this->getMeetingTimeInfo() : $body;
2121
+        }
2122
+
2123
+        // PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message
2124
+        $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
2125
+        $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
2126
+
2127
+        // Set startdate and duedate in response mail.
2128
+        $props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']];
2129
+        $props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']];
2130
+
2131
+        // responselocation is used in the UI in Outlook on the response message
2132
+        if (isset($messageprops[$this->proptags['location']])) {
2133
+            $props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']];
2134
+            $props[$this->proptags['location']] = $messageprops[$this->proptags['location']];
2135
+        }
2136
+
2137
+        $message = $this->createOutgoingMessage($store);
2138
+
2139
+        mapi_setprops($message, $props);
2140
+        mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
2141
+        mapi_savechanges($message);
2142
+        mapi_message_submitmessage($message);
2143
+    }
2144
+
2145
+    /**
2146
+     * Function which finds items in calendar based on globalId and cleanGlobalId.
2147
+     *
2148
+     * @param binary     $goid             GlobalID(0x3) of item
2149
+     * @param MAPIFolder $calendar         MAPI_folder of user (optional)
2150
+     * @param bool       $useCleanGlobalId if true then search should be performed on cleanGlobalId(0x23) else globalId(0x3)
2151
+     */
2152
+    public function findCalendarItems($goid, $calendar = false, $useCleanGlobalId = false) {
2153
+        if ($calendar === false) {
2154
+            // Open the Calendar
2155
+            $calendar = $this->openDefaultCalendar();
2156
+        }
2157
+
2158
+        // Find the item by restricting all items to the correct ID
2159
+        $restrict = [RES_AND, [
2160
+            [RES_PROPERTY,
2161
+                [
2162
+                    RELOP => RELOP_EQ,
2163
+                    ULPROPTAG => ($useCleanGlobalId === true ? $this->proptags['goid2'] : $this->proptags['goid']),
2164
+                    VALUE => $goid,
2165
+                ],
2166
+            ],
2167
+        ]];
2168
+
2169
+        $calendarcontents = mapi_folder_getcontentstable($calendar);
2170
+
2171
+        $rows = mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
2172
+
2173
+        if (empty($rows)) {
2174
+            return;
2175
+        }
2176
+
2177
+        $calendaritems = [];
2178
+
2179
+        // In principle, there should only be one row, but we'll handle them all just in case
2180
+        foreach ($rows as $row) {
2181
+            $calendaritems[] = $row[PR_ENTRYID];
2182
+        }
2183
+
2184
+        return $calendaritems;
2185
+    }
2186
+
2187
+    // Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the
2188
+    // same SMTP address when converted to SMTP
2189
+    public function compareABEntryIDs($entryid1, $entryid2) {
2190
+        // If the session was not passed, just do a 'normal' compare.
2191
+        if (!$this->session) {
2192
+            return $entryid1 == $entryid2;
2193
+        }
2194
+
2195
+        $smtp1 = $this->getSMTPAddress($entryid1);
2196
+        $smtp2 = $this->getSMTPAddress($entryid2);
2197
+
2198
+        if ($smtp1 == $smtp2) {
2199
+            return true;
2200
+        }
2201
+
2202
+        return false;
2203
+    }
2204
+
2205
+    // Gets the SMTP address of the passed addressbook entryid
2206
+    public function getSMTPAddress($entryid) {
2207
+        if (!$this->session) {
2208
+            return false;
2209
+        }
2210
+
2211
+        try {
2212
+            $ab = mapi_openaddressbook($this->session);
2213
+            $abitem = mapi_ab_openentry($ab, $entryid);
2214
+
2215
+            if (!$abitem) {
2216
+                return '';
2217
+            }
2218
+        }
2219
+        catch (MAPIException $e) {
2220
+            return '';
2221
+        }
2222
+
2223
+        $props = mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS]);
2224
+
2225
+        if ($props[PR_ADDRTYPE] == 'SMTP') {
2226
+            return $props[PR_EMAIL_ADDRESS];
2227
+        }
2228
+
2229
+        return $props[PR_SMTP_ADDRESS];
2230
+    }
2231
+
2232
+    /**
2233
+     * Gets the properties associated with the owner of the passed store:
2234
+     * PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID, PR_SEARCH_KEY.
2235
+     *
2236
+     * @param $store message store
2237
+     * @param $fallbackToLoggedInUser if true then return properties of logged in user instead of mailbox owner
2238
+     * not used when passed store is public store. for public store we are always returning logged in user's info.
2239
+     *
2240
+     * @return properties of logged in user in an array in sequence of display_name, email address, address type,
2241
+     *                    entryid and search key
2242
+     */
2243
+    public function getOwnerAddress($store, $fallbackToLoggedInUser = true) {
2244
+        if (!$this->session) {
2245
+            return false;
2246
+        }
2247
+
2248
+        $storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
2249
+
2250
+        $ownerEntryId = false;
2251
+        if (isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) {
2252
+            $ownerEntryId = $storeProps[PR_USER_ENTRYID];
2253
+        }
2254
+
2255
+        if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) {
2256
+            $ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
2257
+        }
2258
+
2259
+        if ($ownerEntryId) {
2260
+            $ab = mapi_openaddressbook($this->session);
2261
+
2262
+            $zarafaUser = mapi_ab_openentry($ab, $ownerEntryId);
2263
+            if (!$zarafaUser) {
2264
+                return false;
2265
+            }
2266
+
2267
+            $ownerProps = mapi_getprops($zarafaUser, [PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SEARCH_KEY]);
2268
+
2269
+            $addrType = $ownerProps[PR_ADDRTYPE];
2270
+            $name = $ownerProps[PR_DISPLAY_NAME];
2271
+            $emailAddr = $ownerProps[PR_EMAIL_ADDRESS];
2272
+            $searchKey = $ownerProps[PR_SEARCH_KEY];
2273
+            $entryId = $ownerEntryId;
2274
+
2275
+            return [$name, $emailAddr, $addrType, $entryId, $searchKey];
2276
+        }
2277
+
2278
+        return false;
2279
+    }
2280
+
2281
+    // Opens this session's default message store
2282
+    public function openDefaultStore() {
2283
+        $storestable = mapi_getmsgstorestable($this->session);
2284
+        $rows = mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
2285
+
2286
+        foreach ($rows as $row) {
2287
+            if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
2288
+                $entryid = $row[PR_ENTRYID];
2289
+                break;
2290
+            }
2291
+        }
2292
+
2293
+        if (!$entryid) {
2294
+            return false;
2295
+        }
2296
+
2297
+        return mapi_openmsgstore($this->session, $entryid);
2298
+    }
2299
+
2300
+    /**
2301
+     *  Function which adds organizer to recipient list which is passed.
2302
+     *  This function also checks if it has organizer.
2303
+     *
2304
+     * @param array $messageProps message properties
2305
+     * @param array $recipients   recipients list of message
2306
+     * @param bool  $isException  true if we are processing recipient of exception
2307
+     */
2308
+    public function addOrganizer($messageProps, &$recipients, $isException = false) {
2309
+        $hasOrganizer = false;
2310
+        // Check if meeting already has an organizer.
2311
+        foreach ($recipients as $key => $recipient) {
2312
+            if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) {
2313
+                $hasOrganizer = true;
2314
+            }
2315
+            elseif ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])) {
2316
+                // Recipients for an occurrence
2317
+                $recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse;
2318
+            }
2319
+        }
2320
+
2321
+        if (!$hasOrganizer) {
2322
+            // Create organizer.
2323
+            $organizer = [];
2324
+            $organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID];
2325
+            $organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
2326
+            $organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
2327
+            $organizer[PR_RECIPIENT_TYPE] = MAPI_TO;
2328
+            $organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
2329
+            $organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_SENT_REPRESENTING_ADDRTYPE];
2330
+            $organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
2331
+            $organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer;
2332
+            $organizer[PR_SEARCH_KEY] = $messageProps[PR_SENT_REPRESENTING_SEARCH_KEY];
2333
+
2334
+            // Add organizer to recipients list.
2335
+            array_unshift($recipients, $organizer);
2336
+        }
2337
+    }
2338
+
2339
+    /**
2340
+     * Function which removes an exception/occurrence from recurrencing meeting
2341
+     * when a meeting cancellation of an occurrence is processed.
2342
+     *
2343
+     * @param string   $basedate basedate of an occurrence
2344
+     * @param resource $message  recurring item from which occurrence has to be deleted
2345
+     * @param resource $store    MAPI_MSG_Store which contains the item
2346
+     */
2347
+    public function doRemoveExceptionFromCalendar($basedate, $message, $store) {
2348
+        $recurr = new Recurrence($store, $message);
2349
+        $recurr->createException([], $basedate, true);
2350
+        mapi_savechanges($message);
2351
+    }
2352
+
2353
+    /**
2354
+     * Function which returns basedate of an changed occurrence from globalID of meeting request.
2355
+     *
2356
+     *@param binary $goid globalID
2357
+     *
2358
+     *@return bool true if basedate is found else false it not found
2359
+     */
2360
+    public function getBasedateFromGlobalID($goid) {
2361
+        $hexguid = bin2hex($goid);
2362
+        $hexbase = substr($hexguid, 32, 8);
2363
+        $day = hexdec(substr($hexbase, 6, 2));
2364
+        $month = hexdec(substr($hexbase, 4, 2));
2365
+        $year = hexdec(substr($hexbase, 0, 4));
2366
+
2367
+        if ($day && $month && $year) {
2368
+            return gmmktime(0, 0, 0, $month, $day, $year);
2369
+        }
2370
+
2371
+        return false;
2372
+    }
2373
+
2374
+    /**
2375
+     * Function which sets basedate in globalID of changed occurrence which is to be send.
2376
+     *
2377
+     *@param binary $goid globalID
2378
+     *@param string basedate of changed occurrence
2379
+     * @param mixed $basedate
2380
+     *
2381
+     *@return binary globalID with basedate in it
2382
+     */
2383
+    public function setBasedateInGlobalID($goid, $basedate = false) {
2384
+        $hexguid = bin2hex($goid);
2385
+        $year = $basedate ? sprintf('%04s', dechex(date('Y', $basedate))) : '0000';
2386
+        $month = $basedate ? sprintf('%02s', dechex(date('m', $basedate))) : '00';
2387
+        $day = $basedate ? sprintf('%02s', dechex(date('d', $basedate))) : '00';
2388
+
2389
+        return hex2bin(strtoupper(substr($hexguid, 0, 32) . $year . $month . $day . substr($hexguid, 40)));
2390
+    }
2391
+
2392
+    /**
2393
+     * Function which replaces attachments with copy_from in copy_to.
2394
+     *
2395
+     * @param MAPIMessage $copy_from      MAPI_message from which attachments are to be copied
2396
+     * @param MAPIMessage $copy_to        MAPI_message to which attachment are to be copied
2397
+     * @param bool        $copyExceptions if true then all exceptions should also be sent as attachments
2398
+     * @param mixed       $copyFrom
2399
+     * @param mixed       $copyTo
2400
+     */
2401
+    public function replaceAttachments($copyFrom, $copyTo, $copyExceptions = true) {
2402
+        /* remove all old attachments */
2403
+        $attachmentTable = mapi_message_getattachmenttable($copyTo);
2404
+        if ($attachmentTable) {
2405
+            $attachments = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2406
+
2407
+            foreach ($attachments as $attachProps) {
2408
+                /* remove exceptions too? */
2409
+                if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2410
+                    continue;
2411
+                }
2412
+                mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
2413
+            }
2414
+        }
2415
+        $attachmentTable = false;
2416
+
2417
+        /* copy new attachments */
2418
+        $attachmentTable = mapi_message_getattachmenttable($copyFrom);
2419
+        if ($attachmentTable) {
2420
+            $attachments = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2421
+
2422
+            foreach ($attachments as $attachProps) {
2423
+                if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2424
+                    continue;
2425
+                }
2426
+
2427
+                $attachOld = mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
2428
+                $attachNewResourceMsg = mapi_message_createattach($copyTo);
2429
+                mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
2430
+                mapi_savechanges($attachNewResourceMsg);
2431
+            }
2432
+        }
2433
+    }
2434
+
2435
+    /**
2436
+     * Function which replaces recipients in copy_to with recipients from copyFrom.
2437
+     *
2438
+     * @param MAPIMessage $copyFrom   MAPI_message from which recipients are to be copied
2439
+     * @param MAPIMessage $copyTo     MAPI_message to which recipients are to be copied
2440
+     * @param bool        $isDelegate indicates delegate is processing
2441
+     *                                so don't copy delegate information to recipient table
2442
+     */
2443
+    public function replaceRecipients($copyFrom, $copyTo, $isDelegate = false) {
2444
+        $recipientTable = mapi_message_getrecipienttable($copyFrom);
2445
+
2446
+        // If delegate, then do not add the delegate in recipients
2447
+        if ($isDelegate) {
2448
+            $delegate = mapi_getprops($copyFrom, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
2449
+            $res = [RES_PROPERTY, [
2450
+                RELOP => RELOP_NE,
2451
+                ULPROPTAG => PR_EMAIL_ADDRESS,
2452
+                VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2453
+            ],
2454
+            ];
2455
+            $recipients = mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
2456
+        }
2457
+        else {
2458
+            $recipients = mapi_table_queryallrows($recipientTable, $this->recipprops);
2459
+        }
2460
+
2461
+        $copyToRecipientTable = mapi_message_getrecipienttable($copyTo);
2462
+        $copyToRecipientRows = mapi_table_queryallrows($copyToRecipientTable, [PR_ROWID]);
2463
+
2464
+        mapi_message_modifyrecipients($copyTo, MODRECIP_REMOVE, $copyToRecipientRows);
2465
+        mapi_message_modifyrecipients($copyTo, MODRECIP_ADD, $recipients);
2466
+    }
2467
+
2468
+    /**
2469
+     * Function creates meeting item in resource's calendar.
2470
+     *
2471
+     * @param resource $message  MAPI_message which is to create in resource's calendar
2472
+     * @param bool     $cancel   cancel meeting
2473
+     * @param string   $prefix   prefix for subject of meeting
2474
+     * @param mixed    $basedate
2475
+     */
2476
+    public function bookResources($message, $cancel, $prefix, $basedate = false) {
2477
+        if (!$this->enableDirectBooking) {
2478
+            return [];
2479
+        }
2480
+
2481
+        // Get the properties of the message
2482
+        $messageprops = mapi_getprops($message);
2483
+
2484
+        if ($basedate) {
2485
+            $recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID]);
2486
+
2487
+            $messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate);
2488
+            $messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
2489
+
2490
+            // Delete properties which are not needed.
2491
+            $deleteProps = [$this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD];
2492
+            foreach ($deleteProps as $propID) {
2493
+                if (isset($messageprops[$propID])) {
2494
+                    unset($messageprops[$propID]);
2495
+                }
2496
+            }
2497
+
2498
+            if (isset($messageprops[$this->proptags['recurring']])) {
2499
+                $messageprops[$this->proptags['recurring']] = false;
2500
+            }
2501
+
2502
+            // Set Outlook properties
2503
+            $messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']];
2504
+            $messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']];
2505
+            $messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']];
2506
+            $messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']];
2507
+            $messageprops[$this->proptags['attendee_critical_change']] = time();
2508
+            $messageprops[$this->proptags['owner_critical_change']] = time();
2509
+        }
2510
+
2511
+        // Get resource recipients
2512
+        $getResourcesRestriction = [RES_AND,
2513
+            [[RES_PROPERTY,
2514
+                [RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2515
+                    ULPROPTAG => PR_RECIPIENT_TYPE,
2516
+                    VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2517
+                ],
2518
+            ]],
2519
+        ];
2520
+        $recipienttable = mapi_message_getrecipienttable($message);
2521
+        $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
2522
+
2523
+        $this->errorSetResource = false;
2524
+        $resourceRecipData = [];
2525
+
2526
+        // Put appointment into store resource users
2527
+        $i = 0;
2528
+        $len = count($resourceRecipients);
2529
+        while (!$this->errorSetResource && $i < $len) {
2530
+            $userStore = $this->openCustomUserStore($resourceRecipients[$i][PR_ENTRYID]);
2531
+
2532
+            // Open root folder
2533
+            $userRoot = mapi_msgstore_openentry($userStore, null);
2534
+
2535
+            // Get calendar entryID
2536
+            $userRootProps = mapi_getprops($userRoot, [PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS]);
2537
+
2538
+            // Open Calendar folder
2539
+            $accessToFolder = false;
2540
+
2541
+            try {
2542
+                // @FIXME this checks delegate has access to resource's calendar folder
2543
+                // but it should use boss' credentials
2544
+
2545
+                $accessToFolder = $this->checkCalendarWriteAccess($this->store);
2546
+                if ($accessToFolder) {
2547
+                    $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
2548
+                }
2549
+            }
2550
+            catch (MAPIException $e) {
2551
+                $e->setHandled();
2552
+                $this->errorSetResource = 1; // No access
2553
+            }
2554
+
2555
+            if ($accessToFolder) {
2556
+                /**
2557
+                 * Get the LocalFreebusy message that contains the properties that
2558
+                 * are set to accept or decline resource meeting requests.
2559
+                 */
2560
+                $localFreebusyMsg = freebusy::getLocalFreeBusyMessage($userStore);
2561
+                if ($localFreebusyMsg) {
2562
+                    $props = mapi_getprops($localFreebusyMsg, [PR_PROCESS_MEETING_REQUESTS, PR_DECLINE_RECURRING_MEETING_REQUESTS, PR_DECLINE_CONFLICTING_MEETING_REQUESTS]);
2563
+
2564
+                    $acceptMeetingRequests = isset($props[PR_PROCESS_MEETING_REQUESTS]) ? $props[PR_PROCESS_MEETING_REQUESTS] : false;
2565
+                    $declineRecurringMeetingRequests = isset($props[PR_DECLINE_RECURRING_MEETING_REQUESTS]) ? $props[PR_DECLINE_RECURRING_MEETING_REQUESTS] : false;
2566
+                    $declineConflictingMeetingRequests = isset($props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS]) ? $props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS] : false;
2567
+
2568
+                    if (!$acceptMeetingRequests) {
2569
+                        /*
2570 2570
 						 * When a resource has not been set to automatically accept meeting requests,
2571 2571
 						 * the meeting request has to be sent to him rather than being put directly into
2572 2572
 						 * his calendar. No error should be returned.
2573 2573
 						 */
2574
-						// $errorSetResource = 2;
2575
-						$this->nonAcceptingResources[] = $resourceRecipients[$i];
2576
-					}
2577
-					else {
2578
-						if ($declineRecurringMeetingRequests && !$cancel) {
2579
-							// Check if appointment is recurring
2580
-							if ($messageprops[$this->proptags['recurring']]) {
2581
-								$this->errorSetResource = 3;
2582
-							}
2583
-						}
2584
-						if ($declineConflictingMeetingRequests && !$cancel) {
2585
-							// Check for conflicting items
2586
-							if ($calFolder && $this->isMeetingConflicting($message, $userStore, $calFolder)) {
2587
-								$this->errorSetResource = 4; // Conflict
2588
-							}
2589
-						}
2590
-					}
2591
-				}
2592
-			}
2593
-
2594
-			if (!$this->errorSetResource && $accessToFolder) {
2595
-				/**
2596
-				 * First search on GlobalID(0x3)
2597
-				 * If (recurring and occurrence) If Resource was booked for only this occurrence then Resource should have only this occurrence in Calendar and not whole series.
2598
-				 * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesn't matter if search is based on GlobalID.
2599
-				 */
2600
-				$rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
2601
-
2602
-				/*
2574
+                        // $errorSetResource = 2;
2575
+                        $this->nonAcceptingResources[] = $resourceRecipients[$i];
2576
+                    }
2577
+                    else {
2578
+                        if ($declineRecurringMeetingRequests && !$cancel) {
2579
+                            // Check if appointment is recurring
2580
+                            if ($messageprops[$this->proptags['recurring']]) {
2581
+                                $this->errorSetResource = 3;
2582
+                            }
2583
+                        }
2584
+                        if ($declineConflictingMeetingRequests && !$cancel) {
2585
+                            // Check for conflicting items
2586
+                            if ($calFolder && $this->isMeetingConflicting($message, $userStore, $calFolder)) {
2587
+                                $this->errorSetResource = 4; // Conflict
2588
+                            }
2589
+                        }
2590
+                    }
2591
+                }
2592
+            }
2593
+
2594
+            if (!$this->errorSetResource && $accessToFolder) {
2595
+                /**
2596
+                 * First search on GlobalID(0x3)
2597
+                 * If (recurring and occurrence) If Resource was booked for only this occurrence then Resource should have only this occurrence in Calendar and not whole series.
2598
+                 * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesn't matter if search is based on GlobalID.
2599
+                 */
2600
+                $rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
2601
+
2602
+                /*
2603 2603
 				 * If no entry is found then
2604 2604
 				 * 1) Resource doesn't have meeting in Calendar. Seriously!!
2605 2605
 				 * OR
2606 2606
 				 * 2) We were looking for occurrence item but Resource has whole series
2607 2607
 				 */
2608
-				if (empty($rows)) {
2609
-					/**
2610
-					 * Now search on CleanGlobalID(0x23) WHY???
2611
-					 * Because we are looking recurring item.
2612
-					 *
2613
-					 * Possible results of this search
2614
-					 * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID
2615
-					 * 2) If Resource was booked for whole series then it should return series.
2616
-					 */
2617
-					$rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
2618
-
2619
-					$newResourceMsg = false;
2620
-					if (!empty($rows)) {
2621
-						// Since we are looking for recurring item, open every result and check for 'recurring' property.
2622
-						foreach ($rows as $row) {
2623
-							$ResourceMsg = mapi_msgstore_openentry($userStore, $row);
2624
-							$ResourceMsgProps = mapi_getprops($ResourceMsg, [$this->proptags['recurring']]);
2625
-
2626
-							if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2627
-								$newResourceMsg = $ResourceMsg;
2628
-								break;
2629
-							}
2630
-						}
2631
-					}
2632
-
2633
-					// Still no results found. I giveup, create new message.
2634
-					if (!$newResourceMsg) {
2635
-						$newResourceMsg = mapi_folder_createmessage($calFolder);
2636
-					}
2637
-				}
2638
-				else {
2639
-					$newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]);
2640
-				}
2641
-
2642
-				// Prefix the subject if needed
2643
-				if ($prefix && isset($messageprops[PR_SUBJECT])) {
2644
-					$messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT];
2645
-				}
2646
-
2647
-				// Set status to cancelled if needed
2648
-				$messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy)
2649
-				if ($cancel) {
2650
-					$messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled
2651
-					$messageprops[$this->proptags['busystatus']] = fbFree; // Free
2652
-				}
2653
-				else {
2654
-					$messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
2655
-				}
2656
-				$messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource automatically accepts the appointment
2657
-
2658
-				$messageprops[PR_MESSAGE_CLASS] = 'IPM.Appointment';
2659
-
2660
-				// Remove the PR_ICON_INDEX as it is not needed in the sent message.
2661
-				$messageprops[PR_ICON_INDEX] = null;
2662
-				$messageprops[PR_RESPONSE_REQUESTED] = true;
2663
-
2664
-				// get the store of organizer, in case of delegates it will be delegate store
2665
-				$defaultStore = $this->openDefaultStore();
2666
-
2667
-				$storeProps = mapi_getprops($this->store, [PR_ENTRYID]);
2668
-				$defaultStoreProps = mapi_getprops($defaultStore, [PR_ENTRYID]);
2669
-
2670
-				// @FIXME use entryid comparison functions here
2671
-				if ($storeProps[PR_ENTRYID] !== $defaultStoreProps[PR_ENTRYID]) {
2672
-					// get delegate information
2673
-					$addrInfo = $this->getOwnerAddress($defaultStore, false);
2674
-
2675
-					if ($addrInfo) {
2676
-						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2677
-
2678
-						$messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
2679
-						$messageprops[PR_SENDER_NAME] = $ownername;
2680
-						$messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
2681
-						$messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
2682
-						$messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
2683
-					}
2684
-
2685
-					// get delegator information
2686
-					$addrInfo = $this->getOwnerAddress($this->store, false);
2687
-
2688
-					if ($addrInfo) {
2689
-						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2690
-
2691
-						$messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
2692
-						$messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
2693
-						$messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
2694
-						$messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2695
-						$messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2696
-					}
2697
-				}
2698
-				else {
2699
-					// get organizer information
2700
-					$addrinfo = $this->getOwnerAddress($this->store);
2701
-
2702
-					if ($addrinfo) {
2703
-						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo;
2704
-
2705
-						$messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
2706
-						$messageprops[PR_SENDER_NAME] = $ownername;
2707
-						$messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
2708
-						$messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
2709
-						$messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
2710
-
2711
-						$messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
2712
-						$messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
2713
-						$messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
2714
-						$messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2715
-						$messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2716
-					}
2717
-				}
2718
-
2719
-				$messageprops[$this->proptags['replytime']] = time();
2720
-
2721
-				if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2722
-					$recurr = new Recurrence($userStore, $newResourceMsg);
2723
-
2724
-					// Copy recipients list
2725
-					$reciptable = mapi_message_getrecipienttable($message);
2726
-					$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2727
-
2728
-					// add owner to recipient table
2729
-					$this->addOrganizer($messageprops, $recips, true);
2730
-
2731
-					// Update occurrence
2732
-					if ($recurr->isException($basedate)) {
2733
-						$recurr->modifyException($messageprops, $basedate, $recips);
2734
-					}
2735
-					else {
2736
-						$recurr->createException($messageprops, $basedate, false, $recips);
2737
-					}
2738
-				}
2739
-				else {
2740
-					mapi_setprops($newResourceMsg, $messageprops);
2741
-
2742
-					// Copy attachments
2743
-					$this->replaceAttachments($message, $newResourceMsg);
2744
-
2745
-					// Copy all recipients too
2746
-					$this->replaceRecipients($message, $newResourceMsg);
2747
-
2748
-					// Now add organizer also to recipient table
2749
-					$recips = [];
2750
-					$this->addOrganizer($messageprops, $recips);
2751
-
2752
-					mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
2753
-				}
2754
-
2755
-				mapi_savechanges($newResourceMsg);
2756
-
2757
-				$resourceRecipData[] = [
2758
-					'store' => $userStore,
2759
-					'folder' => $calFolder,
2760
-					'msg' => $newResourceMsg,
2761
-				];
2762
-				$this->includesResources = true;
2763
-			}
2764
-			else {
2765
-				/*
2608
+                if (empty($rows)) {
2609
+                    /**
2610
+                     * Now search on CleanGlobalID(0x23) WHY???
2611
+                     * Because we are looking recurring item.
2612
+                     *
2613
+                     * Possible results of this search
2614
+                     * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID
2615
+                     * 2) If Resource was booked for whole series then it should return series.
2616
+                     */
2617
+                    $rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
2618
+
2619
+                    $newResourceMsg = false;
2620
+                    if (!empty($rows)) {
2621
+                        // Since we are looking for recurring item, open every result and check for 'recurring' property.
2622
+                        foreach ($rows as $row) {
2623
+                            $ResourceMsg = mapi_msgstore_openentry($userStore, $row);
2624
+                            $ResourceMsgProps = mapi_getprops($ResourceMsg, [$this->proptags['recurring']]);
2625
+
2626
+                            if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2627
+                                $newResourceMsg = $ResourceMsg;
2628
+                                break;
2629
+                            }
2630
+                        }
2631
+                    }
2632
+
2633
+                    // Still no results found. I giveup, create new message.
2634
+                    if (!$newResourceMsg) {
2635
+                        $newResourceMsg = mapi_folder_createmessage($calFolder);
2636
+                    }
2637
+                }
2638
+                else {
2639
+                    $newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]);
2640
+                }
2641
+
2642
+                // Prefix the subject if needed
2643
+                if ($prefix && isset($messageprops[PR_SUBJECT])) {
2644
+                    $messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT];
2645
+                }
2646
+
2647
+                // Set status to cancelled if needed
2648
+                $messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy)
2649
+                if ($cancel) {
2650
+                    $messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled
2651
+                    $messageprops[$this->proptags['busystatus']] = fbFree; // Free
2652
+                }
2653
+                else {
2654
+                    $messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
2655
+                }
2656
+                $messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource automatically accepts the appointment
2657
+
2658
+                $messageprops[PR_MESSAGE_CLASS] = 'IPM.Appointment';
2659
+
2660
+                // Remove the PR_ICON_INDEX as it is not needed in the sent message.
2661
+                $messageprops[PR_ICON_INDEX] = null;
2662
+                $messageprops[PR_RESPONSE_REQUESTED] = true;
2663
+
2664
+                // get the store of organizer, in case of delegates it will be delegate store
2665
+                $defaultStore = $this->openDefaultStore();
2666
+
2667
+                $storeProps = mapi_getprops($this->store, [PR_ENTRYID]);
2668
+                $defaultStoreProps = mapi_getprops($defaultStore, [PR_ENTRYID]);
2669
+
2670
+                // @FIXME use entryid comparison functions here
2671
+                if ($storeProps[PR_ENTRYID] !== $defaultStoreProps[PR_ENTRYID]) {
2672
+                    // get delegate information
2673
+                    $addrInfo = $this->getOwnerAddress($defaultStore, false);
2674
+
2675
+                    if ($addrInfo) {
2676
+                        list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2677
+
2678
+                        $messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
2679
+                        $messageprops[PR_SENDER_NAME] = $ownername;
2680
+                        $messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
2681
+                        $messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
2682
+                        $messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
2683
+                    }
2684
+
2685
+                    // get delegator information
2686
+                    $addrInfo = $this->getOwnerAddress($this->store, false);
2687
+
2688
+                    if ($addrInfo) {
2689
+                        list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2690
+
2691
+                        $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
2692
+                        $messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
2693
+                        $messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
2694
+                        $messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2695
+                        $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2696
+                    }
2697
+                }
2698
+                else {
2699
+                    // get organizer information
2700
+                    $addrinfo = $this->getOwnerAddress($this->store);
2701
+
2702
+                    if ($addrinfo) {
2703
+                        list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo;
2704
+
2705
+                        $messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
2706
+                        $messageprops[PR_SENDER_NAME] = $ownername;
2707
+                        $messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
2708
+                        $messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
2709
+                        $messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
2710
+
2711
+                        $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
2712
+                        $messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
2713
+                        $messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
2714
+                        $messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2715
+                        $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2716
+                    }
2717
+                }
2718
+
2719
+                $messageprops[$this->proptags['replytime']] = time();
2720
+
2721
+                if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2722
+                    $recurr = new Recurrence($userStore, $newResourceMsg);
2723
+
2724
+                    // Copy recipients list
2725
+                    $reciptable = mapi_message_getrecipienttable($message);
2726
+                    $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2727
+
2728
+                    // add owner to recipient table
2729
+                    $this->addOrganizer($messageprops, $recips, true);
2730
+
2731
+                    // Update occurrence
2732
+                    if ($recurr->isException($basedate)) {
2733
+                        $recurr->modifyException($messageprops, $basedate, $recips);
2734
+                    }
2735
+                    else {
2736
+                        $recurr->createException($messageprops, $basedate, false, $recips);
2737
+                    }
2738
+                }
2739
+                else {
2740
+                    mapi_setprops($newResourceMsg, $messageprops);
2741
+
2742
+                    // Copy attachments
2743
+                    $this->replaceAttachments($message, $newResourceMsg);
2744
+
2745
+                    // Copy all recipients too
2746
+                    $this->replaceRecipients($message, $newResourceMsg);
2747
+
2748
+                    // Now add organizer also to recipient table
2749
+                    $recips = [];
2750
+                    $this->addOrganizer($messageprops, $recips);
2751
+
2752
+                    mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
2753
+                }
2754
+
2755
+                mapi_savechanges($newResourceMsg);
2756
+
2757
+                $resourceRecipData[] = [
2758
+                    'store' => $userStore,
2759
+                    'folder' => $calFolder,
2760
+                    'msg' => $newResourceMsg,
2761
+                ];
2762
+                $this->includesResources = true;
2763
+            }
2764
+            else {
2765
+                /*
2766 2766
 				 * If no other errors occurred and you have no access to the
2767 2767
 				 * folder of the resource, throw an error=1.
2768 2768
 				 */
2769
-				if (!$this->errorSetResource) {
2770
-					$this->errorSetResource = 1;
2771
-				}
2772
-
2773
-				for ($j = 0, $len = count($resourceRecipData); $j < $len; ++$j) {
2774
-					// Get the EntryID
2775
-					$props = mapi_message_getprops($resourceRecipData[$j]['msg']);
2776
-
2777
-					mapi_folder_deletemessages($resourceRecipData[$j]['folder'], [$props[PR_ENTRYID]], DELETE_HARD_DELETE);
2778
-				}
2779
-				$this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME];
2780
-			}
2781
-			++$i;
2782
-		}
2783
-
2784
-		/*
2769
+                if (!$this->errorSetResource) {
2770
+                    $this->errorSetResource = 1;
2771
+                }
2772
+
2773
+                for ($j = 0, $len = count($resourceRecipData); $j < $len; ++$j) {
2774
+                    // Get the EntryID
2775
+                    $props = mapi_message_getprops($resourceRecipData[$j]['msg']);
2776
+
2777
+                    mapi_folder_deletemessages($resourceRecipData[$j]['folder'], [$props[PR_ENTRYID]], DELETE_HARD_DELETE);
2778
+                }
2779
+                $this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME];
2780
+            }
2781
+            ++$i;
2782
+        }
2783
+
2784
+        /*
2785 2785
 		 * Set the BCC-recipients (resources) tackstatus to accepted.
2786 2786
 		 */
2787
-		// Get resource recipients
2788
-		$getResourcesRestriction = [RES_AND,
2789
-			[[RES_PROPERTY,
2790
-				[RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2791
-					ULPROPTAG => PR_RECIPIENT_TYPE,
2792
-					VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2793
-				],
2794
-			]],
2795
-		];
2796
-		$recipienttable = mapi_message_getrecipienttable($message);
2797
-		$resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
2798
-		if (!empty($resourceRecipients)) {
2799
-			// Set Tracking status of resource recipients to olResponseAccepted (3)
2800
-			for ($i = 0, $len = count($resourceRecipients); $i < $len; ++$i) {
2801
-				$resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted;
2802
-				$resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time();
2803
-			}
2804
-			mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients);
2805
-		}
2806
-
2807
-		return $resourceRecipData;
2808
-	}
2809
-
2810
-	/**
2811
-	 * Function which save an exception into recurring item.
2812
-	 *
2813
-	 * @param resource $recurringItem  reference to MAPI_message of recurring item
2814
-	 * @param resource $occurrenceItem reference to MAPI_message of occurrence
2815
-	 * @param string   $basedate       basedate of occurrence
2816
-	 * @param bool     $move           if true then occurrence item is deleted
2817
-	 * @param bool     $tentative      true if user has tentatively accepted it or false if user has accepted it
2818
-	 * @param bool     $userAction     true if user has manually responded to meeting request
2819
-	 * @param resource $store          user store
2820
-	 * @param bool     $isDelegate     true if delegate is processing this meeting request
2821
-	 */
2822
-	public function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move = false, $tentative, $userAction = false, $store, $isDelegate = false) {
2823
-		$recurr = new Recurrence($store, $recurringItem);
2824
-
2825
-		// Copy properties from meeting request
2826
-		$exception_props = mapi_getprops($occurrenceItem);
2827
-
2828
-		// Copy recipients list
2829
-		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
2830
-		// If delegate, then do not add the delegate in recipients
2831
-		if ($isDelegate) {
2832
-			$delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
2833
-			$res = [RES_PROPERTY, [
2834
-				RELOP => RELOP_NE,
2835
-				ULPROPTAG => PR_EMAIL_ADDRESS,
2836
-				VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2837
-			],
2838
-			];
2839
-			$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
2840
-		}
2841
-		else {
2842
-			$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2843
-		}
2844
-
2845
-		// add owner to recipient table
2846
-		$this->addOrganizer($exception_props, $recips, true);
2847
-
2848
-		// add delegator to meetings
2849
-		if ($isDelegate) {
2850
-			$this->addDelegator($exception_props, $recips);
2851
-		}
2852
-
2853
-		$exception_props[$this->proptags['meetingstatus']] = olMeetingReceived;
2854
-		$exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
2855
-
2856
-		if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
2857
-			if ($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
2858
-				$exception_props[$this->proptags['busystatus']] = fbTentative;
2859
-			}
2860
-			else {
2861
-				$exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
2862
-			}
2863
-			// we already have intendedbusystatus value in $exception_props so no need to copy it
2864
-		}
2865
-		else {
2866
-			$exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
2867
-		}
2868
-
2869
-		if ($userAction) {
2870
-			$addrInfo = $this->getOwnerAddress($this->store);
2871
-
2872
-			// if user has responded then set replytime and name
2873
-			$exception_props[$this->proptags['replytime']] = time();
2874
-			if (!empty($addrInfo)) {
2875
-				$exception_props[$this->proptags['apptreplyname']] = $addrInfo[0];
2876
-			}
2877
-		}
2878
-
2879
-		if ($recurr->isException($basedate)) {
2880
-			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2881
-		}
2882
-		else {
2883
-			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2884
-		}
2885
-
2886
-		// Move the occurrenceItem to the waste basket
2887
-		if ($move) {
2888
-			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2889
-			$sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
2890
-			mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
2891
-		}
2892
-
2893
-		mapi_savechanges($recurringItem);
2894
-	}
2895
-
2896
-	/**
2897
-	 * Function which merges an exception mapi message to recurring message.
2898
-	 * This will be used when we receive recurring meeting request and we already have an exception message
2899
-	 * of same meeting in calendar and we need to remove that exception message and add it to attachment table
2900
-	 * of recurring meeting.
2901
-	 *
2902
-	 * @param resource $recurringItem  reference to MAPI_message of recurring item
2903
-	 * @param resource $occurrenceItem reference to MAPI_message of occurrence
2904
-	 * @param string   $basedate       basedate of occurrence
2905
-	 * @param resource $store          user store
2906
-	 */
2907
-	public function mergeException(&$recurringItem, &$occurrenceItem, $basedate, $store) {
2908
-		$recurr = new Recurrence($store, $recurringItem);
2909
-
2910
-		// Copy properties from meeting request
2911
-		$exception_props = mapi_getprops($occurrenceItem);
2912
-
2913
-		// Get recipient list from message and add it to exception attachment
2914
-		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
2915
-		$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2916
-
2917
-		if ($recurr->isException($basedate)) {
2918
-			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2919
-		}
2920
-		else {
2921
-			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2922
-		}
2923
-
2924
-		// Move the occurrenceItem to the waste basket
2925
-		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2926
-		$sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
2927
-		mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
2928
-
2929
-		mapi_savechanges($recurringItem);
2930
-	}
2931
-
2932
-	/**
2933
-	 * Function which submits meeting request based on arguments passed to it.
2934
-	 *
2935
-	 * @param resource $message        MAPI_message whose meeting request is to be send
2936
-	 * @param bool     $cancel         if true send request, else send cancellation
2937
-	 * @param string   $prefix         subject prefix
2938
-	 * @param int      $basedate       basedate for an occurrence
2939
-	 * @param object   $recurObject    recurrence object of mr
2940
-	 * @param bool     $copyExceptions When sending update mail for recurring item then we dont send exceptions in attachments
2941
-	 * @param mixed    $modifiedRecips
2942
-	 * @param mixed    $deletedRecips
2943
-	 */
2944
-	public function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $modifiedRecips = false, $deletedRecips = false) {
2945
-		$newmessageprops = $messageprops = mapi_getprops($this->message);
2946
-		$new = $this->createOutgoingMessage();
2947
-
2948
-		// Copy the entire message into the new meeting request message
2949
-		if ($basedate) {
2950
-			// messageprops contains properties of whole recurring series
2951
-			// and newmessageprops contains properties of exception item
2952
-			$newmessageprops = mapi_getprops($message);
2953
-
2954
-			// Ensure that the correct basedate is set in the new message
2955
-			$newmessageprops[$this->proptags['basedate']] = $basedate;
2956
-
2957
-			// Set isRecurring to false, because this is an exception
2958
-			$newmessageprops[$this->proptags['recurring']] = false;
2959
-
2960
-			// set LID_IS_EXCEPTION to true
2961
-			$newmessageprops[$this->proptags['is_exception']] = true;
2962
-
2963
-			// Set to high importance
2964
-			if ($cancel) {
2965
-				$newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;
2966
-			}
2967
-
2968
-			// Set startdate and enddate of exception
2969
-			if ($cancel && $recurObject) {
2970
-				$newmessageprops[$this->proptags['startdate']] = $recurObject->getOccurrenceStart($basedate);
2971
-				$newmessageprops[$this->proptags['duedate']] = $recurObject->getOccurrenceEnd($basedate);
2972
-			}
2973
-
2974
-			// Set basedate in guid (0x3)
2975
-			$newmessageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2976
-			$newmessageprops[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2977
-			$newmessageprops[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2978
-
2979
-			// Get deleted recipiets from exception msg
2980
-			$restriction = [RES_AND,
2981
-				[
2982
-					[RES_BITMASK,
2983
-						[
2984
-							ULTYPE => BMR_NEZ,
2985
-							ULPROPTAG => PR_RECIPIENT_FLAGS,
2986
-							ULMASK => recipExceptionalDeleted,
2987
-						],
2988
-					],
2989
-					[RES_BITMASK,
2990
-						[
2991
-							ULTYPE => BMR_EQZ,
2992
-							ULPROPTAG => PR_RECIPIENT_FLAGS,
2993
-							ULMASK => recipOrganizer,
2994
-						],
2995
-					],
2996
-				],
2997
-			];
2998
-
2999
-			// In direct-booking mode, we don't need to send cancellations to resources
3000
-			if ($this->enableDirectBooking) {
3001
-				$restriction[1][] = [RES_PROPERTY,
3002
-					[RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3003
-						ULPROPTAG => PR_RECIPIENT_TYPE,
3004
-						VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3005
-					],
3006
-				];
3007
-			}
3008
-
3009
-			$recipienttable = mapi_message_getrecipienttable($message);
3010
-			$recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
3011
-
3012
-			if (!$deletedRecips) {
3013
-				$deletedRecips = array_merge([], $recipients);
3014
-			}
3015
-			else {
3016
-				$deletedRecips = array_merge($deletedRecips, $recipients);
3017
-			}
3018
-		}
3019
-
3020
-		// Remove the PR_ICON_INDEX as it is not needed in the sent message.
3021
-		$newmessageprops[PR_ICON_INDEX] = null;
3022
-		$newmessageprops[PR_RESPONSE_REQUESTED] = true;
3023
-
3024
-		// PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3025
-		$newmessageprops[PR_START_DATE] = $newmessageprops[$this->proptags['startdate']];
3026
-		$newmessageprops[PR_END_DATE] = $newmessageprops[$this->proptags['duedate']];
3027
-
3028
-		// Set updatecounter/AppointmentSequenceNumber
3029
-		// get the value of latest updatecounter for the whole series and use it
3030
-		$newmessageprops[$this->proptags['updatecounter']] = $messageprops[$this->proptags['last_updatecounter']];
3031
-
3032
-		$meetingTimeInfo = $this->getMeetingTimeInfo();
3033
-
3034
-		if ($meetingTimeInfo) {
3035
-			// Needs to unset PR_HTML and PR_RTF_COMPRESSED props
3036
-			// because while canceling meeting requests with edit text
3037
-			// will override the PR_BODY because body value is not consistent with
3038
-			// PR_HTML and PR_RTF_COMPRESSED value so in this case PR_RTF_COMPRESSED will
3039
-			// get priority which override the PR_BODY value.
3040
-			unset($newmessageprops[PR_HTML], $newmessageprops[PR_RTF_COMPRESSED]);
3041
-
3042
-			$newmessageprops[PR_BODY] = $meetingTimeInfo;
3043
-		}
3044
-
3045
-		// Send all recurrence info in mail, if this is a recurrence meeting.
3046
-		if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]) {
3047
-			if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
3048
-				$newmessageprops[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
3049
-			}
3050
-			$newmessageprops[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
3051
-			$newmessageprops[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
3052
-			$newmessageprops[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
3053
-
3054
-			if ($recurObject) {
3055
-				$this->generateRecurDates($recurObject, $messageprops, $newmessageprops);
3056
-			}
3057
-		}
3058
-
3059
-		if (isset($newmessageprops[$this->proptags['counter_proposal']])) {
3060
-			unset($newmessageprops[$this->proptags['counter_proposal']]);
3061
-		}
3062
-
3063
-		// Prefix the subject if needed
3064
-		if ($prefix && isset($newmessageprops[PR_SUBJECT])) {
3065
-			$newmessageprops[PR_SUBJECT] = $prefix . $newmessageprops[PR_SUBJECT];
3066
-		}
3067
-
3068
-		if (isset($newmessageprops[$this->proptags['categories']]) &&
3069
-			!empty($newmessageprops[$this->proptags['categories']])) {
3070
-			unset($newmessageprops[$this->proptags['categories']]);
3071
-		}
3072
-		mapi_setprops($new, $newmessageprops);
3073
-
3074
-		// Copy attachments
3075
-		$this->replaceAttachments($message, $new, $copyExceptions);
3076
-
3077
-		// Retrieve only those recipient who should receive this meeting request.
3078
-		$stripResourcesRestriction = [RES_AND,
3079
-			[
3080
-				[RES_BITMASK,
3081
-					[ULTYPE => BMR_EQZ,
3082
-						ULPROPTAG => PR_RECIPIENT_FLAGS,
3083
-						ULMASK => recipExceptionalDeleted,
3084
-					],
3085
-				],
3086
-				[RES_BITMASK,
3087
-					[ULTYPE => BMR_EQZ,
3088
-						ULPROPTAG => PR_RECIPIENT_FLAGS,
3089
-						ULMASK => recipOrganizer,
3090
-					],
3091
-				],
3092
-			],
3093
-		];
3094
-
3095
-		// In direct-booking mode, resources do not receive a meeting request
3096
-		if ($this->enableDirectBooking) {
3097
-			$stripResourcesRestriction[1][] =
3098
-									[RES_PROPERTY,
3099
-										[RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3100
-											ULPROPTAG => PR_RECIPIENT_TYPE,
3101
-											VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3102
-										],
3103
-									];
3104
-		}
3105
-
3106
-		// If no recipients were explicitly provided, we will send the update to all
3107
-		// recipients from the meeting.
3108
-		if ($modifiedRecips === false) {
3109
-			$recipienttable = mapi_message_getrecipienttable($message);
3110
-			$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3111
-
3112
-			if ($basedate && empty($modifiedRecips)) {
3113
-				// Retrieve full list
3114
-				$recipienttable = mapi_message_getrecipienttable($this->message);
3115
-				$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops);
3116
-
3117
-				// Save recipients in exceptions
3118
-				mapi_message_modifyrecipients($message, MODRECIP_ADD, $modifiedRecips);
3119
-
3120
-				// Now retrieve only those recipient who should receive this meeting request.
3121
-				$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3122
-			}
3123
-		}
3124
-
3125
-		// @TODO: handle nonAcceptingResources
3126
-		/*
2787
+        // Get resource recipients
2788
+        $getResourcesRestriction = [RES_AND,
2789
+            [[RES_PROPERTY,
2790
+                [RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2791
+                    ULPROPTAG => PR_RECIPIENT_TYPE,
2792
+                    VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2793
+                ],
2794
+            ]],
2795
+        ];
2796
+        $recipienttable = mapi_message_getrecipienttable($message);
2797
+        $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
2798
+        if (!empty($resourceRecipients)) {
2799
+            // Set Tracking status of resource recipients to olResponseAccepted (3)
2800
+            for ($i = 0, $len = count($resourceRecipients); $i < $len; ++$i) {
2801
+                $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted;
2802
+                $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time();
2803
+            }
2804
+            mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients);
2805
+        }
2806
+
2807
+        return $resourceRecipData;
2808
+    }
2809
+
2810
+    /**
2811
+     * Function which save an exception into recurring item.
2812
+     *
2813
+     * @param resource $recurringItem  reference to MAPI_message of recurring item
2814
+     * @param resource $occurrenceItem reference to MAPI_message of occurrence
2815
+     * @param string   $basedate       basedate of occurrence
2816
+     * @param bool     $move           if true then occurrence item is deleted
2817
+     * @param bool     $tentative      true if user has tentatively accepted it or false if user has accepted it
2818
+     * @param bool     $userAction     true if user has manually responded to meeting request
2819
+     * @param resource $store          user store
2820
+     * @param bool     $isDelegate     true if delegate is processing this meeting request
2821
+     */
2822
+    public function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move = false, $tentative, $userAction = false, $store, $isDelegate = false) {
2823
+        $recurr = new Recurrence($store, $recurringItem);
2824
+
2825
+        // Copy properties from meeting request
2826
+        $exception_props = mapi_getprops($occurrenceItem);
2827
+
2828
+        // Copy recipients list
2829
+        $reciptable = mapi_message_getrecipienttable($occurrenceItem);
2830
+        // If delegate, then do not add the delegate in recipients
2831
+        if ($isDelegate) {
2832
+            $delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
2833
+            $res = [RES_PROPERTY, [
2834
+                RELOP => RELOP_NE,
2835
+                ULPROPTAG => PR_EMAIL_ADDRESS,
2836
+                VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2837
+            ],
2838
+            ];
2839
+            $recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
2840
+        }
2841
+        else {
2842
+            $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2843
+        }
2844
+
2845
+        // add owner to recipient table
2846
+        $this->addOrganizer($exception_props, $recips, true);
2847
+
2848
+        // add delegator to meetings
2849
+        if ($isDelegate) {
2850
+            $this->addDelegator($exception_props, $recips);
2851
+        }
2852
+
2853
+        $exception_props[$this->proptags['meetingstatus']] = olMeetingReceived;
2854
+        $exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
2855
+
2856
+        if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
2857
+            if ($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
2858
+                $exception_props[$this->proptags['busystatus']] = fbTentative;
2859
+            }
2860
+            else {
2861
+                $exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
2862
+            }
2863
+            // we already have intendedbusystatus value in $exception_props so no need to copy it
2864
+        }
2865
+        else {
2866
+            $exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
2867
+        }
2868
+
2869
+        if ($userAction) {
2870
+            $addrInfo = $this->getOwnerAddress($this->store);
2871
+
2872
+            // if user has responded then set replytime and name
2873
+            $exception_props[$this->proptags['replytime']] = time();
2874
+            if (!empty($addrInfo)) {
2875
+                $exception_props[$this->proptags['apptreplyname']] = $addrInfo[0];
2876
+            }
2877
+        }
2878
+
2879
+        if ($recurr->isException($basedate)) {
2880
+            $recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2881
+        }
2882
+        else {
2883
+            $recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2884
+        }
2885
+
2886
+        // Move the occurrenceItem to the waste basket
2887
+        if ($move) {
2888
+            $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2889
+            $sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
2890
+            mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
2891
+        }
2892
+
2893
+        mapi_savechanges($recurringItem);
2894
+    }
2895
+
2896
+    /**
2897
+     * Function which merges an exception mapi message to recurring message.
2898
+     * This will be used when we receive recurring meeting request and we already have an exception message
2899
+     * of same meeting in calendar and we need to remove that exception message and add it to attachment table
2900
+     * of recurring meeting.
2901
+     *
2902
+     * @param resource $recurringItem  reference to MAPI_message of recurring item
2903
+     * @param resource $occurrenceItem reference to MAPI_message of occurrence
2904
+     * @param string   $basedate       basedate of occurrence
2905
+     * @param resource $store          user store
2906
+     */
2907
+    public function mergeException(&$recurringItem, &$occurrenceItem, $basedate, $store) {
2908
+        $recurr = new Recurrence($store, $recurringItem);
2909
+
2910
+        // Copy properties from meeting request
2911
+        $exception_props = mapi_getprops($occurrenceItem);
2912
+
2913
+        // Get recipient list from message and add it to exception attachment
2914
+        $reciptable = mapi_message_getrecipienttable($occurrenceItem);
2915
+        $recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2916
+
2917
+        if ($recurr->isException($basedate)) {
2918
+            $recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2919
+        }
2920
+        else {
2921
+            $recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2922
+        }
2923
+
2924
+        // Move the occurrenceItem to the waste basket
2925
+        $wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2926
+        $sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
2927
+        mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
2928
+
2929
+        mapi_savechanges($recurringItem);
2930
+    }
2931
+
2932
+    /**
2933
+     * Function which submits meeting request based on arguments passed to it.
2934
+     *
2935
+     * @param resource $message        MAPI_message whose meeting request is to be send
2936
+     * @param bool     $cancel         if true send request, else send cancellation
2937
+     * @param string   $prefix         subject prefix
2938
+     * @param int      $basedate       basedate for an occurrence
2939
+     * @param object   $recurObject    recurrence object of mr
2940
+     * @param bool     $copyExceptions When sending update mail for recurring item then we dont send exceptions in attachments
2941
+     * @param mixed    $modifiedRecips
2942
+     * @param mixed    $deletedRecips
2943
+     */
2944
+    public function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $modifiedRecips = false, $deletedRecips = false) {
2945
+        $newmessageprops = $messageprops = mapi_getprops($this->message);
2946
+        $new = $this->createOutgoingMessage();
2947
+
2948
+        // Copy the entire message into the new meeting request message
2949
+        if ($basedate) {
2950
+            // messageprops contains properties of whole recurring series
2951
+            // and newmessageprops contains properties of exception item
2952
+            $newmessageprops = mapi_getprops($message);
2953
+
2954
+            // Ensure that the correct basedate is set in the new message
2955
+            $newmessageprops[$this->proptags['basedate']] = $basedate;
2956
+
2957
+            // Set isRecurring to false, because this is an exception
2958
+            $newmessageprops[$this->proptags['recurring']] = false;
2959
+
2960
+            // set LID_IS_EXCEPTION to true
2961
+            $newmessageprops[$this->proptags['is_exception']] = true;
2962
+
2963
+            // Set to high importance
2964
+            if ($cancel) {
2965
+                $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;
2966
+            }
2967
+
2968
+            // Set startdate and enddate of exception
2969
+            if ($cancel && $recurObject) {
2970
+                $newmessageprops[$this->proptags['startdate']] = $recurObject->getOccurrenceStart($basedate);
2971
+                $newmessageprops[$this->proptags['duedate']] = $recurObject->getOccurrenceEnd($basedate);
2972
+            }
2973
+
2974
+            // Set basedate in guid (0x3)
2975
+            $newmessageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2976
+            $newmessageprops[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2977
+            $newmessageprops[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2978
+
2979
+            // Get deleted recipiets from exception msg
2980
+            $restriction = [RES_AND,
2981
+                [
2982
+                    [RES_BITMASK,
2983
+                        [
2984
+                            ULTYPE => BMR_NEZ,
2985
+                            ULPROPTAG => PR_RECIPIENT_FLAGS,
2986
+                            ULMASK => recipExceptionalDeleted,
2987
+                        ],
2988
+                    ],
2989
+                    [RES_BITMASK,
2990
+                        [
2991
+                            ULTYPE => BMR_EQZ,
2992
+                            ULPROPTAG => PR_RECIPIENT_FLAGS,
2993
+                            ULMASK => recipOrganizer,
2994
+                        ],
2995
+                    ],
2996
+                ],
2997
+            ];
2998
+
2999
+            // In direct-booking mode, we don't need to send cancellations to resources
3000
+            if ($this->enableDirectBooking) {
3001
+                $restriction[1][] = [RES_PROPERTY,
3002
+                    [RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3003
+                        ULPROPTAG => PR_RECIPIENT_TYPE,
3004
+                        VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3005
+                    ],
3006
+                ];
3007
+            }
3008
+
3009
+            $recipienttable = mapi_message_getrecipienttable($message);
3010
+            $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
3011
+
3012
+            if (!$deletedRecips) {
3013
+                $deletedRecips = array_merge([], $recipients);
3014
+            }
3015
+            else {
3016
+                $deletedRecips = array_merge($deletedRecips, $recipients);
3017
+            }
3018
+        }
3019
+
3020
+        // Remove the PR_ICON_INDEX as it is not needed in the sent message.
3021
+        $newmessageprops[PR_ICON_INDEX] = null;
3022
+        $newmessageprops[PR_RESPONSE_REQUESTED] = true;
3023
+
3024
+        // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3025
+        $newmessageprops[PR_START_DATE] = $newmessageprops[$this->proptags['startdate']];
3026
+        $newmessageprops[PR_END_DATE] = $newmessageprops[$this->proptags['duedate']];
3027
+
3028
+        // Set updatecounter/AppointmentSequenceNumber
3029
+        // get the value of latest updatecounter for the whole series and use it
3030
+        $newmessageprops[$this->proptags['updatecounter']] = $messageprops[$this->proptags['last_updatecounter']];
3031
+
3032
+        $meetingTimeInfo = $this->getMeetingTimeInfo();
3033
+
3034
+        if ($meetingTimeInfo) {
3035
+            // Needs to unset PR_HTML and PR_RTF_COMPRESSED props
3036
+            // because while canceling meeting requests with edit text
3037
+            // will override the PR_BODY because body value is not consistent with
3038
+            // PR_HTML and PR_RTF_COMPRESSED value so in this case PR_RTF_COMPRESSED will
3039
+            // get priority which override the PR_BODY value.
3040
+            unset($newmessageprops[PR_HTML], $newmessageprops[PR_RTF_COMPRESSED]);
3041
+
3042
+            $newmessageprops[PR_BODY] = $meetingTimeInfo;
3043
+        }
3044
+
3045
+        // Send all recurrence info in mail, if this is a recurrence meeting.
3046
+        if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]) {
3047
+            if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
3048
+                $newmessageprops[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
3049
+            }
3050
+            $newmessageprops[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
3051
+            $newmessageprops[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
3052
+            $newmessageprops[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
3053
+
3054
+            if ($recurObject) {
3055
+                $this->generateRecurDates($recurObject, $messageprops, $newmessageprops);
3056
+            }
3057
+        }
3058
+
3059
+        if (isset($newmessageprops[$this->proptags['counter_proposal']])) {
3060
+            unset($newmessageprops[$this->proptags['counter_proposal']]);
3061
+        }
3062
+
3063
+        // Prefix the subject if needed
3064
+        if ($prefix && isset($newmessageprops[PR_SUBJECT])) {
3065
+            $newmessageprops[PR_SUBJECT] = $prefix . $newmessageprops[PR_SUBJECT];
3066
+        }
3067
+
3068
+        if (isset($newmessageprops[$this->proptags['categories']]) &&
3069
+            !empty($newmessageprops[$this->proptags['categories']])) {
3070
+            unset($newmessageprops[$this->proptags['categories']]);
3071
+        }
3072
+        mapi_setprops($new, $newmessageprops);
3073
+
3074
+        // Copy attachments
3075
+        $this->replaceAttachments($message, $new, $copyExceptions);
3076
+
3077
+        // Retrieve only those recipient who should receive this meeting request.
3078
+        $stripResourcesRestriction = [RES_AND,
3079
+            [
3080
+                [RES_BITMASK,
3081
+                    [ULTYPE => BMR_EQZ,
3082
+                        ULPROPTAG => PR_RECIPIENT_FLAGS,
3083
+                        ULMASK => recipExceptionalDeleted,
3084
+                    ],
3085
+                ],
3086
+                [RES_BITMASK,
3087
+                    [ULTYPE => BMR_EQZ,
3088
+                        ULPROPTAG => PR_RECIPIENT_FLAGS,
3089
+                        ULMASK => recipOrganizer,
3090
+                    ],
3091
+                ],
3092
+            ],
3093
+        ];
3094
+
3095
+        // In direct-booking mode, resources do not receive a meeting request
3096
+        if ($this->enableDirectBooking) {
3097
+            $stripResourcesRestriction[1][] =
3098
+                                    [RES_PROPERTY,
3099
+                                        [RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3100
+                                            ULPROPTAG => PR_RECIPIENT_TYPE,
3101
+                                            VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3102
+                                        ],
3103
+                                    ];
3104
+        }
3105
+
3106
+        // If no recipients were explicitly provided, we will send the update to all
3107
+        // recipients from the meeting.
3108
+        if ($modifiedRecips === false) {
3109
+            $recipienttable = mapi_message_getrecipienttable($message);
3110
+            $modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3111
+
3112
+            if ($basedate && empty($modifiedRecips)) {
3113
+                // Retrieve full list
3114
+                $recipienttable = mapi_message_getrecipienttable($this->message);
3115
+                $modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops);
3116
+
3117
+                // Save recipients in exceptions
3118
+                mapi_message_modifyrecipients($message, MODRECIP_ADD, $modifiedRecips);
3119
+
3120
+                // Now retrieve only those recipient who should receive this meeting request.
3121
+                $modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3122
+            }
3123
+        }
3124
+
3125
+        // @TODO: handle nonAcceptingResources
3126
+        /*
3127 3127
 		 * Add resource recipients that did not automatically accept the meeting request.
3128 3128
 		 * (note: meaning that they did not decline the meeting request)
3129 3129
 		 */ /*
@@ -3131,798 +3131,798 @@  discard block
 block discarded – undo
3131 3131
 			$recipients[] = $this->nonAcceptingResources[$i];
3132 3132
 		}*/
3133 3133
 
3134
-		if (!empty($modifiedRecips)) {
3135
-			// Strip out the sender/'owner' recipient
3136
-			mapi_message_modifyrecipients($new, MODRECIP_ADD, $modifiedRecips);
3137
-
3138
-			// Set some properties that are different in the sent request than
3139
-			// in the item in our calendar
3140
-
3141
-			// we should store busystatus value to intendedbusystatus property, because busystatus for outgoing meeting request
3142
-			// should always be fbTentative
3143
-			$newmessageprops[$this->proptags['intendedbusystatus']] = isset($newmessageprops[$this->proptags['busystatus']]) ? $newmessageprops[$this->proptags['busystatus']] : $messageprops[$this->proptags['busystatus']];
3144
-			$newmessageprops[$this->proptags['busystatus']] = fbTentative; // The default status when not accepted
3145
-			$newmessageprops[$this->proptags['responsestatus']] = olResponseNotResponded; // The recipient has not responded yet
3146
-			$newmessageprops[$this->proptags['attendee_critical_change']] = time();
3147
-			$newmessageprops[$this->proptags['owner_critical_change']] = time();
3148
-			$newmessageprops[$this->proptags['meetingtype']] = mtgRequest;
3149
-
3150
-			if ($cancel) {
3151
-				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3152
-				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3153
-				$newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3154
-			}
3155
-			else {
3156
-				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Request';
3157
-				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
3158
-			}
3159
-
3160
-			mapi_setprops($new, $newmessageprops);
3161
-			mapi_savechanges($new);
3162
-
3163
-			// Submit message to non-resource recipients
3164
-			mapi_message_submitmessage($new);
3165
-		}
3166
-
3167
-		// Search through the deleted recipients, and see if any of them is also
3168
-		// listed as a recipient to whom we have send an update. As we don't
3169
-		// want to send a cancellation message to recipients who will also receive
3170
-		// an meeting update, we have to filter those recipients out.
3171
-		if ($deletedRecips) {
3172
-			$tmp = [];
3173
-
3174
-			foreach ($deletedRecips as $delRecip) {
3175
-				$found = false;
3176
-
3177
-				// Search if the deleted recipient can be found inside
3178
-				// the updated recipients as well.
3179
-				foreach ($modifiedRecips as $recip) {
3180
-					if ($this->compareABEntryIDs($recip[PR_ENTRYID], $delRecip[PR_ENTRYID])) {
3181
-						$found = true;
3182
-						break;
3183
-					}
3184
-				}
3185
-
3186
-				// If the recipient was not found, it truly is deleted,
3187
-				// and we can safely send a cancellation message
3188
-				if (!$found) {
3189
-					$tmp[] = $delRecip;
3190
-				}
3191
-			}
3192
-
3193
-			$deletedRecips = $tmp;
3194
-		}
3195
-
3196
-		// Send cancellation to deleted attendees
3197
-		if ($deletedRecips && !empty($deletedRecips)) {
3198
-			$new = $this->createOutgoingMessage();
3199
-
3200
-			mapi_message_modifyrecipients($new, MODRECIP_ADD, $deletedRecips);
3201
-
3202
-			$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3203
-			$newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3204
-			$newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3205
-			$newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;	// HIGH Importance
3206
-			if (isset($newmessageprops[PR_SUBJECT])) {
3207
-				$newmessageprops[PR_SUBJECT] = dgettext('zarafa', 'Canceled') . ': ' . $newmessageprops[PR_SUBJECT];
3208
-			}
3209
-
3210
-			mapi_setprops($new, $newmessageprops);
3211
-			mapi_savechanges($new);
3212
-
3213
-			// Submit message to non-resource recipients
3214
-			mapi_message_submitmessage($new);
3215
-		}
3216
-
3217
-		// Set properties on meeting object in calendar
3218
-		// Set requestsent to 'true' (turns on 'tracking', etc)
3219
-		$props = [];
3220
-		$props[$this->proptags['meetingstatus']] = olMeeting;
3221
-		$props[$this->proptags['responsestatus']] = olResponseOrganized;
3222
-		// Only set the 'requestsent' property if it wasn't set previously yet,
3223
-		// this ensures we will not accidentally set it from true to false.
3224
-		if (!isset($messageprops[$this->proptags['requestsent']]) || $messageprops[$this->proptags['requestsent']] !== true) {
3225
-			$props[$this->proptags['requestsent']] = !empty($modifiedRecips) || ($this->includesResources && !$this->errorSetResource);
3226
-		}
3227
-		$props[$this->proptags['attendee_critical_change']] = time();
3228
-		$props[$this->proptags['owner_critical_change']] = time();
3229
-		$props[$this->proptags['meetingtype']] = mtgRequest;
3230
-		// save the new updatecounter to exception/recurring series/normal meeting
3231
-		$props[$this->proptags['updatecounter']] = $newmessageprops[$this->proptags['updatecounter']];
3232
-
3233
-		// PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3234
-		$props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
3235
-		$props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
3236
-
3237
-		mapi_setprops($message, $props);
3238
-
3239
-		// saving of these properties on calendar item should be handled by caller function
3240
-		// based on sending meeting request was successful or not
3241
-	}
3242
-
3243
-	/**
3244
-	 * OL2007 uses these 4 properties to specify occurrence that should be updated.
3245
-	 * ical generates RECURRENCE-ID property based on exception's basedate (PidLidExceptionReplaceTime),
3246
-	 * but OL07 doesn't send this property, so ical will generate RECURRENCE-ID property based on date
3247
-	 * from GlobalObjId and time from StartRecurTime property, so we are sending basedate property and
3248
-	 * also additionally we are sending these properties.
3249
-	 * Ref: MS-OXCICAL 2.2.1.20.20 Property: RECURRENCE-ID.
3250
-	 *
3251
-	 * @param object $recurObject     instance of recurrence class for this message
3252
-	 * @param array  $messageprops    properties of meeting object that is going to be send
3253
-	 * @param array  $newmessageprops properties of meeting request/response that is going to be send
3254
-	 */
3255
-	public function generateRecurDates($recurObject, $messageprops, &$newmessageprops) {
3256
-		if ($messageprops[$this->proptags['startdate']] && $messageprops[$this->proptags['duedate']]) {
3257
-			$startDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['startdate']]));
3258
-			$endDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['duedate']]));
3259
-
3260
-			$startDate = explode(':', $startDate);
3261
-			$endDate = explode(':', $endDate);
3262
-
3263
-			// [0] => year, [1] => month, [2] => day, [3] => hour, [4] => minutes, [5] => seconds
3264
-			// RecurStartDate = year * 512 + month_number * 32 + day_number
3265
-			$newmessageprops[$this->proptags['start_recur_date']] = (((int) $startDate[0]) * 512) + (((int) $startDate[1]) * 32) + ((int) $startDate[2]);
3266
-			// RecurStartTime = hour * 4096 + minutes * 64 + seconds
3267
-			$newmessageprops[$this->proptags['start_recur_time']] = (((int) $startDate[3]) * 4096) + (((int) $startDate[4]) * 64) + ((int) $startDate[5]);
3268
-
3269
-			$newmessageprops[$this->proptags['end_recur_date']] = (((int) $endDate[0]) * 512) + (((int) $endDate[1]) * 32) + ((int) $endDate[2]);
3270
-			$newmessageprops[$this->proptags['end_recur_time']] = (((int) $endDate[3]) * 4096) + (((int) $endDate[4]) * 64) + ((int) $endDate[5]);
3271
-		}
3272
-	}
3273
-
3274
-	/**
3275
-	 * Function will create a new outgoing message that will be used to send meeting mail.
3276
-	 *
3277
-	 * @param MAPIStore $store (optional) store that is used when creating response, if delegate is creating outgoing mail
3278
-	 *                         then this would point to delegate store
3279
-	 *
3280
-	 * @return MAPIMessage outgoing mail that is created and can be used for sending it
3281
-	 */
3282
-	public function createOutgoingMessage($store = false) {
3283
-		// get logged in user's store that will be used to send mail, for delegate this will be
3284
-		// delegate store
3285
-		$userStore = $this->openDefaultStore();
3286
-
3287
-		$sentprops = [];
3288
-		$outbox = $this->openDefaultOutbox($userStore);
3289
-
3290
-		$outgoing = mapi_folder_createmessage($outbox);
3291
-
3292
-		// check if $store is set and it is not equal to $defaultStore (means its the delegation case)
3293
-		if ($store !== false) {
3294
-			$storeProps = mapi_getprops($store, [PR_ENTRYID]);
3295
-			$userStoreProps = mapi_getprops($userStore, [PR_ENTRYID]);
3296
-
3297
-			// @FIXME use entryid comparison functions here
3298
-			if ($storeProps[PR_ENTRYID] !== $userStoreProps[PR_ENTRYID]) {
3299
-				// get the delegator properties and set it into outgoing mail
3300
-				$delegatorDetails = $this->getOwnerAddress($store, false);
3301
-
3302
-				if ($delegatorDetails) {
3303
-					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegatorDetails;
3304
-					$sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3305
-					$sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3306
-					$sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3307
-					$sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3308
-					$sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3309
-				}
3310
-
3311
-				// get the delegate properties and set it into outgoing mail
3312
-				$delegateDetails = $this->getOwnerAddress($userStore, false);
3313
-
3314
-				if ($delegateDetails) {
3315
-					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegateDetails;
3316
-					$sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3317
-					$sentprops[PR_SENDER_NAME] = $ownername;
3318
-					$sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3319
-					$sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3320
-					$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3321
-				}
3322
-			}
3323
-		}
3324
-		else {
3325
-			// normal user is sending mail, so both set of properties will be same
3326
-			$userDetails = $this->getOwnerAddress($userStore);
3327
-
3328
-			if ($userDetails) {
3329
-				list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $userDetails;
3330
-				$sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3331
-				$sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3332
-				$sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3333
-				$sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3334
-				$sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3335
-
3336
-				$sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3337
-				$sentprops[PR_SENDER_NAME] = $ownername;
3338
-				$sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3339
-				$sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3340
-				$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3341
-			}
3342
-		}
3343
-
3344
-		$sentprops[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($userStore);
3345
-
3346
-		mapi_setprops($outgoing, $sentprops);
3347
-
3348
-		return $outgoing;
3349
-	}
3350
-
3351
-	/**
3352
-	 * Function which checks that meeting in attendee's calendar is already updated
3353
-	 * and we are checking an old meeting request. This function also will update property
3354
-	 * meetingtype to indicate that its out of date meeting request.
3355
-	 *
3356
-	 * @return bool true if meeting request is outofdate else false if it is new
3357
-	 */
3358
-	public function isMeetingOutOfDate() {
3359
-		$result = false;
3360
-
3361
-		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['meetingtype'], $this->proptags['owner_critical_change']]);
3362
-
3363
-		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS])) {
3364
-			return $result;
3365
-		}
3366
-
3367
-		if (isset($props[$this->proptags['meetingtype']]) && ($props[$this->proptags['meetingtype']] & mtgOutOfDate) == mtgOutOfDate) {
3368
-			return true;
3369
-		}
3370
-
3371
-		// get the basedate to check for exception
3372
-		$basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]);
3373
-
3374
-		$calendarItem = $this->getCorrespondentCalendarItem(true);
3375
-
3376
-		// if basedate is provided and we could not find the item then it could be that we are checking
3377
-		// an exception so get the exception and check it
3378
-		if ($basedate && $calendarItem !== false) {
3379
-			$exception = $this->getExceptionItem($calendarItem, $basedate);
3380
-
3381
-			if ($exception !== false) {
3382
-				// we are able to find the exception compare with it
3383
-				$calendarItem = $exception;
3384
-			}
3385
-			// we are not able to find exception, could mean that a significant change has occurred on series
3386
-				// and it deleted all exceptions, so compare with series
3387
-				// $calendarItem already contains reference to series
3388
-		}
3389
-
3390
-		if ($calendarItem !== false) {
3391
-			$calendarItemProps = mapi_getprops($calendarItem, [
3392
-				$this->proptags['owner_critical_change'],
3393
-				$this->proptags['updatecounter'],
3394
-			]);
3395
-
3396
-			$updateCounter = (isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]);
3397
-
3398
-			$criticalChange = (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']]);
3399
-
3400
-			if ($updateCounter || $criticalChange) {
3401
-				// meeting request is out of date, set properties to indicate this
3402
-				mapi_setprops($this->message, [$this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033]);
3403
-				mapi_savechanges($this->message);
3404
-
3405
-				$result = true;
3406
-			}
3407
-		}
3408
-
3409
-		return $result;
3410
-	}
3411
-
3412
-	/**
3413
-	 * Function which checks that if we have received a meeting response for an updated meeting in organizer's calendar.
3414
-	 *
3415
-	 * @param Number $basedate basedate of the exception if we want to compare with exception
3416
-	 *
3417
-	 * @return bool true if meeting request is updated later
3418
-	 */
3419
-	public function isMeetingUpdated($basedate = false) {
3420
-		$result = false;
3421
-
3422
-		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['updatecounter']]);
3423
-
3424
-		if (!$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS])) {
3425
-			return $result;
3426
-		}
3427
-
3428
-		$calendarItem = $this->getCorrespondentCalendarItem(true);
3429
-
3430
-		if ($calendarItem !== false) {
3431
-			// basedate is provided so open exception
3432
-			if ($basedate !== false) {
3433
-				$exception = $this->getExceptionItem($calendarItem, $basedate);
3434
-
3435
-				if ($exception !== false) {
3436
-					// we are able to find the exception compare with it
3437
-					$calendarItem = $exception;
3438
-				}
3439
-				// we are not able to find exception, could mean that a significant change has occurred on series
3440
-					// and it deleted all exceptions, so compare with series
3441
-					// $calendarItem already contains reference to series
3442
-			}
3443
-
3444
-			if ($calendarItem !== false) {
3445
-				$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['updatecounter']]);
3446
-
3447
-				/*
3134
+        if (!empty($modifiedRecips)) {
3135
+            // Strip out the sender/'owner' recipient
3136
+            mapi_message_modifyrecipients($new, MODRECIP_ADD, $modifiedRecips);
3137
+
3138
+            // Set some properties that are different in the sent request than
3139
+            // in the item in our calendar
3140
+
3141
+            // we should store busystatus value to intendedbusystatus property, because busystatus for outgoing meeting request
3142
+            // should always be fbTentative
3143
+            $newmessageprops[$this->proptags['intendedbusystatus']] = isset($newmessageprops[$this->proptags['busystatus']]) ? $newmessageprops[$this->proptags['busystatus']] : $messageprops[$this->proptags['busystatus']];
3144
+            $newmessageprops[$this->proptags['busystatus']] = fbTentative; // The default status when not accepted
3145
+            $newmessageprops[$this->proptags['responsestatus']] = olResponseNotResponded; // The recipient has not responded yet
3146
+            $newmessageprops[$this->proptags['attendee_critical_change']] = time();
3147
+            $newmessageprops[$this->proptags['owner_critical_change']] = time();
3148
+            $newmessageprops[$this->proptags['meetingtype']] = mtgRequest;
3149
+
3150
+            if ($cancel) {
3151
+                $newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3152
+                $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3153
+                $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3154
+            }
3155
+            else {
3156
+                $newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Request';
3157
+                $newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
3158
+            }
3159
+
3160
+            mapi_setprops($new, $newmessageprops);
3161
+            mapi_savechanges($new);
3162
+
3163
+            // Submit message to non-resource recipients
3164
+            mapi_message_submitmessage($new);
3165
+        }
3166
+
3167
+        // Search through the deleted recipients, and see if any of them is also
3168
+        // listed as a recipient to whom we have send an update. As we don't
3169
+        // want to send a cancellation message to recipients who will also receive
3170
+        // an meeting update, we have to filter those recipients out.
3171
+        if ($deletedRecips) {
3172
+            $tmp = [];
3173
+
3174
+            foreach ($deletedRecips as $delRecip) {
3175
+                $found = false;
3176
+
3177
+                // Search if the deleted recipient can be found inside
3178
+                // the updated recipients as well.
3179
+                foreach ($modifiedRecips as $recip) {
3180
+                    if ($this->compareABEntryIDs($recip[PR_ENTRYID], $delRecip[PR_ENTRYID])) {
3181
+                        $found = true;
3182
+                        break;
3183
+                    }
3184
+                }
3185
+
3186
+                // If the recipient was not found, it truly is deleted,
3187
+                // and we can safely send a cancellation message
3188
+                if (!$found) {
3189
+                    $tmp[] = $delRecip;
3190
+                }
3191
+            }
3192
+
3193
+            $deletedRecips = $tmp;
3194
+        }
3195
+
3196
+        // Send cancellation to deleted attendees
3197
+        if ($deletedRecips && !empty($deletedRecips)) {
3198
+            $new = $this->createOutgoingMessage();
3199
+
3200
+            mapi_message_modifyrecipients($new, MODRECIP_ADD, $deletedRecips);
3201
+
3202
+            $newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3203
+            $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3204
+            $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3205
+            $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;	// HIGH Importance
3206
+            if (isset($newmessageprops[PR_SUBJECT])) {
3207
+                $newmessageprops[PR_SUBJECT] = dgettext('zarafa', 'Canceled') . ': ' . $newmessageprops[PR_SUBJECT];
3208
+            }
3209
+
3210
+            mapi_setprops($new, $newmessageprops);
3211
+            mapi_savechanges($new);
3212
+
3213
+            // Submit message to non-resource recipients
3214
+            mapi_message_submitmessage($new);
3215
+        }
3216
+
3217
+        // Set properties on meeting object in calendar
3218
+        // Set requestsent to 'true' (turns on 'tracking', etc)
3219
+        $props = [];
3220
+        $props[$this->proptags['meetingstatus']] = olMeeting;
3221
+        $props[$this->proptags['responsestatus']] = olResponseOrganized;
3222
+        // Only set the 'requestsent' property if it wasn't set previously yet,
3223
+        // this ensures we will not accidentally set it from true to false.
3224
+        if (!isset($messageprops[$this->proptags['requestsent']]) || $messageprops[$this->proptags['requestsent']] !== true) {
3225
+            $props[$this->proptags['requestsent']] = !empty($modifiedRecips) || ($this->includesResources && !$this->errorSetResource);
3226
+        }
3227
+        $props[$this->proptags['attendee_critical_change']] = time();
3228
+        $props[$this->proptags['owner_critical_change']] = time();
3229
+        $props[$this->proptags['meetingtype']] = mtgRequest;
3230
+        // save the new updatecounter to exception/recurring series/normal meeting
3231
+        $props[$this->proptags['updatecounter']] = $newmessageprops[$this->proptags['updatecounter']];
3232
+
3233
+        // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3234
+        $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
3235
+        $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
3236
+
3237
+        mapi_setprops($message, $props);
3238
+
3239
+        // saving of these properties on calendar item should be handled by caller function
3240
+        // based on sending meeting request was successful or not
3241
+    }
3242
+
3243
+    /**
3244
+     * OL2007 uses these 4 properties to specify occurrence that should be updated.
3245
+     * ical generates RECURRENCE-ID property based on exception's basedate (PidLidExceptionReplaceTime),
3246
+     * but OL07 doesn't send this property, so ical will generate RECURRENCE-ID property based on date
3247
+     * from GlobalObjId and time from StartRecurTime property, so we are sending basedate property and
3248
+     * also additionally we are sending these properties.
3249
+     * Ref: MS-OXCICAL 2.2.1.20.20 Property: RECURRENCE-ID.
3250
+     *
3251
+     * @param object $recurObject     instance of recurrence class for this message
3252
+     * @param array  $messageprops    properties of meeting object that is going to be send
3253
+     * @param array  $newmessageprops properties of meeting request/response that is going to be send
3254
+     */
3255
+    public function generateRecurDates($recurObject, $messageprops, &$newmessageprops) {
3256
+        if ($messageprops[$this->proptags['startdate']] && $messageprops[$this->proptags['duedate']]) {
3257
+            $startDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['startdate']]));
3258
+            $endDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['duedate']]));
3259
+
3260
+            $startDate = explode(':', $startDate);
3261
+            $endDate = explode(':', $endDate);
3262
+
3263
+            // [0] => year, [1] => month, [2] => day, [3] => hour, [4] => minutes, [5] => seconds
3264
+            // RecurStartDate = year * 512 + month_number * 32 + day_number
3265
+            $newmessageprops[$this->proptags['start_recur_date']] = (((int) $startDate[0]) * 512) + (((int) $startDate[1]) * 32) + ((int) $startDate[2]);
3266
+            // RecurStartTime = hour * 4096 + minutes * 64 + seconds
3267
+            $newmessageprops[$this->proptags['start_recur_time']] = (((int) $startDate[3]) * 4096) + (((int) $startDate[4]) * 64) + ((int) $startDate[5]);
3268
+
3269
+            $newmessageprops[$this->proptags['end_recur_date']] = (((int) $endDate[0]) * 512) + (((int) $endDate[1]) * 32) + ((int) $endDate[2]);
3270
+            $newmessageprops[$this->proptags['end_recur_time']] = (((int) $endDate[3]) * 4096) + (((int) $endDate[4]) * 64) + ((int) $endDate[5]);
3271
+        }
3272
+    }
3273
+
3274
+    /**
3275
+     * Function will create a new outgoing message that will be used to send meeting mail.
3276
+     *
3277
+     * @param MAPIStore $store (optional) store that is used when creating response, if delegate is creating outgoing mail
3278
+     *                         then this would point to delegate store
3279
+     *
3280
+     * @return MAPIMessage outgoing mail that is created and can be used for sending it
3281
+     */
3282
+    public function createOutgoingMessage($store = false) {
3283
+        // get logged in user's store that will be used to send mail, for delegate this will be
3284
+        // delegate store
3285
+        $userStore = $this->openDefaultStore();
3286
+
3287
+        $sentprops = [];
3288
+        $outbox = $this->openDefaultOutbox($userStore);
3289
+
3290
+        $outgoing = mapi_folder_createmessage($outbox);
3291
+
3292
+        // check if $store is set and it is not equal to $defaultStore (means its the delegation case)
3293
+        if ($store !== false) {
3294
+            $storeProps = mapi_getprops($store, [PR_ENTRYID]);
3295
+            $userStoreProps = mapi_getprops($userStore, [PR_ENTRYID]);
3296
+
3297
+            // @FIXME use entryid comparison functions here
3298
+            if ($storeProps[PR_ENTRYID] !== $userStoreProps[PR_ENTRYID]) {
3299
+                // get the delegator properties and set it into outgoing mail
3300
+                $delegatorDetails = $this->getOwnerAddress($store, false);
3301
+
3302
+                if ($delegatorDetails) {
3303
+                    list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegatorDetails;
3304
+                    $sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3305
+                    $sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3306
+                    $sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3307
+                    $sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3308
+                    $sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3309
+                }
3310
+
3311
+                // get the delegate properties and set it into outgoing mail
3312
+                $delegateDetails = $this->getOwnerAddress($userStore, false);
3313
+
3314
+                if ($delegateDetails) {
3315
+                    list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegateDetails;
3316
+                    $sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3317
+                    $sentprops[PR_SENDER_NAME] = $ownername;
3318
+                    $sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3319
+                    $sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3320
+                    $sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3321
+                }
3322
+            }
3323
+        }
3324
+        else {
3325
+            // normal user is sending mail, so both set of properties will be same
3326
+            $userDetails = $this->getOwnerAddress($userStore);
3327
+
3328
+            if ($userDetails) {
3329
+                list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $userDetails;
3330
+                $sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3331
+                $sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3332
+                $sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3333
+                $sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3334
+                $sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3335
+
3336
+                $sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3337
+                $sentprops[PR_SENDER_NAME] = $ownername;
3338
+                $sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3339
+                $sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3340
+                $sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3341
+            }
3342
+        }
3343
+
3344
+        $sentprops[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($userStore);
3345
+
3346
+        mapi_setprops($outgoing, $sentprops);
3347
+
3348
+        return $outgoing;
3349
+    }
3350
+
3351
+    /**
3352
+     * Function which checks that meeting in attendee's calendar is already updated
3353
+     * and we are checking an old meeting request. This function also will update property
3354
+     * meetingtype to indicate that its out of date meeting request.
3355
+     *
3356
+     * @return bool true if meeting request is outofdate else false if it is new
3357
+     */
3358
+    public function isMeetingOutOfDate() {
3359
+        $result = false;
3360
+
3361
+        $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['meetingtype'], $this->proptags['owner_critical_change']]);
3362
+
3363
+        if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS])) {
3364
+            return $result;
3365
+        }
3366
+
3367
+        if (isset($props[$this->proptags['meetingtype']]) && ($props[$this->proptags['meetingtype']] & mtgOutOfDate) == mtgOutOfDate) {
3368
+            return true;
3369
+        }
3370
+
3371
+        // get the basedate to check for exception
3372
+        $basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]);
3373
+
3374
+        $calendarItem = $this->getCorrespondentCalendarItem(true);
3375
+
3376
+        // if basedate is provided and we could not find the item then it could be that we are checking
3377
+        // an exception so get the exception and check it
3378
+        if ($basedate && $calendarItem !== false) {
3379
+            $exception = $this->getExceptionItem($calendarItem, $basedate);
3380
+
3381
+            if ($exception !== false) {
3382
+                // we are able to find the exception compare with it
3383
+                $calendarItem = $exception;
3384
+            }
3385
+            // we are not able to find exception, could mean that a significant change has occurred on series
3386
+                // and it deleted all exceptions, so compare with series
3387
+                // $calendarItem already contains reference to series
3388
+        }
3389
+
3390
+        if ($calendarItem !== false) {
3391
+            $calendarItemProps = mapi_getprops($calendarItem, [
3392
+                $this->proptags['owner_critical_change'],
3393
+                $this->proptags['updatecounter'],
3394
+            ]);
3395
+
3396
+            $updateCounter = (isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]);
3397
+
3398
+            $criticalChange = (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']]);
3399
+
3400
+            if ($updateCounter || $criticalChange) {
3401
+                // meeting request is out of date, set properties to indicate this
3402
+                mapi_setprops($this->message, [$this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033]);
3403
+                mapi_savechanges($this->message);
3404
+
3405
+                $result = true;
3406
+            }
3407
+        }
3408
+
3409
+        return $result;
3410
+    }
3411
+
3412
+    /**
3413
+     * Function which checks that if we have received a meeting response for an updated meeting in organizer's calendar.
3414
+     *
3415
+     * @param Number $basedate basedate of the exception if we want to compare with exception
3416
+     *
3417
+     * @return bool true if meeting request is updated later
3418
+     */
3419
+    public function isMeetingUpdated($basedate = false) {
3420
+        $result = false;
3421
+
3422
+        $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['updatecounter']]);
3423
+
3424
+        if (!$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS])) {
3425
+            return $result;
3426
+        }
3427
+
3428
+        $calendarItem = $this->getCorrespondentCalendarItem(true);
3429
+
3430
+        if ($calendarItem !== false) {
3431
+            // basedate is provided so open exception
3432
+            if ($basedate !== false) {
3433
+                $exception = $this->getExceptionItem($calendarItem, $basedate);
3434
+
3435
+                if ($exception !== false) {
3436
+                    // we are able to find the exception compare with it
3437
+                    $calendarItem = $exception;
3438
+                }
3439
+                // we are not able to find exception, could mean that a significant change has occurred on series
3440
+                    // and it deleted all exceptions, so compare with series
3441
+                    // $calendarItem already contains reference to series
3442
+            }
3443
+
3444
+            if ($calendarItem !== false) {
3445
+                $calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['updatecounter']]);
3446
+
3447
+                /*
3448 3448
 				 * if(message_counter < appointment_counter) meeting object is newer then meeting response (meeting is updated)
3449 3449
 				 * if(message_counter >= appointment_counter) meeting is not updated, do normal processing
3450 3450
 				 */
3451
-				if (isset($calendarItemProps[$this->proptags['updatecounter']], $props[$this->proptags['updatecounter']])) {
3452
-					if ($props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]) {
3453
-						$result = true;
3454
-					}
3455
-				}
3456
-			}
3457
-		}
3458
-
3459
-		return $result;
3460
-	}
3461
-
3462
-	/**
3463
-	 * Checks if there has been any significant changes on appointment/meeting item.
3464
-	 * Significant changes be:
3465
-	 * 1) startdate has been changed
3466
-	 * 2) duedate has been changed OR
3467
-	 * 3) recurrence pattern has been created, modified or removed.
3468
-	 *
3469
-	 * @param array oldProps old props before an update
3470
-	 * @param Number basedate basedate
3471
-	 * @param bool isRecurrenceChanged for change in recurrence pattern.
3472
-	 * isRecurrenceChanged true means Recurrence pattern has been changed, so clear all attendees response
3473
-	 * @param mixed $oldProps
3474
-	 * @param mixed $basedate
3475
-	 * @param mixed $isRecurrenceChanged
3476
-	 */
3477
-	public function checkSignificantChanges($oldProps, $basedate, $isRecurrenceChanged = false) {
3478
-		$message = null;
3479
-		$attach = null;
3480
-
3481
-		// If basedate is specified then we need to open exception message to clear recipient responses
3482
-		if ($basedate) {
3483
-			$recurrence = new Recurrence($this->store, $this->message);
3484
-			if ($recurrence->isException($basedate)) {
3485
-				$attach = $recurrence->getExceptionAttachment($basedate);
3486
-				if ($attach) {
3487
-					$message = mapi_attach_openobj($attach, MAPI_MODIFY);
3488
-				}
3489
-			}
3490
-		}
3491
-		else {
3492
-			// use normal message or recurring series message
3493
-			$message = $this->message;
3494
-		}
3495
-
3496
-		if (!$message) {
3497
-			return;
3498
-		}
3499
-
3500
-		$newProps = mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
3501
-
3502
-		// Check whether message is updated or not.
3503
-		if (isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
3504
-			return;
3505
-		}
3506
-
3507
-		if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) ||
3508
-			($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) ||
3509
-			$isRecurrenceChanged) {
3510
-			$this->clearRecipientResponse($message);
3511
-
3512
-			mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
3513
-
3514
-			mapi_savechanges($message);
3515
-			if ($attach) { // Also save attachment Object.
3516
-				mapi_savechanges($attach);
3517
-			}
3518
-		}
3519
-	}
3520
-
3521
-	/**
3522
-	 * Clear responses of all attendees who have replied in past.
3523
-	 *
3524
-	 * @param MAPI_MESSAGE $message on which responses should be cleared
3525
-	 */
3526
-	public function clearRecipientResponse($message) {
3527
-		$recipTable = mapi_message_getrecipienttable($message);
3528
-		$recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops);
3529
-
3530
-		foreach ($recipsRows as $recipient) {
3531
-			if (($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer) {
3532
-				// Recipient is attendee, set the trackstatus to 'Not Responded'
3533
-				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3534
-			}
3535
-			else {
3536
-				// Recipient is organizer, this is not possible, but for safety
3537
-				// it is best to clear the trackstatus for him as well by setting
3538
-				// the trackstatus to 'Organized'.
3539
-				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3540
-			}
3541
-			mapi_message_modifyrecipients($message, MODRECIP_MODIFY, [$recipient]);
3542
-		}
3543
-	}
3544
-
3545
-	/**
3546
-	 * Function returns correspondent calendar item attached with the meeting request/response/cancellation.
3547
-	 * This will only check for actual MAPIMessages in calendar folder, so if a meeting request is
3548
-	 * for exception then this function will return recurring series for that meeting request
3549
-	 * after that you need to use getExceptionItem function to get exception item that will be
3550
-	 * fetched from the attachment table of recurring series MAPIMessage.
3551
-	 *
3552
-	 * @param bool $open boolean to indicate the function should return entryid or MAPIMessage. Defaults to true.
3553
-	 *
3554
-	 * @return entryid or MAPIMessage resource of calendar item
3555
-	 */
3556
-	public function getCorrespondentCalendarItem($open = true) {
3557
-		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
3558
-
3559
-		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
3560
-			// can work only with meeting requests/responses/cancellations
3561
-			return false;
3562
-		}
3563
-
3564
-		$globalId = $props[$this->proptags['goid']];
3565
-		$cleanGlobalId = $props[$this->proptags['goid2']];
3566
-
3567
-		// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3568
-		if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3569
-			$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
3570
-
3571
-			$store = $delegatorStore['store'];
3572
-			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3573
-		}
3574
-		else {
3575
-			$store = $this->store;
3576
-			$calFolder = $this->openDefaultCalendar();
3577
-		}
3578
-
3579
-		$basedate = $this->getBasedateFromGlobalID($globalId);
3580
-
3581
-		/**
3582
-		 * First search for any appointments which correspond to the $globalId,
3583
-		 * this can be the entire series (if the Meeting Request refers to the
3584
-		 * entire series), or an particular Occurrence (if the meeting Request
3585
-		 * contains a basedate).
3586
-		 *
3587
-		 * If we cannot find a corresponding item, and the $globalId contains
3588
-		 * a $basedate, it might imply that a new exception will have to be
3589
-		 * created for a series which is present in the calendar, we can look
3590
-		 * that one up by searching for the $cleanGlobalId.
3591
-		 */
3592
-		$entryids = $this->findCalendarItems($globalId, $calFolder);
3593
-		if ($basedate && empty($entryids)) {
3594
-			$entryids = $this->findCalendarItems($cleanGlobalId, $calFolder, true);
3595
-		}
3596
-
3597
-		// there should be only one item returned
3598
-		if (!empty($entryids) && count($entryids) === 1) {
3599
-			// return only entryid
3600
-			if ($open === false) {
3601
-				return $entryids[0];
3602
-			}
3603
-
3604
-			// open calendar item and return it
3605
-			return mapi_msgstore_openentry($store, $entryids[0]);
3606
-		}
3607
-
3608
-		// no items found in calendar
3609
-		return false;
3610
-	}
3611
-
3612
-	/**
3613
-	 * Function returns exception item based on the basedate passed.
3614
-	 *
3615
-	 * @param MAPIMessage $recurringMessage Resource of Recurring meeting from calendar
3616
-	 * @param Unixtime    $basedate         basedate of exception that needs to be returned
3617
-	 * @param MAPIStore   $store            store that contains the recurring calendar item
3618
-	 *
3619
-	 * @return entryid or MAPIMessage resource of exception item
3620
-	 */
3621
-	public function getExceptionItem($recurringMessage, $basedate, $store = false) {
3622
-		$occurItem = false;
3623
-
3624
-		$props = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
3625
-
3626
-		// check if the passed item is recurring series
3627
-		if ($props[$this->proptags['recurring']] !== false) {
3628
-			return false;
3629
-		}
3630
-
3631
-		if ($store === false) {
3632
-			// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3633
-			if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3634
-				$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3635
-				$store = $delegatorStore['store'];
3636
-			}
3637
-			else {
3638
-				$store = $this->store;
3639
-			}
3640
-		}
3641
-
3642
-		$recurr = new Recurrence($store, $recurringMessage);
3643
-		$attach = $recurr->getExceptionAttachment($basedate);
3644
-		if ($attach) {
3645
-			$occurItem = mapi_attach_openobj($attach);
3646
-		}
3647
-
3648
-		return $occurItem;
3649
-	}
3650
-
3651
-	/**
3652
-	 * Function which checks whether received meeting request is either conflicting with other appointments or not.
3653
-	 *
3654
-	 * @param MAPIMessage $message   meeting request item that should be checked for conflicts in calendar
3655
-	 * @param MAPIStore   $userStore store containing calendar folder that will be used for confilict checking
3656
-	 * @param MAPIFolder  $calFolder calendar folder for conflict checking
3657
-	 *
3658
-	 * @return mixed(boolean/integer) true if normal meeting is conflicting or an integer which specifies no of instances
3659
-	 * conflict of recurring meeting and false if meeting is not conflicting
3660
-	 * @return mixed if boolean then true/false for indicating conflict, if number then items that are conflicting with the message
3661
-	 */
3662
-	public function isMeetingConflicting($message = false, $userStore = false, $calFolder = false) {
3663
-		$returnValue = false;
3664
-		$noOfInstances = 0;
3665
-
3666
-		if ($message === false) {
3667
-			$message = $this->message;
3668
-		}
3669
-
3670
-		$messageProps = mapi_getprops(
3671
-			$message,
3672
-			[
3673
-				PR_MESSAGE_CLASS,
3674
-				$this->proptags['goid'],
3675
-				$this->proptags['goid2'],
3676
-				$this->proptags['startdate'],
3677
-				$this->proptags['duedate'],
3678
-				$this->proptags['recurring'],
3679
-				$this->proptags['clipstart'],
3680
-				$this->proptags['clipend'],
3681
-				PR_RCVD_REPRESENTING_ENTRYID,
3682
-				$this->proptags['basedate'],
3683
-				PR_RCVD_REPRESENTING_NAME,
3684
-			]
3685
-		);
3686
-
3687
-		if ($userStore === false) {
3688
-			$userStore = $this->store;
3689
-
3690
-			// check if delegate is processing the response
3691
-			if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
3692
-				$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
3693
-
3694
-				$userStore = $delegatorStore['store'];
3695
-				$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3696
-			}
3697
-		}
3698
-
3699
-		if ($calFolder === false) {
3700
-			$calFolder = $this->openDefaultCalendar($userStore);
3701
-		}
3702
-
3703
-		if ($calFolder) {
3704
-			// Meeting request is recurring, so get all occurrence and check for each occurrence whether it conflicts with other appointments in Calendar.
3705
-			if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
3706
-				// Apply recurrence class and retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3707
-				$recurr = new Recurrence($userStore, $message);
3708
-				$items = $recurr->getItems($messageProps[$this->proptags['clipstart']], $messageProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3709
-
3710
-				foreach ($items as $item) {
3711
-					// Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3712
-					$calendarItems = $recurr->getCalendarItems($userStore, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3713
-
3714
-					foreach ($calendarItems as $calendarItem) {
3715
-						if ($calendarItem[$this->proptags['busystatus']] !== fbFree) {
3716
-							/*
3451
+                if (isset($calendarItemProps[$this->proptags['updatecounter']], $props[$this->proptags['updatecounter']])) {
3452
+                    if ($props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]) {
3453
+                        $result = true;
3454
+                    }
3455
+                }
3456
+            }
3457
+        }
3458
+
3459
+        return $result;
3460
+    }
3461
+
3462
+    /**
3463
+     * Checks if there has been any significant changes on appointment/meeting item.
3464
+     * Significant changes be:
3465
+     * 1) startdate has been changed
3466
+     * 2) duedate has been changed OR
3467
+     * 3) recurrence pattern has been created, modified or removed.
3468
+     *
3469
+     * @param array oldProps old props before an update
3470
+     * @param Number basedate basedate
3471
+     * @param bool isRecurrenceChanged for change in recurrence pattern.
3472
+     * isRecurrenceChanged true means Recurrence pattern has been changed, so clear all attendees response
3473
+     * @param mixed $oldProps
3474
+     * @param mixed $basedate
3475
+     * @param mixed $isRecurrenceChanged
3476
+     */
3477
+    public function checkSignificantChanges($oldProps, $basedate, $isRecurrenceChanged = false) {
3478
+        $message = null;
3479
+        $attach = null;
3480
+
3481
+        // If basedate is specified then we need to open exception message to clear recipient responses
3482
+        if ($basedate) {
3483
+            $recurrence = new Recurrence($this->store, $this->message);
3484
+            if ($recurrence->isException($basedate)) {
3485
+                $attach = $recurrence->getExceptionAttachment($basedate);
3486
+                if ($attach) {
3487
+                    $message = mapi_attach_openobj($attach, MAPI_MODIFY);
3488
+                }
3489
+            }
3490
+        }
3491
+        else {
3492
+            // use normal message or recurring series message
3493
+            $message = $this->message;
3494
+        }
3495
+
3496
+        if (!$message) {
3497
+            return;
3498
+        }
3499
+
3500
+        $newProps = mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
3501
+
3502
+        // Check whether message is updated or not.
3503
+        if (isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
3504
+            return;
3505
+        }
3506
+
3507
+        if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) ||
3508
+            ($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) ||
3509
+            $isRecurrenceChanged) {
3510
+            $this->clearRecipientResponse($message);
3511
+
3512
+            mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
3513
+
3514
+            mapi_savechanges($message);
3515
+            if ($attach) { // Also save attachment Object.
3516
+                mapi_savechanges($attach);
3517
+            }
3518
+        }
3519
+    }
3520
+
3521
+    /**
3522
+     * Clear responses of all attendees who have replied in past.
3523
+     *
3524
+     * @param MAPI_MESSAGE $message on which responses should be cleared
3525
+     */
3526
+    public function clearRecipientResponse($message) {
3527
+        $recipTable = mapi_message_getrecipienttable($message);
3528
+        $recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops);
3529
+
3530
+        foreach ($recipsRows as $recipient) {
3531
+            if (($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer) {
3532
+                // Recipient is attendee, set the trackstatus to 'Not Responded'
3533
+                $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3534
+            }
3535
+            else {
3536
+                // Recipient is organizer, this is not possible, but for safety
3537
+                // it is best to clear the trackstatus for him as well by setting
3538
+                // the trackstatus to 'Organized'.
3539
+                $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3540
+            }
3541
+            mapi_message_modifyrecipients($message, MODRECIP_MODIFY, [$recipient]);
3542
+        }
3543
+    }
3544
+
3545
+    /**
3546
+     * Function returns correspondent calendar item attached with the meeting request/response/cancellation.
3547
+     * This will only check for actual MAPIMessages in calendar folder, so if a meeting request is
3548
+     * for exception then this function will return recurring series for that meeting request
3549
+     * after that you need to use getExceptionItem function to get exception item that will be
3550
+     * fetched from the attachment table of recurring series MAPIMessage.
3551
+     *
3552
+     * @param bool $open boolean to indicate the function should return entryid or MAPIMessage. Defaults to true.
3553
+     *
3554
+     * @return entryid or MAPIMessage resource of calendar item
3555
+     */
3556
+    public function getCorrespondentCalendarItem($open = true) {
3557
+        $props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
3558
+
3559
+        if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
3560
+            // can work only with meeting requests/responses/cancellations
3561
+            return false;
3562
+        }
3563
+
3564
+        $globalId = $props[$this->proptags['goid']];
3565
+        $cleanGlobalId = $props[$this->proptags['goid2']];
3566
+
3567
+        // If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3568
+        if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3569
+            $delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
3570
+
3571
+            $store = $delegatorStore['store'];
3572
+            $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3573
+        }
3574
+        else {
3575
+            $store = $this->store;
3576
+            $calFolder = $this->openDefaultCalendar();
3577
+        }
3578
+
3579
+        $basedate = $this->getBasedateFromGlobalID($globalId);
3580
+
3581
+        /**
3582
+         * First search for any appointments which correspond to the $globalId,
3583
+         * this can be the entire series (if the Meeting Request refers to the
3584
+         * entire series), or an particular Occurrence (if the meeting Request
3585
+         * contains a basedate).
3586
+         *
3587
+         * If we cannot find a corresponding item, and the $globalId contains
3588
+         * a $basedate, it might imply that a new exception will have to be
3589
+         * created for a series which is present in the calendar, we can look
3590
+         * that one up by searching for the $cleanGlobalId.
3591
+         */
3592
+        $entryids = $this->findCalendarItems($globalId, $calFolder);
3593
+        if ($basedate && empty($entryids)) {
3594
+            $entryids = $this->findCalendarItems($cleanGlobalId, $calFolder, true);
3595
+        }
3596
+
3597
+        // there should be only one item returned
3598
+        if (!empty($entryids) && count($entryids) === 1) {
3599
+            // return only entryid
3600
+            if ($open === false) {
3601
+                return $entryids[0];
3602
+            }
3603
+
3604
+            // open calendar item and return it
3605
+            return mapi_msgstore_openentry($store, $entryids[0]);
3606
+        }
3607
+
3608
+        // no items found in calendar
3609
+        return false;
3610
+    }
3611
+
3612
+    /**
3613
+     * Function returns exception item based on the basedate passed.
3614
+     *
3615
+     * @param MAPIMessage $recurringMessage Resource of Recurring meeting from calendar
3616
+     * @param Unixtime    $basedate         basedate of exception that needs to be returned
3617
+     * @param MAPIStore   $store            store that contains the recurring calendar item
3618
+     *
3619
+     * @return entryid or MAPIMessage resource of exception item
3620
+     */
3621
+    public function getExceptionItem($recurringMessage, $basedate, $store = false) {
3622
+        $occurItem = false;
3623
+
3624
+        $props = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
3625
+
3626
+        // check if the passed item is recurring series
3627
+        if ($props[$this->proptags['recurring']] !== false) {
3628
+            return false;
3629
+        }
3630
+
3631
+        if ($store === false) {
3632
+            // If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3633
+            if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3634
+                $delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3635
+                $store = $delegatorStore['store'];
3636
+            }
3637
+            else {
3638
+                $store = $this->store;
3639
+            }
3640
+        }
3641
+
3642
+        $recurr = new Recurrence($store, $recurringMessage);
3643
+        $attach = $recurr->getExceptionAttachment($basedate);
3644
+        if ($attach) {
3645
+            $occurItem = mapi_attach_openobj($attach);
3646
+        }
3647
+
3648
+        return $occurItem;
3649
+    }
3650
+
3651
+    /**
3652
+     * Function which checks whether received meeting request is either conflicting with other appointments or not.
3653
+     *
3654
+     * @param MAPIMessage $message   meeting request item that should be checked for conflicts in calendar
3655
+     * @param MAPIStore   $userStore store containing calendar folder that will be used for confilict checking
3656
+     * @param MAPIFolder  $calFolder calendar folder for conflict checking
3657
+     *
3658
+     * @return mixed(boolean/integer) true if normal meeting is conflicting or an integer which specifies no of instances
3659
+     * conflict of recurring meeting and false if meeting is not conflicting
3660
+     * @return mixed if boolean then true/false for indicating conflict, if number then items that are conflicting with the message
3661
+     */
3662
+    public function isMeetingConflicting($message = false, $userStore = false, $calFolder = false) {
3663
+        $returnValue = false;
3664
+        $noOfInstances = 0;
3665
+
3666
+        if ($message === false) {
3667
+            $message = $this->message;
3668
+        }
3669
+
3670
+        $messageProps = mapi_getprops(
3671
+            $message,
3672
+            [
3673
+                PR_MESSAGE_CLASS,
3674
+                $this->proptags['goid'],
3675
+                $this->proptags['goid2'],
3676
+                $this->proptags['startdate'],
3677
+                $this->proptags['duedate'],
3678
+                $this->proptags['recurring'],
3679
+                $this->proptags['clipstart'],
3680
+                $this->proptags['clipend'],
3681
+                PR_RCVD_REPRESENTING_ENTRYID,
3682
+                $this->proptags['basedate'],
3683
+                PR_RCVD_REPRESENTING_NAME,
3684
+            ]
3685
+        );
3686
+
3687
+        if ($userStore === false) {
3688
+            $userStore = $this->store;
3689
+
3690
+            // check if delegate is processing the response
3691
+            if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
3692
+                $delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
3693
+
3694
+                $userStore = $delegatorStore['store'];
3695
+                $calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3696
+            }
3697
+        }
3698
+
3699
+        if ($calFolder === false) {
3700
+            $calFolder = $this->openDefaultCalendar($userStore);
3701
+        }
3702
+
3703
+        if ($calFolder) {
3704
+            // Meeting request is recurring, so get all occurrence and check for each occurrence whether it conflicts with other appointments in Calendar.
3705
+            if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
3706
+                // Apply recurrence class and retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3707
+                $recurr = new Recurrence($userStore, $message);
3708
+                $items = $recurr->getItems($messageProps[$this->proptags['clipstart']], $messageProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3709
+
3710
+                foreach ($items as $item) {
3711
+                    // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3712
+                    $calendarItems = $recurr->getCalendarItems($userStore, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3713
+
3714
+                    foreach ($calendarItems as $calendarItem) {
3715
+                        if ($calendarItem[$this->proptags['busystatus']] !== fbFree) {
3716
+                            /*
3717 3717
 							 * Only meeting requests have globalID, normal appointments do not have globalID
3718 3718
 							 * so if any normal appointment if found then it is assumed to be conflict.
3719 3719
 							 */
3720
-							if (isset($calendarItem[$this->proptags['goid']])) {
3721
-								if ($calendarItem[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) {
3722
-									++$noOfInstances;
3723
-									break;
3724
-								}
3725
-							}
3726
-							else {
3727
-								++$noOfInstances;
3728
-								break;
3729
-							}
3730
-						}
3731
-					}
3732
-				}
3733
-
3734
-				if ($noOfInstances > 0) {
3735
-					$returnValue = $noOfInstances;
3736
-				}
3737
-			}
3738
-			else {
3739
-				// Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3740
-				$items = getCalendarItems($userStore, $calFolder, $messageProps[$this->proptags['startdate']], $messageProps[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3741
-
3742
-				if (isset($messageProps[$this->proptags['basedate']]) && !empty($messageProps[$this->proptags['basedate']])) {
3743
-					$basedate = $messageProps[$this->proptags['basedate']];
3744
-					// Get the goid2 from recurring MR which further used to
3745
-					// check the resource conflicts item.
3746
-					$recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid2']]);
3747
-					$messageProps[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid2']], $basedate);
3748
-					$messageProps[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
3749
-				}
3750
-
3751
-				foreach ($items as $item) {
3752
-					if ($item[$this->proptags['busystatus']] !== fbFree) {
3753
-						if (isset($item[$this->proptags['goid']])) {
3754
-							if (($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) &&
3755
-								($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid2']])) {
3756
-								$returnValue = true;
3757
-								break;
3758
-							}
3759
-						}
3760
-						else {
3761
-							$returnValue = true;
3762
-							break;
3763
-						}
3764
-					}
3765
-				}
3766
-			}
3767
-		}
3768
-
3769
-		return $returnValue;
3770
-	}
3771
-
3772
-	/**
3773
-	 *  Function which adds organizer to recipient list which is passed.
3774
-	 *  This function also checks if it has organizer.
3775
-	 *
3776
-	 * @param array $messageProps message properties
3777
-	 * @param array $recipients   recipients list of message
3778
-	 * @param bool  $isException  true if we are processing recipient of exception
3779
-	 */
3780
-	public function addDelegator($messageProps, &$recipients) {
3781
-		$hasDelegator = false;
3782
-		// Check if meeting already has an organizer.
3783
-		foreach ($recipients as $key => $recipient) {
3784
-			if (isset($messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) && $recipient[PR_EMAIL_ADDRESS] == $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) {
3785
-				$hasDelegator = true;
3786
-			}
3787
-		}
3788
-
3789
-		if (!$hasDelegator) {
3790
-			// Create delegator.
3791
-			$delegator = [];
3792
-			$delegator[PR_ENTRYID] = $messageProps[PR_RCVD_REPRESENTING_ENTRYID];
3793
-			$delegator[PR_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
3794
-			$delegator[PR_EMAIL_ADDRESS] = $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS];
3795
-			$delegator[PR_RECIPIENT_TYPE] = MAPI_TO;
3796
-			$delegator[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
3797
-			$delegator[PR_ADDRTYPE] = empty($messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_RCVD_REPRESENTING_ADDRTYPE];
3798
-			$delegator[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3799
-			$delegator[PR_RECIPIENT_FLAGS] = recipSendable;
3800
-			$delegator[PR_SEARCH_KEY] = $messageProps[PR_RCVD_REPRESENTING_SEARCH_KEY];
3801
-
3802
-			// Add organizer to recipients list.
3803
-			array_unshift($recipients, $delegator);
3804
-		}
3805
-	}
3806
-
3807
-	/**
3808
-	 * Function will return delegator's store and calendar folder for processing meetings.
3809
-	 *
3810
-	 * @param string $receivedRepresentingEnryid  entryid of the delegator user
3811
-	 * @param array  $foldersToOpen               contains list of folder types that should be returned in result
3812
-	 * @param mixed  $receivedRepresentingEntryId
3813
-	 *
3814
-	 * @return array contains store of the delegator and resource of folders if $foldersToOpen is not empty
3815
-	 */
3816
-	public function getDelegatorStore($receivedRepresentingEntryId, $foldersToOpen = []) {
3817
-		$returnData = [];
3818
-
3819
-		$delegatorStore = $this->openCustomUserStore($receivedRepresentingEntryId);
3820
-		$returnData['store'] = $delegatorStore;
3821
-
3822
-		if (!empty($foldersToOpen)) {
3823
-			for ($index = 0, $len = count($foldersToOpen); $index < $len; ++$index) {
3824
-				$folderType = $foldersToOpen[$index];
3825
-
3826
-				// first try with default folders
3827
-				$folder = $this->openDefaultFolder($folderType, $delegatorStore);
3828
-
3829
-				// if folder not found then try with base folders
3830
-				if ($folder === false) {
3831
-					$folder = $this->openBaseFolder($folderType, $delegatorStore);
3832
-				}
3833
-
3834
-				if ($folder === false) {
3835
-					// we are still not able to get the folder so give up
3836
-					continue;
3837
-				}
3838
-
3839
-				$returnData[$folderType] = $folder;
3840
-			}
3841
-		}
3842
-
3843
-		return $returnData;
3844
-	}
3845
-
3846
-	/**
3847
-	 * Function returns extra info about meeting timing along with message body
3848
-	 * which will be included in body while sending meeting request/response.
3849
-	 *
3850
-	 * @return string $meetingTimeInfo info about meeting timing along with message body
3851
-	 */
3852
-	public function getMeetingTimeInfo() {
3853
-		return $this->meetingTimeInfo;
3854
-	}
3855
-
3856
-	/**
3857
-	 * Function sets extra info about meeting timing along with message body
3858
-	 * which will be included in body while sending meeting request/response.
3859
-	 *
3860
-	 * @param string $meetingTimeInfo info about meeting timing along with message body
3861
-	 */
3862
-	public function setMeetingTimeInfo($meetingTimeInfo) {
3863
-		$this->meetingTimeInfo = $meetingTimeInfo;
3864
-	}
3865
-
3866
-	/**
3867
-	 * Helper function which is use to get local categories of all occurrence.
3868
-	 *
3869
-	 * @param MAPIMessage $calendarItem meeting request item
3870
-	 * @param MAPIStore   $store        store containing calendar folder
3871
-	 * @param MAPIFolder  $calFolder    calendar folder
3872
-	 *
3873
-	 * @return array $localCategories which contain array of basedate along with categories
3874
-	 */
3875
-	public function getLocalCategories($calendarItem, $store, $calFolder) {
3876
-		$calendarItemProps = mapi_getprops($calendarItem);
3877
-		$recurrence = new Recurrence($store, $calendarItem);
3878
-
3879
-		// Retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3880
-		$items = $recurrence->getItems($calendarItemProps[$this->proptags['clipstart']], $calendarItemProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3881
-		$localCategories = [];
3882
-
3883
-		foreach ($items as $item) {
3884
-			$recurrenceItems = $recurrence->getCalendarItems($store, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus'], $this->proptags['categories']]);
3885
-			foreach ($recurrenceItems as $recurrenceItem) {
3886
-				// Check if occurrence is exception then get the local categories of that occurrence.
3887
-				if (isset($recurrenceItem[$this->proptags['goid']]) && $recurrenceItem[$this->proptags['goid']] == $calendarItemProps[$this->proptags['goid']]) {
3888
-					$exceptionAttach = $recurrence->getExceptionAttachment($recurrenceItem['basedate']);
3889
-
3890
-					if ($exceptionAttach) {
3891
-						$exception = mapi_attach_openobj($exceptionAttach, 0);
3892
-						$exceptionProps = mapi_getprops($exception, [$this->proptags['categories']]);
3893
-						if (isset($exceptionProps[$this->proptags['categories']])) {
3894
-							$localCategories[$recurrenceItem['basedate']] = $exceptionProps[$this->proptags['categories']];
3895
-						}
3896
-					}
3897
-				}
3898
-			}
3899
-		}
3900
-
3901
-		return $localCategories;
3902
-	}
3903
-
3904
-	/**
3905
-	 * Helper function which is use to apply local categories on respective occurrences.
3906
-	 *
3907
-	 * @param MAPIMessage $calendarItem    meeting request item
3908
-	 * @param MAPIStore   $store           store containing calendar folder
3909
-	 * @param array       $localCategories array contains basedate and array of categories
3910
-	 */
3911
-	public function applyLocalCategories($calendarItem, $store, $localCategories) {
3912
-		$calendarItemProps = mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
3913
-		$message = mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
3914
-		$recurrence = new Recurrence($store, $message);
3915
-
3916
-		// Check for all occurrence if it is exception then modify the exception by setting up categories,
3917
-		// Otherwise create new exception with categories.
3918
-		foreach ($localCategories as $key => $value) {
3919
-			if ($recurrence->isException($key)) {
3920
-				$recurrence->modifyException([$this->proptags['categories'] => $value], $key);
3921
-			}
3922
-			else {
3923
-				$recurrence->createException([$this->proptags['categories'] => $value], $key, false);
3924
-			}
3925
-			mapi_savechanges($message);
3926
-		}
3927
-	}
3720
+                            if (isset($calendarItem[$this->proptags['goid']])) {
3721
+                                if ($calendarItem[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) {
3722
+                                    ++$noOfInstances;
3723
+                                    break;
3724
+                                }
3725
+                            }
3726
+                            else {
3727
+                                ++$noOfInstances;
3728
+                                break;
3729
+                            }
3730
+                        }
3731
+                    }
3732
+                }
3733
+
3734
+                if ($noOfInstances > 0) {
3735
+                    $returnValue = $noOfInstances;
3736
+                }
3737
+            }
3738
+            else {
3739
+                // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3740
+                $items = getCalendarItems($userStore, $calFolder, $messageProps[$this->proptags['startdate']], $messageProps[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3741
+
3742
+                if (isset($messageProps[$this->proptags['basedate']]) && !empty($messageProps[$this->proptags['basedate']])) {
3743
+                    $basedate = $messageProps[$this->proptags['basedate']];
3744
+                    // Get the goid2 from recurring MR which further used to
3745
+                    // check the resource conflicts item.
3746
+                    $recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid2']]);
3747
+                    $messageProps[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid2']], $basedate);
3748
+                    $messageProps[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
3749
+                }
3750
+
3751
+                foreach ($items as $item) {
3752
+                    if ($item[$this->proptags['busystatus']] !== fbFree) {
3753
+                        if (isset($item[$this->proptags['goid']])) {
3754
+                            if (($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) &&
3755
+                                ($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid2']])) {
3756
+                                $returnValue = true;
3757
+                                break;
3758
+                            }
3759
+                        }
3760
+                        else {
3761
+                            $returnValue = true;
3762
+                            break;
3763
+                        }
3764
+                    }
3765
+                }
3766
+            }
3767
+        }
3768
+
3769
+        return $returnValue;
3770
+    }
3771
+
3772
+    /**
3773
+     *  Function which adds organizer to recipient list which is passed.
3774
+     *  This function also checks if it has organizer.
3775
+     *
3776
+     * @param array $messageProps message properties
3777
+     * @param array $recipients   recipients list of message
3778
+     * @param bool  $isException  true if we are processing recipient of exception
3779
+     */
3780
+    public function addDelegator($messageProps, &$recipients) {
3781
+        $hasDelegator = false;
3782
+        // Check if meeting already has an organizer.
3783
+        foreach ($recipients as $key => $recipient) {
3784
+            if (isset($messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) && $recipient[PR_EMAIL_ADDRESS] == $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) {
3785
+                $hasDelegator = true;
3786
+            }
3787
+        }
3788
+
3789
+        if (!$hasDelegator) {
3790
+            // Create delegator.
3791
+            $delegator = [];
3792
+            $delegator[PR_ENTRYID] = $messageProps[PR_RCVD_REPRESENTING_ENTRYID];
3793
+            $delegator[PR_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
3794
+            $delegator[PR_EMAIL_ADDRESS] = $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS];
3795
+            $delegator[PR_RECIPIENT_TYPE] = MAPI_TO;
3796
+            $delegator[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
3797
+            $delegator[PR_ADDRTYPE] = empty($messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_RCVD_REPRESENTING_ADDRTYPE];
3798
+            $delegator[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3799
+            $delegator[PR_RECIPIENT_FLAGS] = recipSendable;
3800
+            $delegator[PR_SEARCH_KEY] = $messageProps[PR_RCVD_REPRESENTING_SEARCH_KEY];
3801
+
3802
+            // Add organizer to recipients list.
3803
+            array_unshift($recipients, $delegator);
3804
+        }
3805
+    }
3806
+
3807
+    /**
3808
+     * Function will return delegator's store and calendar folder for processing meetings.
3809
+     *
3810
+     * @param string $receivedRepresentingEnryid  entryid of the delegator user
3811
+     * @param array  $foldersToOpen               contains list of folder types that should be returned in result
3812
+     * @param mixed  $receivedRepresentingEntryId
3813
+     *
3814
+     * @return array contains store of the delegator and resource of folders if $foldersToOpen is not empty
3815
+     */
3816
+    public function getDelegatorStore($receivedRepresentingEntryId, $foldersToOpen = []) {
3817
+        $returnData = [];
3818
+
3819
+        $delegatorStore = $this->openCustomUserStore($receivedRepresentingEntryId);
3820
+        $returnData['store'] = $delegatorStore;
3821
+
3822
+        if (!empty($foldersToOpen)) {
3823
+            for ($index = 0, $len = count($foldersToOpen); $index < $len; ++$index) {
3824
+                $folderType = $foldersToOpen[$index];
3825
+
3826
+                // first try with default folders
3827
+                $folder = $this->openDefaultFolder($folderType, $delegatorStore);
3828
+
3829
+                // if folder not found then try with base folders
3830
+                if ($folder === false) {
3831
+                    $folder = $this->openBaseFolder($folderType, $delegatorStore);
3832
+                }
3833
+
3834
+                if ($folder === false) {
3835
+                    // we are still not able to get the folder so give up
3836
+                    continue;
3837
+                }
3838
+
3839
+                $returnData[$folderType] = $folder;
3840
+            }
3841
+        }
3842
+
3843
+        return $returnData;
3844
+    }
3845
+
3846
+    /**
3847
+     * Function returns extra info about meeting timing along with message body
3848
+     * which will be included in body while sending meeting request/response.
3849
+     *
3850
+     * @return string $meetingTimeInfo info about meeting timing along with message body
3851
+     */
3852
+    public function getMeetingTimeInfo() {
3853
+        return $this->meetingTimeInfo;
3854
+    }
3855
+
3856
+    /**
3857
+     * Function sets extra info about meeting timing along with message body
3858
+     * which will be included in body while sending meeting request/response.
3859
+     *
3860
+     * @param string $meetingTimeInfo info about meeting timing along with message body
3861
+     */
3862
+    public function setMeetingTimeInfo($meetingTimeInfo) {
3863
+        $this->meetingTimeInfo = $meetingTimeInfo;
3864
+    }
3865
+
3866
+    /**
3867
+     * Helper function which is use to get local categories of all occurrence.
3868
+     *
3869
+     * @param MAPIMessage $calendarItem meeting request item
3870
+     * @param MAPIStore   $store        store containing calendar folder
3871
+     * @param MAPIFolder  $calFolder    calendar folder
3872
+     *
3873
+     * @return array $localCategories which contain array of basedate along with categories
3874
+     */
3875
+    public function getLocalCategories($calendarItem, $store, $calFolder) {
3876
+        $calendarItemProps = mapi_getprops($calendarItem);
3877
+        $recurrence = new Recurrence($store, $calendarItem);
3878
+
3879
+        // Retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3880
+        $items = $recurrence->getItems($calendarItemProps[$this->proptags['clipstart']], $calendarItemProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3881
+        $localCategories = [];
3882
+
3883
+        foreach ($items as $item) {
3884
+            $recurrenceItems = $recurrence->getCalendarItems($store, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus'], $this->proptags['categories']]);
3885
+            foreach ($recurrenceItems as $recurrenceItem) {
3886
+                // Check if occurrence is exception then get the local categories of that occurrence.
3887
+                if (isset($recurrenceItem[$this->proptags['goid']]) && $recurrenceItem[$this->proptags['goid']] == $calendarItemProps[$this->proptags['goid']]) {
3888
+                    $exceptionAttach = $recurrence->getExceptionAttachment($recurrenceItem['basedate']);
3889
+
3890
+                    if ($exceptionAttach) {
3891
+                        $exception = mapi_attach_openobj($exceptionAttach, 0);
3892
+                        $exceptionProps = mapi_getprops($exception, [$this->proptags['categories']]);
3893
+                        if (isset($exceptionProps[$this->proptags['categories']])) {
3894
+                            $localCategories[$recurrenceItem['basedate']] = $exceptionProps[$this->proptags['categories']];
3895
+                        }
3896
+                    }
3897
+                }
3898
+            }
3899
+        }
3900
+
3901
+        return $localCategories;
3902
+    }
3903
+
3904
+    /**
3905
+     * Helper function which is use to apply local categories on respective occurrences.
3906
+     *
3907
+     * @param MAPIMessage $calendarItem    meeting request item
3908
+     * @param MAPIStore   $store           store containing calendar folder
3909
+     * @param array       $localCategories array contains basedate and array of categories
3910
+     */
3911
+    public function applyLocalCategories($calendarItem, $store, $localCategories) {
3912
+        $calendarItemProps = mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
3913
+        $message = mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
3914
+        $recurrence = new Recurrence($store, $message);
3915
+
3916
+        // Check for all occurrence if it is exception then modify the exception by setting up categories,
3917
+        // Otherwise create new exception with categories.
3918
+        foreach ($localCategories as $key => $value) {
3919
+            if ($recurrence->isException($key)) {
3920
+                $recurrence->modifyException([$this->proptags['categories'] => $value], $key);
3921
+            }
3922
+            else {
3923
+                $recurrence->createException([$this->proptags['categories'] => $value], $key, false);
3924
+            }
3925
+            mapi_savechanges($message);
3926
+        }
3927
+    }
3928 3928
 }
Please login to merge, or discard this patch.
Braces   +75 added lines, -150 removed lines patch added patch discarded remove patch
@@ -304,8 +304,7 @@  discard block
 block discarded – undo
304 304
 		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
305 305
 			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
306 306
 			$userStore = $delegatorStore['store'];
307
-		}
308
-		else {
307
+		} else {
309 308
 			$userStore = $this->store;
310 309
 		}
311 310
 
@@ -373,8 +372,7 @@  discard block
 block discarded – undo
373 372
 			// Create/modify exception
374 373
 			if ($recurr->isException($basedate)) {
375 374
 				$recurr->modifyException($exception_props, $basedate);
376
-			}
377
-			else {
375
+			} else {
378 376
 				// When we are creating an exception we need copy recipients from main recurring item
379 377
 				$recipTable = mapi_message_getrecipienttable($calendarItem);
380 378
 				$recips = mapi_table_queryallrows($recipTable, $this->recipprops);
@@ -392,8 +390,7 @@  discard block
 block discarded – undo
392 390
 			if ($attach) {
393 391
 				$recurringItem = $calendarItem;
394 392
 				$calendarItem = mapi_attach_openobj($attach, MAPI_MODIFY);
395
-			}
396
-			else {
393
+			} else {
397 394
 				return false;
398 395
 			}
399 396
 		}
@@ -480,8 +477,7 @@  discard block
 block discarded – undo
480 477
 			$props = [];
481 478
 			if ($messageprops[$this->proptags['counter_proposal']]) {
482 479
 				$props[$this->proptags['counter_proposal']] = true;
483
-			}
484
-			else {
480
+			} else {
485 481
 				$props[$this->proptags['counter_proposal']] = false;
486 482
 			}
487 483
 
@@ -537,8 +533,7 @@  discard block
 block discarded – undo
537 533
 
538 534
 			$store = $delegatorStore['store'];
539 535
 			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
540
-		}
541
-		else {
536
+		} else {
542 537
 			$store = $this->store;
543 538
 			$calFolder = $this->openDefaultCalendar();
544 539
 		}
@@ -566,13 +561,11 @@  discard block
 block discarded – undo
566 561
 
567 562
 					if ($recurr->isException($basedate)) {
568 563
 						$recurr->modifyException($messageProps, $basedate);
569
-					}
570
-					else {
564
+					} else {
571 565
 						$recurr->createException($messageProps, $basedate);
572 566
 					}
573 567
 				}
574
-			}
575
-			else {
568
+			} else {
576 569
 				// set the properties of the cancellation object
577 570
 				mapi_setprops($calendarItem, $messageProps);
578 571
 			}
@@ -626,8 +619,7 @@  discard block
 block discarded – undo
626 619
 
627 620
 			$store = $delegatorStore['store'];
628 621
 			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
629
-		}
630
-		else {
622
+		} else {
631 623
 			$calFolder = $this->openDefaultCalendar();
632 624
 			$store = $this->store;
633 625
 		}
@@ -690,8 +682,7 @@  discard block
 block discarded – undo
690 682
 		$senderEntryId = isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) ? $messageprops[PR_SENT_REPRESENTING_ENTRYID] : $messageprops[PR_SENDER_ENTRYID];
691 683
 		if (isset($messageprops[PR_RECEIVED_BY_ENTRYID]) && $GLOBALS["entryid"]->compareEntryIds($senderEntryId, $messageprops[PR_RECEIVED_BY_ENTRYID])) {
692 684
 			$entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $body, true, $store, $calFolder, $basedate);
693
-		}
694
-		else {
685
+		} else {
695 686
 			$entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $body, $userAction, $store, $calFolder, $basedate);
696 687
 		}
697 688
 
@@ -745,8 +736,7 @@  discard block
 block discarded – undo
745 736
 				if (!$calendarItem) {
746 737
 					// Recurring item not found, so create new meeting in Calendar
747 738
 					$calendarItem = mapi_folder_createmessage($calFolder);
748
-				}
749
-				else {
739
+				} else {
750 740
 					// we have found the main recurring item, check if this meeting request is already processed
751 741
 					if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
752 742
 						// only set required properties, other properties are already copied when processing this meeting request
@@ -768,8 +758,7 @@  discard block
 block discarded – undo
768 758
 					if (isset($props[$this->proptags['reminderminutes']])) {
769 759
 						$props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
770 760
 					}
771
-				}
772
-				else {
761
+				} else {
773 762
 					// only get required properties so we will not overwrite existing updated properties from calendar
774 763
 					$props = mapi_getprops($this->message, [PR_ENTRYID]);
775 764
 				}
@@ -790,13 +779,11 @@  discard block
 block discarded – undo
790 779
 				if (isset($props[$this->proptags['intendedbusystatus']])) {
791 780
 					if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
792 781
 						$props[$this->proptags['busystatus']] = fbTentative;
793
-					}
794
-					else {
782
+					} else {
795 783
 						$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
796 784
 					}
797 785
 					// we already have intendedbusystatus value in $props so no need to copy it
798
-				}
799
-				else {
786
+				} else {
800 787
 					$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
801 788
 				}
802 789
 
@@ -859,8 +846,7 @@  discard block
 block discarded – undo
859 846
 				}
860 847
 
861 848
 				$entryid = $props[PR_ENTRYID];
862
-			}
863
-			else {
849
+			} else {
864 850
 				/**
865 851
 				 * This meeting request is not recurring, so can be an exception or normal meeting.
866 852
 				 * If exception then find main recurring item and update exception
@@ -933,13 +919,11 @@  discard block
 block discarded – undo
933 919
 						if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
934 920
 							if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
935 921
 								$calItemProps[$this->proptags['busystatus']] = fbTentative;
936
-							}
937
-							else {
922
+							} else {
938 923
 								$calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
939 924
 							}
940 925
 							$calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
941
-						}
942
-						else {
926
+						} else {
943 927
 							$calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
944 928
 						}
945 929
 
@@ -989,8 +973,7 @@  discard block
 block discarded – undo
989 973
 
990 974
 						$messageprops = mapi_getprops($calmsg, [PR_ENTRYID]);
991 975
 						$entryid = $messageprops[PR_ENTRYID];
992
-					}
993
-					else {
976
+					} else {
994 977
 						// Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment
995 978
 						$new = mapi_folder_createmessage($calFolder);
996 979
 						$props = mapi_getprops($this->message);
@@ -1036,13 +1019,11 @@  discard block
 block discarded – undo
1036 1019
 						if (isset($props[$this->proptags['intendedbusystatus']])) {
1037 1020
 							if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
1038 1021
 								$props[$this->proptags['busystatus']] = fbTentative;
1039
-							}
1040
-							else {
1022
+							} else {
1041 1023
 								$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
1042 1024
 							}
1043 1025
 							// we already have intendedbusystatus value in $props so no need to copy it
1044
-						}
1045
-						else {
1026
+						} else {
1046 1027
 							$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1047 1028
 						}
1048 1029
 
@@ -1072,8 +1053,7 @@  discard block
 block discarded – undo
1072 1053
 								],
1073 1054
 							];
1074 1055
 							$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
1075
-						}
1076
-						else {
1056
+						} else {
1077 1057
 							$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1078 1058
 						}
1079 1059
 
@@ -1086,8 +1066,7 @@  discard block
 block discarded – undo
1086 1066
 					}
1087 1067
 				}
1088 1068
 			}
1089
-		}
1090
-		else {
1069
+		} else {
1091 1070
 			// Here only properties are set on calendaritem, because user is responding from calendar.
1092 1071
 			$props = [];
1093 1072
 			$props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted;
@@ -1095,13 +1074,11 @@  discard block
 block discarded – undo
1095 1074
 			if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
1096 1075
 				if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
1097 1076
 					$props[$this->proptags['busystatus']] = fbTentative;
1098
-				}
1099
-				else {
1077
+				} else {
1100 1078
 					$props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1101 1079
 				}
1102 1080
 				$props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1103
-			}
1104
-			else {
1081
+			} else {
1105 1082
 				$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1106 1083
 			}
1107 1084
 
@@ -1124,8 +1101,7 @@  discard block
 block discarded – undo
1124 1101
 
1125 1102
 				if ($recurr->isException($basedate)) {
1126 1103
 					$recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips);
1127
-				}
1128
-				else {
1104
+				} else {
1129 1105
 					$props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
1130 1106
 					$props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
1131 1107
 
@@ -1137,8 +1113,7 @@  discard block
 block discarded – undo
1137 1113
 
1138 1114
 					$recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips);
1139 1115
 				}
1140
-			}
1141
-			else {
1116
+			} else {
1142 1117
 				mapi_setprops($this->message, $proposeNewTimeProps + $props);
1143 1118
 			}
1144 1119
 			mapi_savechanges($this->message);
@@ -1179,8 +1154,7 @@  discard block
 block discarded – undo
1179 1154
 
1180 1155
 			$store = $delegatorStore['store'];
1181 1156
 			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1182
-		}
1183
-		else {
1157
+		} else {
1184 1158
 			$calFolder = $this->openDefaultCalendar();
1185 1159
 			$store = $this->store;
1186 1160
 		}
@@ -1272,8 +1246,7 @@  discard block
 block discarded – undo
1272 1246
 
1273 1247
 			$store = $delegatorStore['store'];
1274 1248
 			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1275
-		}
1276
-		else {
1249
+		} else {
1277 1250
 			$store = $this->store;
1278 1251
 			$calFolder = $this->openDefaultCalendar();
1279 1252
 		}
@@ -1305,8 +1278,7 @@  discard block
 block discarded – undo
1305 1278
 						// exception found, remove it from calendar
1306 1279
 						$this->doRemoveExceptionFromCalendar($basedate, $calendarItem, $store);
1307 1280
 					}
1308
-				}
1309
-				else {
1281
+				} else {
1310 1282
 					// remove normal / recurring series from calendar
1311 1283
 					$entryids = mapi_getprops($calendarItem, [PR_ENTRYID]);
1312 1284
 
@@ -1321,14 +1293,12 @@  discard block
 block discarded – undo
1321 1293
 
1322 1294
 			// Move the cancellation mail to wastebasket
1323 1295
 			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1324
-		}
1325
-		else {
1296
+		} else {
1326 1297
 			// Here only properties are set on calendaritem, because user is responding from calendar.
1327 1298
 			if ($basedate) {
1328 1299
 				// remove the occurrence
1329 1300
 				$this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
1330
-			}
1331
-			else {
1301
+			} else {
1332 1302
 				// remove normal/recurring meeting item.
1333 1303
 				// Move the message to the waste basket
1334 1304
 				mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
@@ -1375,8 +1345,7 @@  discard block
 block discarded – undo
1375 1345
 
1376 1346
 			// save changes in the message
1377 1347
 			mapi_savechanges($this->message);
1378
-		}
1379
-		else {
1348
+		} else {
1380 1349
 			// cancellation of normal meeting request
1381 1350
 			// Send the cancellation
1382 1351
 			$this->updateMeetingRequest();
@@ -1523,8 +1492,7 @@  discard block
 block discarded – undo
1523 1492
 					}
1524 1493
 				}
1525 1494
 			}
1526
-		}
1527
-		else {
1495
+		} else {
1528 1496
 			// Basedate found, an exception is to be send
1529 1497
 			if ($basedate) {
1530 1498
 				$recurr = new Recurrence($this->openDefaultStore(), $this->message);
@@ -1532,8 +1500,7 @@  discard block
 block discarded – undo
1532 1500
 				if ($cancel) {
1533 1501
 					// @TODO: remove occurrence from Resource's Calendar if resource was booked for whole series
1534 1502
 					$this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false);
1535
-				}
1536
-				else {
1503
+				} else {
1537 1504
 					$attach = $recurr->getExceptionAttachment($basedate);
1538 1505
 
1539 1506
 					if ($attach) {
@@ -1552,8 +1519,7 @@  discard block
 block discarded – undo
1552 1519
 						}
1553 1520
 					}
1554 1521
 				}
1555
-			}
1556
-			else {
1522
+			} else {
1557 1523
 				// This is normal meeting
1558 1524
 				$resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1559 1525
 
@@ -1600,20 +1566,15 @@  discard block
 block discarded – undo
1600 1566
 			$blockItem['end'] = $event['EndTime'];
1601 1567
 			if ($event['BusyType'] == 'Free') {
1602 1568
 				$blockItem['status'] = 0;
1603
-			}
1604
-			elseif ($event['BusyType'] == 'Tentative') {
1569
+			} elseif ($event['BusyType'] == 'Tentative') {
1605 1570
 				$blockItem['status'] = 1;
1606
-			}
1607
-			elseif ($event['BusyType'] == 'Busy') {
1571
+			} elseif ($event['BusyType'] == 'Busy') {
1608 1572
 				$blockItem['status'] = 2;
1609
-			}
1610
-			elseif ($event['BusyType'] == 'OOF') {
1573
+			} elseif ($event['BusyType'] == 'OOF') {
1611 1574
 				$blockItem['status'] = 3;
1612
-			}
1613
-			elseif ($event['BusyType'] == 'WorkingElsewhere') {
1575
+			} elseif ($event['BusyType'] == 'WorkingElsewhere') {
1614 1576
 				$blockItem['status'] = 4;
1615
-			}
1616
-			else {
1577
+			} else {
1617 1578
 				$blockItem['status'] = -1;
1618 1579
 			}
1619 1580
 			$last_end = $event['EndTime'];
@@ -1637,8 +1598,7 @@  discard block
 block discarded – undo
1637 1598
 
1638 1599
 		if (!isset($messageprops[$this->proptags['goid']])) {
1639 1600
 			$this->setMeetingRequest($basedate);
1640
-		}
1641
-		else {
1601
+		} else {
1642 1602
 			$counter = $messageprops[$this->proptags['last_updatecounter']] + 1;
1643 1603
 
1644 1604
 			// increment value of last_updatecounter, last_updatecounter will be common for recurring series
@@ -1657,8 +1617,7 @@  discard block
 block discarded – undo
1657 1617
 		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
1658 1618
 			// we are checking with calendar item
1659 1619
 			$calendarItem = $this->message;
1660
-		}
1661
-		else {
1620
+		} else {
1662 1621
 			// we are checking with meeting request / response / cancellation mail
1663 1622
 			// get calendar items
1664 1623
 			$calendarItem = $this->getCorrespondentCalendarItem(true);
@@ -1790,8 +1749,7 @@  discard block
 block discarded – undo
1790 1749
 			if (isset($inboxprops[$prop])) {
1791 1750
 				return $inboxprops[$prop];
1792 1751
 			}
1793
-		}
1794
-		catch (MAPIException $e) {
1752
+		} catch (MAPIException $e) {
1795 1753
 			// public store doesn't support this method
1796 1754
 			if ($e->getCode() == MAPI_E_NO_SUPPORT) {
1797 1755
 				// don't propagate this error to parent handlers, if store doesn't support it
@@ -1881,8 +1839,7 @@  discard block
 block discarded – undo
1881 1839
 				if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1882 1840
 					$accessToFolder = true;
1883 1841
 				}
1884
-			}
1885
-			catch (MAPIException $e) {
1842
+			} catch (MAPIException $e) {
1886 1843
 				// we don't have rights to open folder, so return false
1887 1844
 				if ($e->getCode() == MAPI_E_NO_ACCESS) {
1888 1845
 					return $accessToFolder;
@@ -1912,8 +1869,7 @@  discard block
 block discarded – undo
1912 1869
 				$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID]);
1913 1870
 
1914 1871
 				$store = $delegatorStore['store'];
1915
-			}
1916
-			else {
1872
+			} else {
1917 1873
 				$store = $this->store;
1918 1874
 			}
1919 1875
 		}
@@ -1923,8 +1879,7 @@  discard block
 block discarded – undo
1923 1879
 		if (isset($provider[PR_MDB_PROVIDER]) && $provider[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1924 1880
 			$entryid = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
1925 1881
 			$entryid = $entryid[PR_PARENT_ENTRYID];
1926
-		}
1927
-		else {
1882
+		} else {
1928 1883
 			$entryid = $this->getDefaultFolderEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1929 1884
 			if ($entryid === false) {
1930 1885
 				$entryid = $this->getBaseEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
@@ -1950,8 +1905,7 @@  discard block
 block discarded – undo
1950 1905
 
1951 1906
 		try {
1952 1907
 			$mailuser = mapi_ab_openentry($ab, $ownerentryid);
1953
-		}
1954
-		catch (MAPIException $e) {
1908
+		} catch (MAPIException $e) {
1955 1909
 			return;
1956 1910
 		}
1957 1911
 
@@ -2018,8 +1972,7 @@  discard block
 block discarded – undo
2018 1972
 				$props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
2019 1973
 				$props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
2020 1974
 				$props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
2021
-			}
2022
-			else {
1975
+			} else {
2023 1976
 				// Exceptions is deleted.
2024 1977
 				// Update $messageprops with timings of occurrence
2025 1978
 				$messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
@@ -2031,8 +1984,7 @@  discard block
 block discarded – undo
2031 1984
 
2032 1985
 			$props[$this->proptags['recurring']] = false;
2033 1986
 			$props[$this->proptags['is_exception']] = true;
2034
-		}
2035
-		else {
1987
+		} else {
2036 1988
 			// we are creating a response from meeting request mail (it could be recurring or non-recurring)
2037 1989
 			// Send all recurrence info in response, if this is a recurrence meeting.
2038 1990
 			$isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
@@ -2215,8 +2167,7 @@  discard block
 block discarded – undo
2215 2167
 			if (!$abitem) {
2216 2168
 				return '';
2217 2169
 			}
2218
-		}
2219
-		catch (MAPIException $e) {
2170
+		} catch (MAPIException $e) {
2220 2171
 			return '';
2221 2172
 		}
2222 2173
 
@@ -2311,8 +2262,7 @@  discard block
 block discarded – undo
2311 2262
 		foreach ($recipients as $key => $recipient) {
2312 2263
 			if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) {
2313 2264
 				$hasOrganizer = true;
2314
-			}
2315
-			elseif ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])) {
2265
+			} elseif ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])) {
2316 2266
 				// Recipients for an occurrence
2317 2267
 				$recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse;
2318 2268
 			}
@@ -2453,8 +2403,7 @@  discard block
 block discarded – undo
2453 2403
 			],
2454 2404
 			];
2455 2405
 			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
2456
-		}
2457
-		else {
2406
+		} else {
2458 2407
 			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops);
2459 2408
 		}
2460 2409
 
@@ -2546,8 +2495,7 @@  discard block
 block discarded – undo
2546 2495
 				if ($accessToFolder) {
2547 2496
 					$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
2548 2497
 				}
2549
-			}
2550
-			catch (MAPIException $e) {
2498
+			} catch (MAPIException $e) {
2551 2499
 				$e->setHandled();
2552 2500
 				$this->errorSetResource = 1; // No access
2553 2501
 			}
@@ -2573,8 +2521,7 @@  discard block
 block discarded – undo
2573 2521
 						 */
2574 2522
 						// $errorSetResource = 2;
2575 2523
 						$this->nonAcceptingResources[] = $resourceRecipients[$i];
2576
-					}
2577
-					else {
2524
+					} else {
2578 2525
 						if ($declineRecurringMeetingRequests && !$cancel) {
2579 2526
 							// Check if appointment is recurring
2580 2527
 							if ($messageprops[$this->proptags['recurring']]) {
@@ -2634,8 +2581,7 @@  discard block
 block discarded – undo
2634 2581
 					if (!$newResourceMsg) {
2635 2582
 						$newResourceMsg = mapi_folder_createmessage($calFolder);
2636 2583
 					}
2637
-				}
2638
-				else {
2584
+				} else {
2639 2585
 					$newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]);
2640 2586
 				}
2641 2587
 
@@ -2649,8 +2595,7 @@  discard block
 block discarded – undo
2649 2595
 				if ($cancel) {
2650 2596
 					$messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled
2651 2597
 					$messageprops[$this->proptags['busystatus']] = fbFree; // Free
2652
-				}
2653
-				else {
2598
+				} else {
2654 2599
 					$messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
2655 2600
 				}
2656 2601
 				$messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource automatically accepts the appointment
@@ -2694,8 +2639,7 @@  discard block
 block discarded – undo
2694 2639
 						$messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2695 2640
 						$messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2696 2641
 					}
2697
-				}
2698
-				else {
2642
+				} else {
2699 2643
 					// get organizer information
2700 2644
 					$addrinfo = $this->getOwnerAddress($this->store);
2701 2645
 
@@ -2731,12 +2675,10 @@  discard block
 block discarded – undo
2731 2675
 					// Update occurrence
2732 2676
 					if ($recurr->isException($basedate)) {
2733 2677
 						$recurr->modifyException($messageprops, $basedate, $recips);
2734
-					}
2735
-					else {
2678
+					} else {
2736 2679
 						$recurr->createException($messageprops, $basedate, false, $recips);
2737 2680
 					}
2738
-				}
2739
-				else {
2681
+				} else {
2740 2682
 					mapi_setprops($newResourceMsg, $messageprops);
2741 2683
 
2742 2684
 					// Copy attachments
@@ -2760,8 +2702,7 @@  discard block
 block discarded – undo
2760 2702
 					'msg' => $newResourceMsg,
2761 2703
 				];
2762 2704
 				$this->includesResources = true;
2763
-			}
2764
-			else {
2705
+			} else {
2765 2706
 				/*
2766 2707
 				 * If no other errors occurred and you have no access to the
2767 2708
 				 * folder of the resource, throw an error=1.
@@ -2837,8 +2778,7 @@  discard block
 block discarded – undo
2837 2778
 			],
2838 2779
 			];
2839 2780
 			$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
2840
-		}
2841
-		else {
2781
+		} else {
2842 2782
 			$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2843 2783
 		}
2844 2784
 
@@ -2856,13 +2796,11 @@  discard block
 block discarded – undo
2856 2796
 		if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
2857 2797
 			if ($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
2858 2798
 				$exception_props[$this->proptags['busystatus']] = fbTentative;
2859
-			}
2860
-			else {
2799
+			} else {
2861 2800
 				$exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
2862 2801
 			}
2863 2802
 			// we already have intendedbusystatus value in $exception_props so no need to copy it
2864
-		}
2865
-		else {
2803
+		} else {
2866 2804
 			$exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
2867 2805
 		}
2868 2806
 
@@ -2878,8 +2816,7 @@  discard block
 block discarded – undo
2878 2816
 
2879 2817
 		if ($recurr->isException($basedate)) {
2880 2818
 			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2881
-		}
2882
-		else {
2819
+		} else {
2883 2820
 			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2884 2821
 		}
2885 2822
 
@@ -2916,8 +2853,7 @@  discard block
 block discarded – undo
2916 2853
 
2917 2854
 		if ($recurr->isException($basedate)) {
2918 2855
 			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2919
-		}
2920
-		else {
2856
+		} else {
2921 2857
 			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2922 2858
 		}
2923 2859
 
@@ -3011,8 +2947,7 @@  discard block
 block discarded – undo
3011 2947
 
3012 2948
 			if (!$deletedRecips) {
3013 2949
 				$deletedRecips = array_merge([], $recipients);
3014
-			}
3015
-			else {
2950
+			} else {
3016 2951
 				$deletedRecips = array_merge($deletedRecips, $recipients);
3017 2952
 			}
3018 2953
 		}
@@ -3151,8 +3086,7 @@  discard block
 block discarded – undo
3151 3086
 				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3152 3087
 				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3153 3088
 				$newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3154
-			}
3155
-			else {
3089
+			} else {
3156 3090
 				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Request';
3157 3091
 				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
3158 3092
 			}
@@ -3320,8 +3254,7 @@  discard block
 block discarded – undo
3320 3254
 					$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3321 3255
 				}
3322 3256
 			}
3323
-		}
3324
-		else {
3257
+		} else {
3325 3258
 			// normal user is sending mail, so both set of properties will be same
3326 3259
 			$userDetails = $this->getOwnerAddress($userStore);
3327 3260
 
@@ -3487,8 +3420,7 @@  discard block
 block discarded – undo
3487 3420
 					$message = mapi_attach_openobj($attach, MAPI_MODIFY);
3488 3421
 				}
3489 3422
 			}
3490
-		}
3491
-		else {
3423
+		} else {
3492 3424
 			// use normal message or recurring series message
3493 3425
 			$message = $this->message;
3494 3426
 		}
@@ -3531,8 +3463,7 @@  discard block
 block discarded – undo
3531 3463
 			if (($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer) {
3532 3464
 				// Recipient is attendee, set the trackstatus to 'Not Responded'
3533 3465
 				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3534
-			}
3535
-			else {
3466
+			} else {
3536 3467
 				// Recipient is organizer, this is not possible, but for safety
3537 3468
 				// it is best to clear the trackstatus for him as well by setting
3538 3469
 				// the trackstatus to 'Organized'.
@@ -3570,8 +3501,7 @@  discard block
 block discarded – undo
3570 3501
 
3571 3502
 			$store = $delegatorStore['store'];
3572 3503
 			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3573
-		}
3574
-		else {
3504
+		} else {
3575 3505
 			$store = $this->store;
3576 3506
 			$calFolder = $this->openDefaultCalendar();
3577 3507
 		}
@@ -3633,8 +3563,7 @@  discard block
 block discarded – undo
3633 3563
 			if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3634 3564
 				$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3635 3565
 				$store = $delegatorStore['store'];
3636
-			}
3637
-			else {
3566
+			} else {
3638 3567
 				$store = $this->store;
3639 3568
 			}
3640 3569
 		}
@@ -3722,8 +3651,7 @@  discard block
 block discarded – undo
3722 3651
 									++$noOfInstances;
3723 3652
 									break;
3724 3653
 								}
3725
-							}
3726
-							else {
3654
+							} else {
3727 3655
 								++$noOfInstances;
3728 3656
 								break;
3729 3657
 							}
@@ -3734,8 +3662,7 @@  discard block
 block discarded – undo
3734 3662
 				if ($noOfInstances > 0) {
3735 3663
 					$returnValue = $noOfInstances;
3736 3664
 				}
3737
-			}
3738
-			else {
3665
+			} else {
3739 3666
 				// Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3740 3667
 				$items = getCalendarItems($userStore, $calFolder, $messageProps[$this->proptags['startdate']], $messageProps[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3741 3668
 
@@ -3756,8 +3683,7 @@  discard block
 block discarded – undo
3756 3683
 								$returnValue = true;
3757 3684
 								break;
3758 3685
 							}
3759
-						}
3760
-						else {
3686
+						} else {
3761 3687
 							$returnValue = true;
3762 3688
 							break;
3763 3689
 						}
@@ -3918,8 +3844,7 @@  discard block
 block discarded – undo
3918 3844
 		foreach ($localCategories as $key => $value) {
3919 3845
 			if ($recurrence->isException($key)) {
3920 3846
 				$recurrence->modifyException([$this->proptags['categories'] => $value], $key);
3921
-			}
3922
-			else {
3847
+			} else {
3923 3848
 				$recurrence->createException([$this->proptags['categories'] => $value], $key, false);
3924 3849
 			}
3925 3850
 			mapi_savechanges($message);
Please login to merge, or discard this patch.