Passed
Push — master ( 7a8062...ff661f )
by
unknown
13:06
created

class.meetingrequest.php (426 issues)

1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2005-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 */
7
8
class Meetingrequest {
9
	/*
10
	 * NOTE
11
	 *
12
	 * This class is designed to modify and update meeting request properties
13
	 * and to search for linked appointments in the calendar. It does not
14
	 * - set standard properties like subject or location
15
	 * - commit property changes through savechanges() (except in accept() and decline())
16
	 *
17
	 * To set all the other properties, just handle the item as any other appointment
18
	 * item. You aren't even required to set those properties before or after using
19
	 * this class. If you update properties before REsending a meeting request (ie with
20
	 * a time change) you MUST first call updateMeetingRequest() so the internal counters
21
	 * can be updated. You can then submit the message any way you like.
22
	 *
23
	 */
24
25
	/*
26
	 * How to use
27
	 * ----------
28
	 *
29
	 * Sending a meeting request:
30
	 * - Create appointment item as normal, but as 'tentative'
31
	 *   (this is the state of the item when the receiving user has received but
32
	 *    not accepted the item)
33
	 * - Set recipients as normally in e-mails
34
	 * - Create Meetingrequest class instance
35
	 * - Call checkCalendarWriteAccess(), to check for write permissions on calendar folder
36
	 * - Call setMeetingRequest(), this turns on all the meeting request properties in the
37
	 *   calendar item
38
	 * - Call sendMeetingRequest(), this sends a copy of the item with some extra properties
39
	 *
40
	 * Updating a meeting request:
41
	 * - Create Meetingrequest class instance
42
	 * - Call checkCalendarWriteAccess(), to check for write permissions on calendar folder
43
	 * - Call updateMeetingRequest(), this updates the counters
44
	 * - Call checkSignificantChanges(), this will check for significant changes and if needed will clear the
45
	 *   existing recipient responses
46
	 * - Call sendMeetingRequest()
47
	 *
48
	 * Clicking on a an e-mail:
49
	 * - Create Meetingrequest class instance
50
	 * - Check isMeetingRequest(), if true:
51
	 *   - Check isLocalOrganiser(), if true then ignore the message
52
	 *   - Check isInCalendar(), if not call doAccept(true, false, false). This adds the item in your
53
	 *     calendar as tentative without sending a response
54
	 *   - Show Accept, Tentative, Decline buttons
55
	 *   - When the user presses Accept, Tentative or Decline, call doAccept(false, true, true),
56
	 *     doAccept(true, true, true) or doDecline(true) respectively to really accept or decline and
57
	 *     send the response. This will remove the request from your inbox.
58
	 * - Check isMeetingRequestResponse, if true:
59
	 *   - Check isLocalOrganiser(), if not true then ignore the message
60
	 *   - Call processMeetingRequestResponse()
61
	 *     This will update the trackstatus of all recipients, and set the item to 'busy'
62
	 *     when all the recipients have accepted.
63
	 * - Check isMeetingCancellation(), if true:
64
	 *   - Check isLocalOrganiser(), if true then ignore the message
65
	 *   - Check isInCalendar(), if not, then ignore
66
	 *     Call processMeetingCancellation()
67
	 *   - Show 'Remove From Calendar' button to user
68
	 *   - When userpresses button, call doRemoveFromCalendar(), which removes the item from your
69
	 *     calendar and deletes the message
70
	 *
71
	 * Cancelling a meeting request:
72
	 *   - Call doCancelInvitation, which will send cancellation mails to attendees and will remove
73
	 *     meeting object from calendar
74
	 */
75
76
	// All properties for a recipient that are interesting
77
	public $recipprops = [
78
		PR_ENTRYID,
79
		PR_DISPLAY_NAME,
0 ignored issues
show
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
80
		PR_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
81
		PR_RECIPIENT_ENTRYID,
0 ignored issues
show
The constant PR_RECIPIENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
82
		PR_RECIPIENT_TYPE,
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
83
		PR_SEND_INTERNET_ENCODING,
0 ignored issues
show
The constant PR_SEND_INTERNET_ENCODING was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
84
		PR_SEND_RICH_INFO,
0 ignored issues
show
The constant PR_SEND_RICH_INFO was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
85
		PR_RECIPIENT_DISPLAY_NAME,
0 ignored issues
show
The constant PR_RECIPIENT_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
86
		PR_ADDRTYPE,
0 ignored issues
show
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
87
		PR_DISPLAY_TYPE,
0 ignored issues
show
The constant PR_DISPLAY_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
88
		PR_DISPLAY_TYPE_EX,
0 ignored issues
show
The constant PR_DISPLAY_TYPE_EX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
89
		PR_RECIPIENT_TRACKSTATUS,
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
90
		PR_RECIPIENT_TRACKSTATUS_TIME,
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
91
		PR_RECIPIENT_FLAGS,
0 ignored issues
show
The constant PR_RECIPIENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
92
		PR_ROWID,
0 ignored issues
show
The constant PR_ROWID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
93
		PR_OBJECT_TYPE,
0 ignored issues
show
The constant PR_OBJECT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
94
		PR_SEARCH_KEY,
0 ignored issues
show
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
95
	];
96
97
	/**
98
	 * Indication whether the setting of resources in a Meeting Request is success (false) or if it
99
	 * has failed (integer).
100
	 *
101
	 * @var null|false|int
102
	 *
103
	 * @psalm-var 1|3|4|false|null
104
	 */
105
	public $errorSetResource;
106
107
	public $proptags;
108
	private $store;
109
	public $message;
110
	private $session;
111
112
	/**
113
	 * @var false|string
114
	 */
115
	private $meetingTimeInfo;
116
	private $enableDirectBooking;
117
118
	/**
119
	 * @var null|bool
120
	 */
121
	private $includesResources;
122
	private $nonAcceptingResources;
123
	private $recipientDisplayname;
124
125
	/**
126
	 * Constructor.
127
	 *
128
	 * Takes a store and a message. The message is an appointment item
129
	 * that should be converted into a meeting request or an incoming
130
	 * e-mail message that is a meeting request.
131
	 *
132
	 * The $session variable is optional, but required if the following features
133
	 * are to be used:
134
	 *
135
	 * - Sending meeting requests for meetings that are not in your own store
136
	 * - Sending meeting requests to resources, resource availability checking and resource freebusy updates
137
	 *
138
	 * @param mixed $store
139
	 * @param mixed $message
140
	 * @param mixed $session
141
	 * @param mixed $enableDirectBooking
142
	 */
143
	public function __construct($store, $message, $session = false, $enableDirectBooking = true) {
144
		$this->store = $store;
145
		$this->message = $message;
146
		$this->session = $session;
147
		// This variable string saves time information for the MR.
148
		$this->meetingTimeInfo = false;
149
		$this->enableDirectBooking = $enableDirectBooking;
150
151
		$properties = [];
152
		$properties['goid'] = 'PT_BINARY:PSETID_Meeting:0x3';
153
		$properties['goid2'] = 'PT_BINARY:PSETID_Meeting:0x23';
154
		$properties['type'] = 'PT_STRING8:PSETID_Meeting:0x24';
155
		$properties['meetingrecurring'] = 'PT_BOOLEAN:PSETID_Meeting:0x5';
156
		$properties['unknown2'] = 'PT_BOOLEAN:PSETID_Meeting:0xa';
157
		$properties['attendee_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1';
158
		$properties['owner_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1a';
159
		$properties['meetingstatus'] = 'PT_LONG:PSETID_Appointment:' . PidLidAppointmentStateFlags;
0 ignored issues
show
The constant PidLidAppointmentStateFlags was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
160
		$properties['responsestatus'] = 'PT_LONG:PSETID_Appointment:0x8218';
161
		$properties['unknown6'] = 'PT_LONG:PSETID_Meeting:0x4';
162
		$properties['replytime'] = 'PT_SYSTIME:PSETID_Appointment:0x8220';
163
		$properties['usetnef'] = 'PT_BOOLEAN:PSETID_Common:0x8582';
164
		$properties['recurrence_data'] = 'PT_BINARY:PSETID_Appointment:' . PidLidAppointmentRecur;
0 ignored issues
show
The constant PidLidAppointmentRecur was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
165
		$properties['reminderminutes'] = 'PT_LONG:PSETID_Common:' . PidLidReminderDelta;
0 ignored issues
show
The constant PidLidReminderDelta was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
166
		$properties['reminderset'] = 'PT_BOOLEAN:PSETID_Common:' . PidLidReminderSet;
0 ignored issues
show
The constant PidLidReminderSet was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
167
		$properties['sendasical'] = 'PT_BOOLEAN:PSETID_Appointment:0x8200';
168
		$properties['updatecounter'] = 'PT_LONG:PSETID_Appointment:' . PidLidAppointmentSequence;					// AppointmentSequenceNumber
0 ignored issues
show
The constant PidLidAppointmentSequence was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
169
		$properties['unknown7'] = 'PT_LONG:PSETID_Appointment:0x8202';
170
		$properties['last_updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8203';			// AppointmentLastSequence
171
		$properties['busystatus'] = 'PT_LONG:PSETID_Appointment:' . PidLidBusyStatus;
0 ignored issues
show
The constant PidLidBusyStatus was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
172
		$properties['intendedbusystatus'] = 'PT_LONG:PSETID_Appointment:' . PidLidIntendedBusyStatus;
0 ignored issues
show
The constant PidLidIntendedBusyStatus was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
173
		$properties['start'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentStartWhole;
0 ignored issues
show
The constant PidLidAppointmentStartWhole was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
174
		$properties['responselocation'] = 'PT_STRING8:PSETID_Meeting:0x2';
175
		$properties['location'] = 'PT_STRING8:PSETID_Appointment:' . PidLidLocation;
0 ignored issues
show
The constant PidLidLocation was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
176
		$properties['requestsent'] = 'PT_BOOLEAN:PSETID_Appointment:0x8229';		// PidLidFInvited, MeetingRequestWasSent
177
		$properties['startdate'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentStartWhole;
178
		$properties['duedate'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentEndWhole;
0 ignored issues
show
The constant PidLidAppointmentEndWhole was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
179
		$properties['flagdueby'] = 'PT_SYSTIME:PSETID_Common:' . PidLidReminderSignalTime;
0 ignored issues
show
The constant PidLidReminderSignalTime was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
180
		$properties['commonstart'] = 'PT_SYSTIME:PSETID_Common:0x8516';
181
		$properties['commonend'] = 'PT_SYSTIME:PSETID_Common:0x8517';
182
		$properties['recurring'] = 'PT_BOOLEAN:PSETID_Appointment:' . PidLidRecurring;
0 ignored issues
show
The constant PidLidRecurring was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
183
		$properties['clipstart'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidClipStart;
0 ignored issues
show
The constant PidLidClipStart was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
184
		$properties['clipend'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidClipEnd;
0 ignored issues
show
The constant PidLidClipEnd was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
185
		$properties['start_recur_date'] = 'PT_LONG:PSETID_Meeting:0xD';				// StartRecurTime
186
		$properties['start_recur_time'] = 'PT_LONG:PSETID_Meeting:0xE';				// StartRecurTime
187
		$properties['end_recur_date'] = 'PT_LONG:PSETID_Meeting:0xF';				// EndRecurDate
188
		$properties['end_recur_time'] = 'PT_LONG:PSETID_Meeting:0x10';				// EndRecurTime
189
		$properties['is_exception'] = 'PT_BOOLEAN:PSETID_Meeting:0xA';				// LID_IS_EXCEPTION
190
		$properties['apptreplyname'] = 'PT_STRING8:PSETID_Appointment:0x8230';
191
		// Propose new time properties
192
		$properties['proposed_start_whole'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentProposedStartWhole;
0 ignored issues
show
The constant PidLidAppointmentProposedStartWhole was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
193
		$properties['proposed_end_whole'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentProposedEndWhole;
0 ignored issues
show
The constant PidLidAppointmentProposedEndWhole was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
194
		$properties['proposed_duration'] = 'PT_LONG:PSETID_Appointment:0x8256';
195
		$properties['counter_proposal'] = 'PT_BOOLEAN:PSETID_Appointment:' . PidLidAppointmentCounterProposal;
0 ignored issues
show
The constant PidLidAppointmentCounterProposal was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
196
		$properties['recurring_pattern'] = 'PT_STRING8:PSETID_Appointment:0x8232';
197
		$properties['basedate'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidExceptionReplaceTime;
0 ignored issues
show
The constant PidLidExceptionReplaceTime was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
198
		$properties['meetingtype'] = 'PT_LONG:PSETID_Meeting:0x26';
199
		$properties['timezone_data'] = 'PT_BINARY:PSETID_Appointment:' . PidLidTimeZoneStruct;
0 ignored issues
show
The constant PidLidTimeZoneStruct was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
200
		$properties['timezone'] = 'PT_STRING8:PSETID_Appointment:' . PidLidTimeZoneDescription;
0 ignored issues
show
The constant PidLidTimeZoneDescription was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
201
		$properties['categories'] = 'PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords';
202
		$properties['private'] = 'PT_BOOLEAN:PSETID_Common:' . PidLidPrivate;
0 ignored issues
show
The constant PidLidPrivate was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
203
		$properties['alldayevent'] = 'PT_BOOLEAN:PSETID_Appointment:' . PidLidAppointmentSubType;
0 ignored issues
show
The constant PidLidAppointmentSubType was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
204
		$properties['toattendeesstring'] = 'PT_STRING8:PSETID_Appointment:0x823B';
205
		$properties['ccattendeesstring'] = 'PT_STRING8:PSETID_Appointment:0x823C';
206
207
		$this->proptags = getPropIdsFromStrings($store, $properties);
208
	}
209
210
	/**
211
	 * Sets the direct booking property. This is an alternative to the setting of the direct booking
212
	 * property through the constructor. However, setting it in the constructor is preferred.
213
	 *
214
	 * @param bool $directBookingSetting
215
	 */
216
	public function setDirectBooking($directBookingSetting): void {
217
		$this->enableDirectBooking = $directBookingSetting;
218
	}
219
220
	/**
221
	 * Returns TRUE if the message pointed to is an incoming meeting request and should
222
	 * therefore be replied to with doAccept or doDecline().
223
	 *
224
	 * @param string $messageClass message class to use for checking
225
	 *
226
	 * @return bool returns true if this is a meeting request else false
227
	 */
228
	public function isMeetingRequest($messageClass = false) {
229
		if ($messageClass === false) {
230
			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

230
			$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
Loading history...
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
231
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
232
		}
233
234
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.request') === 0) {
235
			return true;
236
		}
237
238
		return false;
239
	}
240
241
	/**
242
	 * Returns TRUE if the message pointed to is a returning meeting request response.
243
	 *
244
	 * @param string $messageClass message class to use for checking
245
	 *
246
	 * @return bool returns true if this is a meeting request else false
247
	 */
248
	public function isMeetingRequestResponse($messageClass = false) {
249
		if ($messageClass === false) {
250
			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

250
			$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
Loading history...
251
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
252
		}
253
254
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.resp') === 0) {
255
			return true;
256
		}
257
258
		return false;
259
	}
260
261
	/**
262
	 * Returns TRUE if the message pointed to is a cancellation request.
263
	 *
264
	 * @param string $messageClass message class to use for checking
265
	 *
266
	 * @return bool returns true if this is a meeting request else false
267
	 */
268
	public function isMeetingCancellation($messageClass = false) {
269
		if ($messageClass === false) {
270
			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

270
			$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
Loading history...
271
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
272
		}
273
274
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.canceled') === 0) {
275
			return true;
276
		}
277
278
		return false;
279
	}
280
281
	/**
282
	 * Function is used to get the last update counter of meeting request.
283
	 *
284
	 * @return bool|int false when last_updatecounter not found else return last_updatecounter
285
	 */
286
	public function getLastUpdateCounter() {
287
		$calendarItemProps = mapi_getprops($this->message, [$this->proptags['last_updatecounter']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

287
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['last_updatecounter']]);
Loading history...
288
		if (isset($calendarItemProps) && !empty($calendarItemProps)) {
289
			return $calendarItemProps[$this->proptags['last_updatecounter']];
290
		}
291
292
		return false;
293
	}
294
295
	/**
296
	 * Process an incoming meeting request response. This updates the appointment
297
	 * in your calendar to show whether the user has accepted or declined.
298
	 */
299
	public function processMeetingRequestResponse() {
300
		if (!$this->isMeetingRequestResponse()) {
301
			return;
302
		}
303
304
		if (!$this->isLocalOrganiser()) {
305
			return;
306
		}
307
308
		// Get information we need from the response message
309
		$messageprops = mapi_getprops($this->message, [
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

309
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [
Loading history...
310
			$this->proptags['goid'],
311
			$this->proptags['goid2'],
312
			PR_OWNER_APPT_ID,
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
313
			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
314
			PR_SENT_REPRESENTING_NAME,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
315
			PR_SENT_REPRESENTING_ADDRTYPE,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
316
			PR_SENT_REPRESENTING_ENTRYID,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
317
			PR_SENT_REPRESENTING_SEARCH_KEY,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
318
			PR_MESSAGE_DELIVERY_TIME,
0 ignored issues
show
The constant PR_MESSAGE_DELIVERY_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
319
			PR_MESSAGE_CLASS,
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
320
			PR_PROCESSED,
0 ignored issues
show
The constant PR_PROCESSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
321
			PR_RCVD_REPRESENTING_ENTRYID,
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
322
			$this->proptags['proposed_start_whole'],
323
			$this->proptags['proposed_end_whole'],
324
			$this->proptags['proposed_duration'],
325
			$this->proptags['counter_proposal'],
326
			$this->proptags['attendee_critical_change'],
327
		]);
328
329
		$goid2 = $messageprops[$this->proptags['goid2']];
330
331
		if (!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS])) {
332
			return;
333
		}
334
335
		// Find basedate in GlobalID(0x3), this can be a response for an occurrence
336
		$basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
337
338
		// check if delegate is processing the response
339
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
340
			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
341
			$userStore = $delegatorStore['store'];
342
		}
343
		else {
344
			$userStore = $this->store;
345
		}
346
347
		// check for calendar access
348
		if ($this->checkCalendarWriteAccess($userStore) !== true) {
349
			// Throw an exception that we don't have write permissions on calendar folder,
350
			// allow caller to fill the error message
351
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
352
		}
353
354
		$calendarItem = $this->getCorrespondentCalendarItem(true);
355
356
		// Open the calendar items, and update all the recipients of the calendar item that match
357
		// the email address of the response.
358
		if ($calendarItem !== false) {
359
			$this->processResponse($userStore, $calendarItem, $basedate, $messageprops);
360
		}
361
	}
362
363
	/**
364
	 * Process every incoming MeetingRequest response.This updates the appointment
365
	 * in your calendar to show whether the user has accepted or declined.
366
	 *
367
	 * @param resource $store        contains the userStore in which the meeting is created
368
	 * @param mixed    $calendarItem resource of the calendar item for which this response has arrived
369
	 * @param mixed    $basedate     if present the create an exception
370
	 * @param array    $messageprops contains message properties
371
	 *
372
	 * @return null|false
373
	 */
374
	public function processResponse($store, $calendarItem, $basedate, $messageprops) {
375
		$senderentryid = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
376
		$messageclass = $messageprops[PR_MESSAGE_CLASS];
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
377
		$deliverytime = $messageprops[PR_MESSAGE_DELIVERY_TIME];
0 ignored issues
show
The constant PR_MESSAGE_DELIVERY_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
378
379
		// Open the calendar item, find the sender in the recipient table and update all the recipients of the calendar item that match
380
		// the email address of the response.
381
		$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

381
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($calendarItem, [$this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter']]);
Loading history...
The constant PR_STORE_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
382
383
		// check if meeting response is already processed
384
		if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
0 ignored issues
show
The constant PR_PROCESSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
385
			// meeting is already processed
386
			return;
387
		}
388
		mapi_setprops($this->message, [PR_PROCESSED => true]);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

388
		/** @scrutinizer ignore-call */ 
389
  mapi_setprops($this->message, [PR_PROCESSED => true]);
Loading history...
389
		mapi_savechanges($this->message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

389
		/** @scrutinizer ignore-call */ 
390
  mapi_savechanges($this->message);
Loading history...
390
391
		// if meeting is updated in organizer's calendar then we don't need to process
392
		// old response
393
		if ($this->isMeetingUpdated($basedate)) {
394
			return;
395
		}
396
397
		// If basedate is found, then create/modify exception msg and do processing
398
		if ($basedate && isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] === true) {
399
			$recurr = new Recurrence($store, $calendarItem);
400
401
			// Copy properties from meeting request
402
			$exception_props = mapi_getprops($this->message, [
403
				PR_OWNER_APPT_ID,
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
404
				$this->proptags['proposed_start_whole'],
405
				$this->proptags['proposed_end_whole'],
406
				$this->proptags['proposed_duration'],
407
				$this->proptags['counter_proposal'],
408
			]);
409
410
			// Create/modify exception
411
			if ($recurr->isException($basedate)) {
412
				$recurr->modifyException($exception_props, $basedate);
413
			}
414
			else {
415
				// When we are creating an exception we need copy recipients from main recurring item
416
				$recipTable = mapi_message_getrecipienttable($calendarItem);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

416
				$recipTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($calendarItem);
Loading history...
417
				$recips = mapi_table_queryallrows($recipTable, $this->recipprops);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

417
				$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipTable, $this->recipprops);
Loading history...
418
419
				// Retrieve actual start/due dates from calendar item.
420
				$exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
421
				$exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
422
423
				$recurr->createException($exception_props, $basedate, false, $recips);
424
			}
425
426
			mapi_savechanges($calendarItem);
427
428
			$attach = $recurr->getExceptionAttachment($basedate);
429
			if ($attach) {
430
				$recurringItem = $calendarItem;
431
				$calendarItem = mapi_attach_openobj($attach, MAPI_MODIFY);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

431
				$calendarItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
432
			}
433
			else {
434
				return false;
435
			}
436
		}
437
438
		// Get the recipients of the calendar item
439
		$reciptable = mapi_message_getrecipienttable($calendarItem);
440
		$recipients = mapi_table_queryallrows($reciptable, $this->recipprops);
441
442
		// FIXME we should look at the updatecounter property and compare it
443
		// to the counter in the recipient to see if this update is actually
444
		// newer than the status in the calendar item
445
		$found = false;
446
447
		$totalrecips = 0;
448
		$acceptedrecips = 0;
449
		foreach ($recipients as $recipient) {
450
			++$totalrecips;
451
			if (isset($recipient[PR_ENTRYID]) && $this->compareABEntryIDs($recipient[PR_ENTRYID], $senderentryid)) {
452
				$found = true;
453
454
				/*
455
				 * If value of attendee_critical_change on meeting response mail is less than PR_RECIPIENT_TRACKSTATUS_TIME
456
				 * on the corresponding recipientRow of meeting then we ignore this response mail.
457
				 */
458
				if (isset($recipient[PR_RECIPIENT_TRACKSTATUS_TIME]) && ($messageprops[$this->proptags['attendee_critical_change']] < $recipient[PR_RECIPIENT_TRACKSTATUS_TIME])) {
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
459
					continue;
460
				}
461
462
				// The email address matches, update the row
463
				$recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
464
				if (isset($messageprops[$this->proptags['attendee_critical_change']])) {
465
					$recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $messageprops[$this->proptags['attendee_critical_change']];
466
				}
467
468
				// If this is a counter proposal, set the proposal properties in the recipient row
469
				if (isset($messageprops[$this->proptags['counter_proposal']]) && $messageprops[$this->proptags['counter_proposal']]) {
470
					$recipient[PR_RECIPIENT_PROPOSEDSTARTTIME] = $messageprops[$this->proptags['proposed_start_whole']];
0 ignored issues
show
The constant PR_RECIPIENT_PROPOSEDSTARTTIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
471
					$recipient[PR_RECIPIENT_PROPOSEDENDTIME] = $messageprops[$this->proptags['proposed_end_whole']];
0 ignored issues
show
The constant PR_RECIPIENT_PROPOSEDENDTIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
472
					$recipient[PR_RECIPIENT_PROPOSED] = $messageprops[$this->proptags['counter_proposal']];
0 ignored issues
show
The constant PR_RECIPIENT_PROPOSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
473
				}
474
475
				// Update the recipient information
476
				mapi_message_modifyrecipients($calendarItem, MODRECIP_REMOVE, [$recipient]);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

476
				/** @scrutinizer ignore-call */ 
477
    mapi_message_modifyrecipients($calendarItem, MODRECIP_REMOVE, [$recipient]);
Loading history...
477
				mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
478
			}
479
			if (isset($recipient[PR_RECIPIENT_TRACKSTATUS]) && $recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
480
				++$acceptedrecips;
481
			}
482
		}
483
484
		// If the recipient was not found in the original calendar item,
485
		// then add the recpient as a new optional recipient
486
		if (!$found) {
487
			$recipient = [];
488
			$recipient[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
489
			$recipient[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
490
			$recipient[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
491
			$recipient[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
492
			$recipient[PR_RECIPIENT_TYPE] = MAPI_CC;
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
493
			$recipient[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
0 ignored issues
show
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
494
			$recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass);
495
			$recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $deliverytime;
496
497
			// If this is a counter proposal, set the proposal properties in the recipient row
498
			if (isset($messageprops[$this->proptags['counter_proposal']])) {
499
				$recipient[PR_RECIPIENT_PROPOSEDSTARTTIME] = $messageprops[$this->proptags['proposed_start_whole']];
500
				$recipient[PR_RECIPIENT_PROPOSEDENDTIME] = $messageprops[$this->proptags['proposed_end_whole']];
501
				$recipient[PR_RECIPIENT_PROPOSED] = $messageprops[$this->proptags['counter_proposal']];
502
			}
503
504
			mapi_message_modifyrecipients($calendarItem, MODRECIP_ADD, [$recipient]);
505
			++$totalrecips;
506
			if ($recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) {
507
				++$acceptedrecips;
508
			}
509
		}
510
511
		// TODO: Update counter proposal number property on message
512
		/*
513
		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.
514
		*/
515
		// If this is a counter proposal, set the counter proposal indicator boolean
516
		if (isset($messageprops[$this->proptags['counter_proposal']])) {
517
			$props = [];
518
			if ($messageprops[$this->proptags['counter_proposal']]) {
519
				$props[$this->proptags['counter_proposal']] = true;
520
			}
521
			else {
522
				$props[$this->proptags['counter_proposal']] = false;
523
			}
524
525
			mapi_setprops($calendarItem, $props);
526
		}
527
528
		mapi_savechanges($calendarItem);
529
		if (isset($attach)) {
530
			mapi_savechanges($attach);
531
			mapi_savechanges($recurringItem);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $recurringItem does not seem to be defined for all execution paths leading up to this point.
Loading history...
532
		}
533
	}
534
535
	/**
536
	 * Process an incoming meeting request cancellation. This updates the
537
	 * appointment in your calendar to show that the meeting has been cancelled.
538
	 */
539
	public function processMeetingCancellation() {
540
		if (!$this->isMeetingCancellation()) {
541
			return;
542
		}
543
544
		if ($this->isLocalOrganiser()) {
545
			return;
546
		}
547
548
		if (!$this->isInCalendar()) {
549
			return;
550
		}
551
552
		$listProperties = $this->proptags;
553
		$listProperties['subject'] = PR_SUBJECT;
0 ignored issues
show
The constant PR_SUBJECT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
554
		$listProperties['sent_representing_name'] = PR_SENT_REPRESENTING_NAME;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
555
		$listProperties['sent_representing_address_type'] = PR_SENT_REPRESENTING_ADDRTYPE;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
556
		$listProperties['sent_representing_email_address'] = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
557
		$listProperties['sent_representing_entryid'] = PR_SENT_REPRESENTING_ENTRYID;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
558
		$listProperties['sent_representing_search_key'] = PR_SENT_REPRESENTING_SEARCH_KEY;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
559
		$listProperties['rcvd_representing_name'] = PR_RCVD_REPRESENTING_NAME;
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
560
		$listProperties['rcvd_representing_address_type'] = PR_RCVD_REPRESENTING_ADDRTYPE;
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
561
		$listProperties['rcvd_representing_email_address'] = PR_RCVD_REPRESENTING_EMAIL_ADDRESS;
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
562
		$listProperties['rcvd_representing_entryid'] = PR_RCVD_REPRESENTING_ENTRYID;
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
563
		$listProperties['rcvd_representing_search_key'] = PR_RCVD_REPRESENTING_SEARCH_KEY;
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
564
		$messageProps = mapi_getprops($this->message, $listProperties);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

564
		$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, $listProperties);
Loading history...
565
566
		$goid = $messageProps[$this->proptags['goid']];	// GlobalID (0x3)
567
		if (!isset($goid)) {
568
			return;
569
		}
570
571
		// get delegator store, if delegate is processing this cancellation
572
		if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
573
			$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
574
575
			$store = $delegatorStore['store'];
576
		}
577
		else {
578
			$store = $this->store;
579
		}
580
581
		// check for calendar access
582
		if ($this->checkCalendarWriteAccess($store) !== true) {
583
			// Throw an exception that we don't have write permissions on calendar folder,
584
			// allow caller to fill the error message
585
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
586
		}
587
588
		$calendarItem = $this->getCorrespondentCalendarItem(true);
589
		$basedate = $this->getBasedateFromGlobalID($goid);
590
591
		if ($calendarItem !== false) {
592
			// if basedate is provided and we could not find the item then it could be that we are processing
593
			// an exception so get the exception and process it
594
			if ($basedate) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $basedate of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
595
				$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring']]);
596
				if ($calendarItemProps[$this->proptags['recurring']] === true) {
597
					$recurr = new Recurrence($store, $calendarItem);
598
599
					// Set message class
600
					$messageProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
601
602
					if ($recurr->isException($basedate)) {
603
						$recurr->modifyException($messageProps, $basedate);
604
					}
605
					else {
606
						$recurr->createException($messageProps, $basedate);
607
					}
608
				}
609
			}
610
			else {
611
				// set the properties of the cancellation object
612
				mapi_setprops($calendarItem, $messageProps);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

612
				/** @scrutinizer ignore-call */ 
613
    mapi_setprops($calendarItem, $messageProps);
Loading history...
613
			}
614
615
			mapi_savechanges($calendarItem);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

615
			/** @scrutinizer ignore-call */ 
616
   mapi_savechanges($calendarItem);
Loading history...
616
		}
617
	}
618
619
	/**
620
	 * Returns true if the corresponding calendar items exists in the celendar folder for this
621
	 * meeting request/response/cancellation.
622
	 */
623
	public function isInCalendar(): bool {
624
		// @TODO check for deleted exceptions
625
		return $this->getCorrespondentCalendarItem(false) !== false;
626
	}
627
628
	/**
629
	 * Accepts the meeting request by moving the item to the calendar
630
	 * and sending a confirmation message back to the sender. If $tentative
631
	 * is TRUE, then the item is accepted tentatively. After accepting, you
632
	 * can't use this class instance any more. The message is closed. If you
633
	 * specify TRUE for 'move', then the item is actually moved (from your
634
	 * inbox probably) to the calendar. If you don't, it is copied into
635
	 * your calendar.
636
	 *
637
	 * @param bool  $tentative            true if user as tentative accepted the meeting
638
	 * @param bool  $sendresponse         true if a response has to be sent to organizer
639
	 * @param bool  $move                 true if the meeting request should be moved to the deleted items after processing
640
	 * @param mixed $newProposedStartTime contains starttime if user has proposed other time
641
	 * @param mixed $newProposedEndTime   contains endtime if user has proposed other time
642
	 * @param mixed $body
643
	 * @param mixed $userAction
644
	 * @param mixed $store
645
	 * @param mixed $basedate             start of day of occurrence for which user has accepted the recurrent meeting
646
	 * @param bool  $isImported           true to indicate that MR is imported from .ics or .vcs file else it false.
647
	 *
648
	 * @return bool|string $entryid entryid of item which created/updated in calendar
649
	 */
650
	public function doAccept($tentative, $sendresponse, $move, $newProposedStartTime = false, $newProposedEndTime = false, $body = false, $userAction = false, $store = false, $basedate = false, $isImported = false) {
651
		if ($this->isLocalOrganiser()) {
652
			return false;
653
		}
654
655
		// Remove any previous calendar items with this goid and appt id
656
		$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]);
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENDER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RECEIVED_BY_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

656
		$messageprops = /** @scrutinizer ignore-call */ 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]);
Loading history...
The constant PR_PROCESSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
657
658
		// If this meeting request is received by a delegate then open delegator's store.
659
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
660
			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
661
662
			$store = $delegatorStore['store'];
663
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
664
		}
665
		else {
666
			$calFolder = $this->openDefaultCalendar();
667
			$store = $this->store;
668
		}
669
670
		// check for calendar access
671
		if ($this->checkCalendarWriteAccess($store) !== true) {
672
			// Throw an exception that we don't have write permissions on calendar folder,
673
			// allow caller to fill the error message
674
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
675
		}
676
677
		// if meeting is out dated then don't process it
678
		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $this->isMeetingOutOfDate()) {
679
			return false;
680
		}
681
682
		/*
683
		 *	if this function is called automatically with meeting request object then there will be
684
		 *	two possibilitites
685
		 *	1) meeting request is opened first time, in this case make a tentative appointment in
686
		 *		recipient's calendar
687
		 *	2) after this every subsequent request to open meeting request will not do any processing
688
		 */
689
		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction == false) {
690
			if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
691
				// if meeting request is already processed then don't do anything
692
				return false;
693
			}
694
695
			// if correspondent calendar item is already processed then don't do anything
696
			$calendarItem = $this->getCorrespondentCalendarItem();
697
			if ($calendarItem) {
698
				$calendarItemProps = mapi_getprops($calendarItem, [PR_PROCESSED]);
699
				if (isset($calendarItemProps[PR_PROCESSED]) && $calendarItemProps[PR_PROCESSED] == true) {
700
					// mark meeting-request mail as processed as well
701
					mapi_setprops($this->message, [PR_PROCESSED => true]);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

701
					/** @scrutinizer ignore-call */ 
702
     mapi_setprops($this->message, [PR_PROCESSED => true]);
Loading history...
702
					mapi_savechanges($this->message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

702
					/** @scrutinizer ignore-call */ 
703
     mapi_savechanges($this->message);
Loading history...
703
704
					return false;
705
				}
706
			}
707
		}
708
709
		// Retrieve basedate from globalID, if it is not received as argument
710
		if (!$basedate) {
711
			$basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]);
712
		}
713
714
		// set counter proposal properties in calendar item when proposing new time
715
		$proposeNewTimeProps = [];
716
		if ($newProposedStartTime && $newProposedEndTime) {
717
			$proposeNewTimeProps[$this->proptags['proposed_start_whole']] = $newProposedStartTime;
718
			$proposeNewTimeProps[$this->proptags['proposed_end_whole']] = $newProposedEndTime;
719
			$proposeNewTimeProps[$this->proptags['proposed_duration']] = round($newProposedEndTime - $newProposedStartTime) / 60;
720
			$proposeNewTimeProps[$this->proptags['counter_proposal']] = true;
721
		}
722
723
		// While sender is receiver then we have to process the meeting request as per the intended busy status
724
		// instead of tentative, and accept the same as per the intended busystatus.
725
		$senderEntryId = isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) ? $messageprops[PR_SENT_REPRESENTING_ENTRYID] : $messageprops[PR_SENDER_ENTRYID];
726
		if (isset($messageprops[PR_RECEIVED_BY_ENTRYID]) && compareEntryIds($senderEntryId, $messageprops[PR_RECEIVED_BY_ENTRYID])) {
727
			$entryid = $this->accept(false, $sendresponse, $move, $proposeNewTimeProps, $body, true, $store, $calFolder, $basedate);
728
		}
729
		else {
730
			$entryid = $this->accept($tentative, $sendresponse, $move, $proposeNewTimeProps, $body, $userAction, $store, $calFolder, $basedate);
731
		}
732
733
		// if we have first time processed this meeting then set PR_PROCESSED property
734
		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS]) && $userAction === false && $isImported === false) {
735
			if (!isset($messageprops[PR_PROCESSED]) || $messageprops[PR_PROCESSED] != true) {
736
				// set processed flag
737
				mapi_setprops($this->message, [PR_PROCESSED => true]);
738
				mapi_savechanges($this->message);
739
			}
740
		}
741
742
		return $entryid;
743
	}
744
745
	/**
746
	 * @param (float|mixed|true)[] $proposeNewTimeProps
747
	 * @param resource $calFolder
748
	 * @param mixed    $body
749
	 * @param mixed    $store
750
	 * @param mixed    $basedate
751
	 *
752
	 * @psalm-param array<float|mixed|true> $proposeNewTimeProps
753
	 */
754
	public function accept(bool $tentative, bool $sendresponse, bool $move, array $proposeNewTimeProps, $body, bool $userAction, $store, $calFolder, $basedate = false) {
755
		$messageprops = mapi_getprops($this->message);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

755
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message);
Loading history...
756
		$isDelegate = isset($messageprops[PR_RCVD_REPRESENTING_NAME]);
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
757
758
		if ($sendresponse) {
759
			$this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
760
		}
761
762
		/*
763
		 * Further processing depends on what user is receiving. User can receive recurring item, a single occurrence or a normal meeting.
764
		 * 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.
765
		 * 2) If single occurrence then find occurrence itself using globalID and if item is not found then use cleanGlobalID to find main recurring item
766
		 * 3) Normal meeting req are handled normally as they were handled previously.
767
		 *
768
		 * Also user can respond(accept/decline) to item either from previewpane or from calendar by opening the item. If user is responding the meeting from previewpane
769
		 * 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.
770
		 * If user is responding from calendar then item is opened and properties are set such as meetingstatus, responsestatus, busystatus etc.
771
		 */
772
		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
773
			// While processing the item mark it as read.
774
			mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT);
0 ignored issues
show
The function mapi_message_setreadflag was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

774
			/** @scrutinizer ignore-call */ 
775
   mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT);
Loading history...
775
776
			// This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
777
			if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] == true) {
778
				$calendarItem = false;
779
780
				// Find main recurring item based on GlobalID (0x3)
781
				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
782
				if (is_array($items)) {
783
					foreach ($items as $key => $entryid) {
784
						$calendarItem = mapi_msgstore_openentry($store, $entryid);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

784
						$calendarItem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
785
					}
786
				}
787
788
				$processed = false;
789
				if (!$calendarItem) {
790
					// Recurring item not found, so create new meeting in Calendar
791
					$calendarItem = mapi_folder_createmessage($calFolder);
0 ignored issues
show
The function mapi_folder_createmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

791
					$calendarItem = /** @scrutinizer ignore-call */ mapi_folder_createmessage($calFolder);
Loading history...
792
				}
793
				else {
794
					// we have found the main recurring item, check if this meeting request is already processed
795
					if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) {
0 ignored issues
show
The constant PR_PROCESSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
796
						// only set required properties, other properties are already copied when processing this meeting request
797
						// for the first time
798
						$processed = true;
799
					}
800
				}
801
802
				if (!$processed) {
803
					// get all the properties and copy that to calendar item
804
					$props = mapi_getprops($this->message);
805
					// reset the PidLidMeetingType to Unspecified for outlook display the item
806
					$props[$this->proptags['meetingtype']] = mtgEmpty;
807
					/*
808
					 * the client which has sent this meeting request can generate wrong flagdueby
809
					 * time (mainly OL), so regenerate that property so we will always show reminder
810
					 * on right time
811
					 */
812
					if (isset($props[$this->proptags['reminderminutes']])) {
813
						$props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
814
					}
815
				}
816
				else {
817
					// only get required properties so we will not overwrite existing updated properties from calendar
818
					$props = mapi_getprops($this->message, [PR_ENTRYID]);
819
				}
820
821
				// While we applying updates of MR then all local categories will be removed,
822
				// So get the local categories of all occurrence before applying update from organiser.
823
				$localCategories = $this->getLocalCategories($calendarItem, $store, $calFolder);
824
825
				$props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
826
				// When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
827
				if (!isset($props[$this->proptags['updatecounter']])) {
828
					$props[$this->proptags['updatecounter']] = 0;
829
				}
830
				$props[$this->proptags['meetingstatus']] = olMeetingReceived;
831
				// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
832
				$props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
833
834
				if (isset($props[$this->proptags['intendedbusystatus']])) {
835
					if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
836
						$props[$this->proptags['busystatus']] = fbTentative;
837
					}
838
					else {
839
						$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
840
					}
841
				// we already have intendedbusystatus value in $props so no need to copy it
842
				}
843
				else {
844
					$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
845
				}
846
847
				if ($userAction) {
848
					$addrInfo = $this->getOwnerAddress($this->store);
849
850
					// if user has responded then set replytime and name
851
					$props[$this->proptags['replytime']] = time();
852
					if (!empty($addrInfo)) {
853
						// @FIXME conditionally set this property only for delegation case
854
						$props[$this->proptags['apptreplyname']] = $addrInfo[0];
855
					}
856
				}
857
858
				mapi_setprops($calendarItem, $props);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

858
				/** @scrutinizer ignore-call */ 
859
    mapi_setprops($calendarItem, $props);
Loading history...
859
860
				// we have already processed attachments and recipients, so no need to do it again
861
				if (!$processed) {
862
					// Copy attachments too
863
					$this->replaceAttachments($this->message, $calendarItem);
864
					// Copy recipients too
865
					$this->replaceRecipients($this->message, $calendarItem, $isDelegate);
866
				}
867
868
				// Find all occurrences based on CleanGlobalID (0x23)
869
				// there will be no exceptions left if $processed is true, but even if it doesn't hurt to recheck
870
				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
871
				if (is_array($items)) {
872
					// Save all existing occurrence as exceptions
873
					foreach ($items as $entryid) {
874
						// Open occurrence
875
						$occurrenceItem = mapi_msgstore_openentry($store, $entryid);
876
877
						// Save occurrence into main recurring item as exception
878
						if ($occurrenceItem) {
879
							$occurrenceItemProps = mapi_getprops($occurrenceItem, [$this->proptags['goid'], $this->proptags['recurring']]);
880
881
							// Find basedate of occurrence item
882
							$basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]);
883
							if ($basedate && $occurrenceItemProps[$this->proptags['recurring']] != true) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $basedate of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
884
								$this->mergeException($calendarItem, $occurrenceItem, $basedate, $store);
885
							}
886
						}
887
					}
888
				}
889
890
				mapi_savechanges($calendarItem);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

890
				/** @scrutinizer ignore-call */ 
891
    mapi_savechanges($calendarItem);
Loading history...
891
892
				// After applying update of organiser all local categories of occurrence was removed,
893
				// So if local categories exist then apply it on respective occurrence.
894
				if (!empty($localCategories)) {
895
					$this->applyLocalCategories($calendarItem, $store, $localCategories);
896
				}
897
898
				if ($move) {
899
					// open wastebasket of currently logged in user and move the meeting request to it
900
					// for delegates this will be delegate's wastebasket folder
901
					$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
902
					mapi_folder_copymessages($calFolder, [$props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

902
					/** @scrutinizer ignore-call */ 
903
     mapi_folder_copymessages($calFolder, [$props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
903
				}
904
905
				$entryid = $props[PR_ENTRYID];
906
			}
907
			else {
908
				/**
909
				 * This meeting request is not recurring, so can be an exception or normal meeting.
910
				 * If exception then find main recurring item and update exception
911
				 * If main recurring item is not found then put exception into Calendar as normal meeting.
912
				 */
913
				$calendarItem = false;
914
915
				// We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence.
916
				if ($basedate) {
917
					// Find main recurring item from CleanGlobalID of this meeting request
918
					$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
919
					if (is_array($items)) {
920
						foreach ($items as $key => $entryid) {
921
							$calendarItem = mapi_msgstore_openentry($store, $entryid);
922
						}
923
					}
924
925
					// Main recurring item is found, so now update exception
926
					if ($calendarItem) {
927
						$this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
928
						$calendarItemProps = mapi_getprops($calendarItem, [PR_ENTRYID]);
929
						$entryid = $calendarItemProps[PR_ENTRYID];
930
					}
931
				}
932
933
				if (!$calendarItem) {
934
					$items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
935
					if (is_array($items)) {
936
						// Get local categories before deleting MR.
937
						$message = mapi_msgstore_openentry($store, $items[0]);
938
						$localCategories = mapi_getprops($message, [$this->proptags['categories']]);
939
						mapi_folder_deletemessages($calFolder, $items);
0 ignored issues
show
The function mapi_folder_deletemessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

939
						/** @scrutinizer ignore-call */ 
940
      mapi_folder_deletemessages($calFolder, $items);
Loading history...
940
					}
941
942
					if ($move) {
943
						// All we have to do is open the default calendar,
944
						// set the message class correctly to be an appointment item
945
						// and move it to the calendar folder
946
						$sourcefolder = $this->openParentFolder();
947
948
						// create a new calendar message, and copy the message to there,
949
						// since we want to delete (move to wastebasket) the original message
950
						$old_entryid = mapi_getprops($this->message, [PR_ENTRYID]);
951
						$calmsg = mapi_folder_createmessage($calFolder);
952
						mapi_copyto($this->message, [], [], $calmsg); /* includes attachments and recipients */
0 ignored issues
show
The function mapi_copyto was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

952
						/** @scrutinizer ignore-call */ 
953
      mapi_copyto($this->message, [], [], $calmsg); /* includes attachments and recipients */
Loading history...
953
						// reset the PidLidMeetingType to Unspecified for outlook display the item
954
						$tmp_props = [];
955
						$tmp_props[$this->proptags['meetingtype']] = mtgEmpty;
956
						// OL needs this field always being set, or it will not display item
957
						$tmp_props[$this->proptags['recurring']] = false;
958
						mapi_setprops($calmsg, $tmp_props);
959
960
						// After creating new MR, If local categories exist then apply it on new MR.
961
						if (!empty($localCategories)) {
962
							mapi_setprops($calmsg, $localCategories);
963
						}
964
965
						$calItemProps = [];
966
						$calItemProps[PR_MESSAGE_CLASS] = 'IPM.Appointment';
967
968
						/*
969
						 * the client which has sent this meeting request can generate wrong flagdueby
970
						 * time (mainly OL), so regenerate that property so we will always show reminder
971
						 * on right time
972
						 */
973
						if (isset($messageprops[$this->proptags['reminderminutes']])) {
974
							$calItemProps[$this->proptags['flagdueby']] = $messageprops[$this->proptags['startdate']] - ($messageprops[$this->proptags['reminderminutes']] * 60);
975
						}
976
977
						if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
978
							if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
979
								$calItemProps[$this->proptags['busystatus']] = fbTentative;
980
							}
981
							else {
982
								$calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
983
							}
984
							$calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
985
						}
986
						else {
987
							$calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
988
						}
989
990
						// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
991
						$calItemProps[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
992
						if ($userAction) {
993
							$addrInfo = $this->getOwnerAddress($this->store);
994
995
							// if user has responded then set replytime and name
996
							$calItemProps[$this->proptags['replytime']] = time();
997
							if (!empty($addrInfo)) {
998
								$calItemProps[$this->proptags['apptreplyname']] = $addrInfo[0];
999
							}
1000
						}
1001
1002
						$calItemProps[$this->proptags['recurring_pattern']] = '';
1003
						$calItemProps[$this->proptags['alldayevent']] = $calItemProps[$this->proptags['alldayevent']] ?? false;
1004
						$calItemProps[$this->proptags['private']] = $calItemProps[$this->proptags['private']] ?? false;
1005
						$calItemProps[$this->proptags['meetingstatus']] = $calItemProps[$this->proptags['meetingstatus']] ?? olMeetingReceived;
1006
						if (isset($calItemProps[$this->proptags['startdate']])) {
1007
							$calItemProps[$this->proptags['commonstart']] = $calItemProps[$this->proptags['startdate']];
1008
						}
1009
						if (isset($calItemProps[$this->proptags['duedate']])) {
1010
							$calItemProps[$this->proptags['commonend']] = $calItemProps[$this->proptags['duedate']];
1011
						}
1012
1013
						mapi_setprops($calmsg, $proposeNewTimeProps + $calItemProps);
1014
1015
						// get properties which stores owner information in meeting request mails
1016
						$props = mapi_getprops($calmsg, [
1017
							PR_SENT_REPRESENTING_ENTRYID,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1018
							PR_SENT_REPRESENTING_NAME,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1019
							PR_SENT_REPRESENTING_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1020
							PR_SENT_REPRESENTING_ADDRTYPE,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1021
							PR_SENT_REPRESENTING_SEARCH_KEY,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1022
						]);
1023
1024
						// add owner to recipient table
1025
						$recips = [];
1026
						$this->addOrganizer($props, $recips);
1027
						mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1027
						/** @scrutinizer ignore-call */ 
1028
      mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips);
Loading history...
1028
						mapi_savechanges($calmsg);
1029
1030
						// Move the message to the wastebasket
1031
						$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1032
						mapi_folder_copymessages($sourcefolder, [$old_entryid[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1033
1034
						$messageprops = mapi_getprops($calmsg, [PR_ENTRYID]);
1035
						$entryid = $messageprops[PR_ENTRYID];
1036
					}
1037
					else {
1038
						// Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment
1039
						$new = mapi_folder_createmessage($calFolder);
1040
						$props = mapi_getprops($this->message);
1041
1042
						$props[$this->proptags['recurring_pattern']] = '';
1043
						$props[$this->proptags['alldayevent']] = $props[$this->proptags['alldayevent']] ?? false;
1044
						$props[$this->proptags['private']] = $props[$this->proptags['private']] ?? false;
1045
						$props[$this->proptags['meetingstatus']] = $props[$this->proptags['meetingstatus']] ?? olMeetingReceived;
1046
						if (isset($props[$this->proptags['startdate']])) {
1047
							$props[$this->proptags['commonstart']] = $props[$this->proptags['startdate']];
1048
						}
1049
						if (isset($props[$this->proptags['duedate']])) {
1050
							$props[$this->proptags['commonend']] = $props[$this->proptags['duedate']];
1051
						}
1052
1053
						$props[PR_MESSAGE_CLASS] = 'IPM.Appointment';
1054
						// reset the PidLidMeetingType to Unspecified for outlook display the item
1055
						$props[$this->proptags['meetingtype']] = mtgEmpty;
1056
						// OL needs this field always being set, or it will not display item
1057
						$props[$this->proptags['recurring']] = false;
1058
1059
						// After creating new MR, If local categories exist then apply it on new MR.
1060
						if (!empty($localCategories)) {
1061
							mapi_setprops($new, $localCategories);
1062
						}
1063
1064
						/*
1065
						 * the client which has sent this meeting request can generate wrong flagdueby
1066
						 * time (mainly OL), so regenerate that property so we will always show reminder
1067
						 * on right time
1068
						 */
1069
						if (isset($props[$this->proptags['reminderminutes']])) {
1070
							$props[$this->proptags['flagdueby']] = $props[$this->proptags['startdate']] - ($props[$this->proptags['reminderminutes']] * 60);
1071
						}
1072
1073
						// When meeting requests are generated by third-party solutions, we might be missing the updatecounter property.
1074
						if (!isset($props[$this->proptags['updatecounter']])) {
1075
							$props[$this->proptags['updatecounter']] = 0;
1076
						}
1077
						// when we are automatically processing the meeting request set responsestatus to olResponseNotResponded
1078
						$props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
1079
1080
						if (isset($props[$this->proptags['intendedbusystatus']])) {
1081
							if ($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) {
1082
								$props[$this->proptags['busystatus']] = fbTentative;
1083
							}
1084
							else {
1085
								$props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']];
1086
							}
1087
						// we already have intendedbusystatus value in $props so no need to copy it
1088
						}
1089
						else {
1090
							$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1091
						}
1092
1093
						if ($userAction) {
1094
							$addrInfo = $this->getOwnerAddress($this->store);
1095
1096
							// if user has responded then set replytime and name
1097
							$props[$this->proptags['replytime']] = time();
1098
							if (!empty($addrInfo)) {
1099
								$props[$this->proptags['apptreplyname']] = $addrInfo[0];
1100
							}
1101
						}
1102
1103
						mapi_setprops($new, $proposeNewTimeProps + $props);
1104
1105
						$reciptable = mapi_message_getrecipienttable($this->message);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1105
						$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($this->message);
Loading history...
1106
1107
						$recips = [];
1108
						// If delegate, then do not add the delegate in recipients
1109
						if ($isDelegate) {
1110
							$delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
0 ignored issues
show
The constant PR_RECEIVED_BY_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1111
							$res = [
1112
								RES_PROPERTY,
1113
								[
1114
									RELOP => RELOP_NE,
1115
									ULPROPTAG => PR_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1116
									VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
1117
								],
1118
							];
1119
							$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1119
							$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops, $res);
Loading history...
1120
						}
1121
						else {
1122
							$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1123
						}
1124
1125
						$this->addOrganizer($props, $recips);
1126
						mapi_message_modifyrecipients($new, MODRECIP_ADD, $recips);
1127
						mapi_savechanges($new);
1128
1129
						$props = mapi_getprops($new, [PR_ENTRYID]);
1130
						$entryid = $props[PR_ENTRYID];
1131
					}
1132
				}
1133
			}
1134
		}
1135
		else {
1136
			// Here only properties are set on calendaritem, because user is responding from calendar.
1137
			$props = [];
1138
			$props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted;
1139
1140
			if (isset($messageprops[$this->proptags['intendedbusystatus']])) {
1141
				if ($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) {
1142
					$props[$this->proptags['busystatus']] = fbTentative;
1143
				}
1144
				else {
1145
					$props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1146
				}
1147
				$props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']];
1148
			}
1149
			else {
1150
				$props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
1151
			}
1152
1153
			$props[$this->proptags['meetingstatus']] = olMeetingReceived;
1154
1155
			$addrInfo = $this->getOwnerAddress($this->store);
1156
1157
			// if user has responded then set replytime and name
1158
			$props[$this->proptags['replytime']] = time();
1159
			if (!empty($addrInfo)) {
1160
				$props[$this->proptags['apptreplyname']] = $addrInfo[0];
1161
			}
1162
1163
			if ($basedate) {
1164
				$recurr = new Recurrence($store, $this->message);
1165
1166
				// Copy recipients list
1167
				$reciptable = mapi_message_getrecipienttable($this->message);
1168
				$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
1169
1170
				if ($recurr->isException($basedate)) {
1171
					$recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips);
1172
				}
1173
				else {
1174
					$props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
1175
					$props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
1176
1177
					$props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
1178
					$props[PR_SENT_REPRESENTING_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
1179
					$props[PR_SENT_REPRESENTING_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
1180
					$props[PR_SENT_REPRESENTING_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
1181
					$props[PR_SENT_REPRESENTING_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
1182
1183
					$recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips);
1184
				}
1185
			}
1186
			else {
1187
				mapi_setprops($this->message, $proposeNewTimeProps + $props);
1188
			}
1189
			mapi_savechanges($this->message);
1190
1191
			$entryid = $messageprops[PR_ENTRYID];
1192
		}
1193
1194
		return $entryid;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $entryid does not seem to be defined for all execution paths leading up to this point.
Loading history...
1195
	}
1196
1197
	/**
1198
	 * Declines the meeting request by moving the item to the deleted
1199
	 * items folder and sending a decline message. After declining, you
1200
	 * can't use this class instance any more. The message is closed.
1201
	 * When an occurrence is decline then false is returned because that
1202
	 * occurrence is deleted not the recurring item.
1203
	 *
1204
	 * @param bool  $sendresponse true if a response has to be sent to organizer
1205
	 * @param mixed $basedate     if specified contains starttime of day of an occurrence
1206
	 * @param mixed $body
1207
	 *
1208
	 * @return bool true if item is deleted from Calendar else false
1209
	 */
1210
	public function doDecline($sendresponse, $basedate = false, $body = false) {
1211
		if ($this->isLocalOrganiser()) {
1212
			return false;
1213
		}
1214
1215
		$result = false;
1216
		$calendaritem = false;
1217
1218
		// Remove any previous calendar items with this goid and appt id
1219
		$messageprops = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1219
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
1220
1221
		// If this meeting request is received by a delegate then open delegator's store.
1222
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1223
			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1224
1225
			$store = $delegatorStore['store'];
1226
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1227
		}
1228
		else {
1229
			$calFolder = $this->openDefaultCalendar();
1230
			$store = $this->store;
1231
		}
1232
1233
		// check for calendar access before deleting the calendar item
1234
		if ($this->checkCalendarWriteAccess($store) !== true) {
1235
			// Throw an exception that we don't have write permissions on calendar folder,
1236
			// allow caller to fill the error message
1237
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1238
		}
1239
1240
		$goid = $messageprops[$this->proptags['goid']];
1241
1242
		// First, find the items in the calendar by GlobalObjid (0x3)
1243
		$entryids = $this->findCalendarItems($goid, $calFolder);
1244
1245
		if (!$basedate) {
1246
			$basedate = $this->getBasedateFromGlobalID($goid);
1247
		}
1248
1249
		if ($sendresponse) {
1250
			$this->createResponse(olResponseDeclined, [], $body, $store, $basedate, $calFolder);
1251
		}
1252
1253
		if ($basedate) {
1254
			// use CleanGlobalObjid (0x23)
1255
			$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
1256
1257
			if (is_array($calendaritems)) {
1258
				foreach ($calendaritems as $entryid) {
1259
					// Open each calendar item and set the properties of the cancellation object
1260
					$calendaritem = mapi_msgstore_openentry($store, $entryid);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1260
					$calendaritem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
1261
1262
					// Recurring item is found, now delete exception
1263
					if ($calendaritem) {
1264
						$this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
1265
						$result = true;
1266
					}
1267
				}
1268
			}
1269
1270
			if ($this->isMeetingRequest()) {
1271
				$calendaritem = false;
1272
			}
1273
		}
1274
1275
		if (!$calendaritem) {
1276
			$calendar = $this->openDefaultCalendar($store);
1277
1278
			if (!empty($entryids)) {
1279
				mapi_folder_deletemessages($calendar, $entryids);
0 ignored issues
show
The function mapi_folder_deletemessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1279
				/** @scrutinizer ignore-call */ 
1280
    mapi_folder_deletemessages($calendar, $entryids);
Loading history...
1280
			}
1281
1282
			// All we have to do to decline, is to move the item to the waste basket
1283
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1284
			$sourcefolder = $this->openParentFolder();
1285
1286
			$messageprops = mapi_getprops($this->message, [PR_ENTRYID]);
1287
1288
			// Release the message
1289
			$this->message = null;
1290
1291
			// Move the message to the waste basket
1292
			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1292
			/** @scrutinizer ignore-call */ 
1293
   mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
1293
1294
			$result = true;
1295
		}
1296
1297
		return $result;
1298
	}
1299
1300
	/**
1301
	 * Removes a meeting request from the calendar when the user presses the
1302
	 * 'remove from calendar' button in response to a meeting cancellation.
1303
	 *
1304
	 * @param mixed $basedate if specified contains starttime of day of an occurrence
1305
	 *
1306
	 * @return null|false
1307
	 */
1308
	public function doRemoveFromCalendar($basedate) {
1309
		if ($this->isLocalOrganiser()) {
1310
			return false;
1311
		}
1312
1313
		$messageprops = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_ENTRYID, PR_MESSAGE_CLASS]);
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1313
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_ENTRYID, PR_MESSAGE_CLASS]);
Loading history...
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1314
1315
		$goid = $messageprops[$this->proptags['goid']];
1316
1317
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1318
			$delegatorStore = $this->getDelegatorStore($messageprops[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1319
1320
			$store = $delegatorStore['store'];
1321
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1322
		}
1323
		else {
1324
			$store = $this->store;
1325
			$calFolder = $this->openDefaultCalendar();
1326
		}
1327
1328
		// check for calendar access before deleting the calendar item
1329
		if ($this->checkCalendarWriteAccess($store) !== true) {
1330
			// Throw an exception that we don't have write permissions on calendar folder,
1331
			// allow caller to fill the error message
1332
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1333
		}
1334
1335
		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1336
		// get the source folder of the meeting message
1337
		$sourcefolder = $this->openParentFolder();
1338
1339
		// Check if the message is a meeting request in the inbox or a calendaritem by checking the message class
1340
		if ($this->isMeetingCancellation($messageprops[PR_MESSAGE_CLASS])) {
1341
			// get the basedate to check for exception
1342
			$basedate = $this->getBasedateFromGlobalID($goid);
1343
1344
			$calendarItem = $this->getCorrespondentCalendarItem(true);
1345
1346
			if ($calendarItem !== false) {
1347
				// basedate is provided so open exception
1348
				if ($basedate) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $basedate of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1349
					$exception = $this->getExceptionItem($calendarItem, $basedate);
1350
1351
					if ($exception !== false) {
0 ignored issues
show
The condition $exception !== false is always true.
Loading history...
1352
						// exception found, remove it from calendar
1353
						$this->doRemoveExceptionFromCalendar($basedate, $calendarItem, $store);
1354
					}
1355
				}
1356
				else {
1357
					// remove normal / recurring series from calendar
1358
					$entryids = mapi_getprops($calendarItem, [PR_ENTRYID]);
1359
1360
					$entryids = [$entryids[PR_ENTRYID]];
1361
1362
					mapi_folder_copymessages($calFolder, $entryids, $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1362
					/** @scrutinizer ignore-call */ 
1363
     mapi_folder_copymessages($calFolder, $entryids, $wastebasket, MESSAGE_MOVE);
Loading history...
1363
				}
1364
			}
1365
1366
			// Release the message, because we are going to move it to wastebasket
1367
			$this->message = null;
1368
1369
			// Move the cancellation mail to wastebasket
1370
			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1371
		}
1372
		else {
1373
			// Here only properties are set on calendaritem, because user is responding from calendar.
1374
			if ($basedate) {
1375
				// remove the occurrence
1376
				$this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
1377
			}
1378
			else {
1379
				// remove normal/recurring meeting item.
1380
				// Move the message to the waste basket
1381
				mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1382
			}
1383
		}
1384
	}
1385
1386
	/**
1387
	 * Function can be used to cancel any existing meeting and send cancellation mails to attendees.
1388
	 * Should only be called from meeting object from calendar.
1389
	 *
1390
	 * @param mixed $basedate (optional) basedate of occurrence which should be cancelled
1391
	 *
1392
	 * @FIXME cancellation mail is also sent to attendee which has declined the meeting
1393
	 * @FIXME don't send canellation mail when cancelling meeting from past
1394
	 */
1395
	public function doCancelInvitation($basedate = false) {
1396
		if (!$this->isLocalOrganiser()) {
1397
			return;
1398
		}
1399
1400
		// check write access for delegate
1401
		if ($this->checkCalendarWriteAccess($this->store) !== true) {
1402
			// Throw an exception that we don't have write permissions on calendar folder,
1403
			// error message will be filled by module
1404
			throw new MAPIException(null, MAPI_E_NO_ACCESS);
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1405
		}
1406
1407
		$messageProps = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['recurring']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1407
		$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['recurring']]);
Loading history...
1408
1409
		if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
1410
			// cancellation of recurring series or one occurrence
1411
			$recurrence = new Recurrence($this->store, $this->message);
1412
1413
			// if basedate is specified then we are cancelling only one occurrence, so create exception for that occurrence
1414
			if ($basedate) {
1415
				$recurrence->createException([], $basedate, true);
1416
			}
1417
1418
			// update the meeting request
1419
			$this->updateMeetingRequest();
1420
1421
			// send cancellation mails
1422
			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ', $basedate);
1423
1424
			// save changes in the message
1425
			mapi_savechanges($this->message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1425
			/** @scrutinizer ignore-call */ 
1426
   mapi_savechanges($this->message);
Loading history...
1426
		}
1427
		else {
1428
			// cancellation of normal meeting request
1429
			// Send the cancellation
1430
			$this->updateMeetingRequest();
1431
			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ');
1432
1433
			// save changes in the message
1434
			mapi_savechanges($this->message);
1435
		}
1436
1437
		// if basedate is specified then we have already created exception of it so nothing should be done now
1438
		// but when cancelling normal / recurring meeting request we need to remove meeting from calendar
1439
		if ($basedate === false) {
1440
			// get the wastebasket folder, for delegate this will give wastebasket of delegate
1441
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1442
1443
			// get the source folder of the meeting message
1444
			$sourcefolder = $this->openParentFolder();
1445
1446
			// Move the message to the deleted items
1447
			mapi_folder_copymessages($sourcefolder, [$messageProps[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1447
			/** @scrutinizer ignore-call */ 
1448
   mapi_folder_copymessages($sourcefolder, [$messageProps[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
1448
		}
1449
	}
1450
1451
	/**
1452
	 * Convert epoch to MAPI FileTime, number of 100-nanosecond units since
1453
	 * the start of January 1, 1601.
1454
	 * https://msdn.microsoft.com/en-us/library/office/cc765906.aspx.
1455
	 *
1456
	 * @param int $epoch the current epoch
1457
	 *
1458
	 * @return int the MAPI FileTime equalevent to the given epoch time
1459
	 */
1460
	public function epochToMapiFileTime($epoch) {
1461
		$nanoseconds_between_epoch = 116444736000000000;
1462
1463
		return ($epoch * 10000000) + $nanoseconds_between_epoch;
1464
	}
1465
1466
	/**
1467
	 * Sets the properties in the message so that is can be sent
1468
	 * as a meeting request. The caller has to submit the message. This
1469
	 * is only used for new MeetingRequests. Pass the appointment item as $message
1470
	 * in the constructor to do this.
1471
	 *
1472
	 * @param mixed $basedate
1473
	 */
1474
	public function setMeetingRequest($basedate = false): void {
1475
		$props = mapi_getprops($this->message, [$this->proptags['updatecounter']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1475
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['updatecounter']]);
Loading history...
1476
1477
		// Create a new global id for this item
1478
		// https://msdn.microsoft.com/en-us/library/ee160198(v=exchg.80).aspx
1479
		$goid = pack('H*', '040000008200E00074C5B7101A82E00800000000');
1480
		/*
1481
		$year = gmdate('Y');
1482
		$month = gmdate('n');
1483
		$day = gmdate('j');
1484
		$goid .= pack('n', $year);
1485
		$goid .= pack('C', $month);
1486
		$goid .= pack('C', $day);
1487
		*/
1488
		// Creation Time
1489
		$time = $this->epochToMapiFileTime(time());
1490
		$goid .= pack('V', $time & 0xFFFFFFFF);
1491
		$goid .= pack('V', $time >> 32);
1492
		// 8 Zeros
1493
		$goid .= pack('H*', '0000000000000000');
1494
		// Length of the random data
1495
		$goid .= pack('V', 16);
1496
		// Random data.
1497
		for ($i = 0; $i < 16; ++$i) {
1498
			$goid .= chr(rand(0, 255));
1499
		}
1500
1501
		// Create a new appointment id for this item
1502
		$apptid = rand();
1503
1504
		$props[PR_OWNER_APPT_ID] = $apptid;
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1505
		$props[PR_ICON_INDEX] = 1026;
0 ignored issues
show
The constant PR_ICON_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1506
		$props[$this->proptags['goid']] = $goid;
1507
		$props[$this->proptags['goid2']] = $goid;
1508
1509
		if (!isset($props[$this->proptags['updatecounter']])) {
1510
			$props[$this->proptags['updatecounter']] = 0;			// OL also starts sequence no with zero.
1511
			$props[$this->proptags['last_updatecounter']] = 0;
1512
		}
1513
1514
		mapi_setprops($this->message, $props);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1514
		/** @scrutinizer ignore-call */ 
1515
  mapi_setprops($this->message, $props);
Loading history...
1515
	}
1516
1517
	/**
1518
	 * Sends a meeting request by copying it to the outbox, converting
1519
	 * the message class, adding some properties that are required only
1520
	 * for sending the message and submitting the message. Set cancel to
1521
	 * true if you wish to completely cancel the meeting request. You can
1522
	 * specify an optional 'prefix' to prefix the sent message, which is normally
1523
	 * 'Canceled: '.
1524
	 *
1525
	 * @param mixed $cancel
1526
	 * @param mixed $prefix
1527
	 * @param mixed $basedate
1528
	 * @param mixed $modifiedRecips
1529
	 * @param mixed $deletedRecips
1530
	 *
1531
	 * @return (int|mixed)[]|true
1532
	 *
1533
	 * @psalm-return array{error: 1|3|4, displayname: mixed}|true
1534
	 */
1535
	public function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $modifiedRecips = false, $deletedRecips = false) {
1536
		$this->includesResources = false;
1537
		$this->nonAcceptingResources = [];
1538
1539
		// Get the properties of the message
1540
		$messageprops = mapi_getprops($this->message, [$this->proptags['recurring']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1540
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['recurring']]);
Loading history...
1541
1542
		/*
1543
		 * Submit message to non-resource recipients
1544
		 */
1545
		// Set BusyStatus to olTentative (1)
1546
		// Set MeetingStatus to olMeetingReceived
1547
		// Set ResponseStatus to olResponseNotResponded
1548
1549
		/*
1550
		 * While sending recurrence meeting exceptions are not sent as attachments
1551
		 * because first all exceptions are sent and then recurrence meeting is sent.
1552
		 */
1553
		if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) {
1554
			// Book resource
1555
			$this->bookResources($this->message, $cancel, $prefix);
1556
1557
			if (!$this->errorSetResource) {
1558
				$recurr = new Recurrence($this->openDefaultStore(), $this->message);
0 ignored issues
show
It seems like $this->openDefaultStore() can also be of type false; however, parameter $store of Recurrence::__construct() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1558
				$recurr = new Recurrence(/** @scrutinizer ignore-type */ $this->openDefaultStore(), $this->message);
Loading history...
1559
1560
				// First send meetingrequest for recurring item
1561
				$this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $modifiedRecips, $deletedRecips);
1562
1563
				// Then send all meeting request for all exceptions
1564
				$exceptions = $recurr->getAllExceptions();
1565
				if ($exceptions) {
1566
					foreach ($exceptions as $exceptionBasedate) {
1567
						$attach = $recurr->getExceptionAttachment($exceptionBasedate);
1568
1569
						if ($attach) {
1570
							$occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1570
							$occurrenceItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
1571
							$this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $modifiedRecips, $deletedRecips);
1572
							mapi_savechanges($attach);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1572
							/** @scrutinizer ignore-call */ 
1573
       mapi_savechanges($attach);
Loading history...
1573
						}
1574
					}
1575
				}
1576
			}
1577
		}
1578
		else {
1579
			// Basedate found, an exception is to be sent
1580
			if ($basedate) {
1581
				$recurr = new Recurrence($this->openDefaultStore(), $this->message);
1582
1583
				if ($cancel) {
1584
					// @TODO: remove occurrence from Resource's Calendar if resource was booked for whole series
1585
					$this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false);
1586
				}
1587
				else {
1588
					$attach = $recurr->getExceptionAttachment($basedate);
1589
1590
					if ($attach) {
1591
						$occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
1592
1593
						// Book resource for this occurrence
1594
						$resourceRecipData = $this->bookResources($occurrenceItem, $cancel, $prefix, $basedate);
0 ignored issues
show
The assignment to $resourceRecipData is dead and can be removed.
Loading history...
1595
1596
						if (!$this->errorSetResource) {
1597
							// Save all previous changes
1598
							mapi_savechanges($this->message);
1599
1600
							$this->submitMeetingRequest($occurrenceItem, $cancel, $prefix, $basedate, $recurr, true, $modifiedRecips, $deletedRecips);
1601
							mapi_savechanges($occurrenceItem);
1602
							mapi_savechanges($attach);
1603
						}
1604
					}
1605
				}
1606
			}
1607
			else {
1608
				// This is normal meeting
1609
				$resourceRecipData = $this->bookResources($this->message, $cancel, $prefix);
1610
1611
				if (!$this->errorSetResource) {
1612
					$this->submitMeetingRequest($this->message, $cancel, $prefix, false, false, false, $modifiedRecips, $deletedRecips);
1613
				}
1614
			}
1615
		}
1616
1617
		if (isset($this->errorSetResource) && $this->errorSetResource) {
1618
			return [
1619
				'error' => $this->errorSetResource,
1620
				'displayname' => $this->recipientDisplayname,
1621
			];
1622
		}
1623
1624
		return true;
1625
	}
1626
1627
	/**
1628
	 * Updates the message after an update has been performed (for example,
1629
	 * changing the time of the meeting). This must be called before re-sending
1630
	 * the meeting request. You can also call this function instead of 'setMeetingRequest()'
1631
	 * as it will automatically call setMeetingRequest on this object if it is the first
1632
	 * call to this function.
1633
	 *
1634
	 * @param mixed $basedate
1635
	 */
1636
	public function updateMeetingRequest($basedate = false): void {
1637
		$messageprops = mapi_getprops($this->message, [$this->proptags['last_updatecounter'], $this->proptags['goid']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1637
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['last_updatecounter'], $this->proptags['goid']]);
Loading history...
1638
1639
		if (!isset($messageprops[$this->proptags['goid']])) {
1640
			$this->setMeetingRequest($basedate);
1641
		}
1642
		else {
1643
			$counter = $messageprops[$this->proptags['last_updatecounter']] + 1;
1644
1645
			// increment value of last_updatecounter, last_updatecounter will be common for recurring series
1646
			// so even if you sending an exception only you need to update the last_updatecounter in the recurring series message
1647
			// this way we can make sure that every time we will be using a uniwue number for every operation
1648
			mapi_setprops($this->message, [$this->proptags['last_updatecounter'] => $counter]);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1648
			/** @scrutinizer ignore-call */ 
1649
   mapi_setprops($this->message, [$this->proptags['last_updatecounter'] => $counter]);
Loading history...
1649
		}
1650
	}
1651
1652
	/**
1653
	 * Returns TRUE if we are the organiser of the meeting. Can be used with any type of meeting object.
1654
	 */
1655
	public function isLocalOrganiser(): bool {
1656
		$props = mapi_getprops($this->message, [$this->proptags['goid'], PR_MESSAGE_CLASS]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1656
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['goid'], PR_MESSAGE_CLASS]);
Loading history...
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1657
1658
		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
1659
			// we are checking with calendar item
1660
			$calendarItem = $this->message;
1661
		}
1662
		else {
1663
			// we are checking with meeting request / response / cancellation mail
1664
			// get calendar items
1665
			$calendarItem = $this->getCorrespondentCalendarItem(true);
1666
		}
1667
1668
		// even if we have received request/response for exception/occurrence then also
1669
		// we can check recurring series for organizer, no need to check with exception/occurrence
1670
1671
		if ($calendarItem !== false) {
1672
			$messageProps = mapi_getprops($calendarItem, [$this->proptags['responsestatus']]);
1673
1674
			if (isset($messageProps[$this->proptags['responsestatus']]) && $messageProps[$this->proptags['responsestatus']] === olResponseOrganized) {
1675
				return true;
1676
			}
1677
		}
1678
1679
		return false;
1680
	}
1681
1682
	/*
1683
	 * Support functions - INTERNAL ONLY
1684
	 ***************************************************************************************************
1685
	 */
1686
1687
	/**
1688
	 * Return the tracking status of a recipient based on the IPM class (passed).
1689
	 *
1690
	 * @param mixed $class
1691
	 */
1692
	public function getTrackStatus($class) {
1693
		$status = olRecipientTrackStatusNone;
1694
1695
		switch ($class) {
1696
			case 'IPM.Schedule.Meeting.Resp.Pos':
1697
				$status = olRecipientTrackStatusAccepted;
1698
				break;
1699
1700
			case 'IPM.Schedule.Meeting.Resp.Tent':
1701
				$status = olRecipientTrackStatusTentative;
1702
				break;
1703
1704
			case 'IPM.Schedule.Meeting.Resp.Neg':
1705
				$status = olRecipientTrackStatusDeclined;
1706
				break;
1707
		}
1708
1709
		return $status;
1710
	}
1711
1712
	/**
1713
	 * Function returns MAPIFolder resource of the folder that currently holds this meeting/meeting request
1714
	 * object.
1715
	 */
1716
	public function openParentFolder() {
1717
		$messageprops = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1717
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
Loading history...
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1718
1719
		return mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1719
		return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
Loading history...
1720
	}
1721
1722
	/**
1723
	 * Function will return resource of the default calendar folder of store.
1724
	 *
1725
	 * @param mixed $store {optional} user store whose default calendar should be opened
1726
	 *
1727
	 * @return resource default calendar folder of store
1728
	 */
1729
	public function openDefaultCalendar($store = false) {
1730
		return $this->openDefaultFolder(PR_IPM_APPOINTMENT_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1731
	}
1732
1733
	/**
1734
	 * Function will return resource of the default outbox folder of store.
1735
	 *
1736
	 * @param mixed $store {optional} user store whose default outbox should be opened
1737
	 *
1738
	 * @return resource default outbox folder of store
1739
	 */
1740
	public function openDefaultOutbox($store = false) {
1741
		return $this->openBaseFolder(PR_IPM_OUTBOX_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_OUTBOX_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1742
	}
1743
1744
	/**
1745
	 * Function will return resource of the default wastebasket folder of store.
1746
	 *
1747
	 * @param mixed $store {optional} user store whose default wastebasket should be opened
1748
	 *
1749
	 * @return resource default wastebasket folder of store
1750
	 */
1751
	public function openDefaultWastebasket($store = false) {
1752
		return $this->openBaseFolder(PR_IPM_WASTEBASKET_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_WASTEBASKET_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1753
	}
1754
1755
	/**
1756
	 * Function will return resource of the default calendar folder of store.
1757
	 *
1758
	 * @param mixed $store {optional} user store whose default calendar should be opened
1759
	 *
1760
	 * @return bool|string default calendar folder of store
1761
	 */
1762
	public function getDefaultWastebasketEntryID($store = false) {
1763
		return $this->getBaseEntryID(PR_IPM_WASTEBASKET_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_WASTEBASKET_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1764
	}
1765
1766
	/**
1767
	 * Function will return resource of the default sent mail folder of store.
1768
	 *
1769
	 * @param mixed $store {optional} user store whose default sent mail should be opened
1770
	 *
1771
	 * @return bool|string default sent mail folder of store
1772
	 */
1773
	public function getDefaultSentmailEntryID($store = false) {
1774
		return $this->getBaseEntryID(PR_IPM_SENTMAIL_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_SENTMAIL_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1775
	}
1776
1777
	/**
1778
	 * Function will return entryid of any default folder of store. This method is useful when you want
1779
	 * to get entryid of folder which is stored as properties of inbox folder
1780
	 * (PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID).
1781
	 *
1782
	 * @param int   $prop  proptag of the folder for which we want to get entryid
1783
	 * @param mixed $store {optional} user store from which we need to get entryid of default folder
1784
	 *
1785
	 * @return bool|string entryid of folder pointed by $prop
1786
	 */
1787
	public function getDefaultFolderEntryID($prop, $store = false) {
1788
		try {
1789
			$inbox = mapi_msgstore_getreceivefolder($store ? $store : $this->store);
0 ignored issues
show
The function mapi_msgstore_getreceivefolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1789
			$inbox = /** @scrutinizer ignore-call */ mapi_msgstore_getreceivefolder($store ? $store : $this->store);
Loading history...
1790
			$inboxprops = mapi_getprops($inbox, [$prop]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1790
			$inboxprops = /** @scrutinizer ignore-call */ mapi_getprops($inbox, [$prop]);
Loading history...
1791
			if (isset($inboxprops[$prop])) {
1792
				return $inboxprops[$prop];
1793
			}
1794
		}
1795
		catch (MAPIException $e) {
1796
			// public store doesn't support this method
1797
			if ($e->getCode() == MAPI_E_NO_SUPPORT) {
0 ignored issues
show
The constant MAPI_E_NO_SUPPORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1798
				// don't propagate this error to parent handlers, if store doesn't support it
1799
				$e->setHandled();
1800
			}
1801
		}
1802
1803
		return false;
1804
	}
1805
1806
	/**
1807
	 * Function will return resource of any default folder of store.
1808
	 *
1809
	 * @param int   $prop  proptag of the folder that we want to open
1810
	 * @param mixed $store {optional} user store from which we need to open default folder
1811
	 *
1812
	 * @return resource default folder of store
1813
	 */
1814
	public function openDefaultFolder($prop, $store = false) {
1815
		$folder = false;
1816
		$entryid = $this->getDefaultFolderEntryID($prop, $store);
1817
1818
		if ($entryid !== false) {
1819
			$folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1819
			$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
Loading history...
1820
		}
1821
1822
		return $folder;
1823
	}
1824
1825
	/**
1826
	 * Function will return entryid of default folder from store. This method is useful when you want
1827
	 * to get entryid of folder which is stored as store properties
1828
	 * (PR_IPM_FAVORITES_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID).
1829
	 *
1830
	 * @param int   $prop  proptag of the folder whose entryid we want to get
1831
	 * @param mixed $store {optional} user store from which we need to get entryid of default folder
1832
	 *
1833
	 * @return bool|string entryid of default folder from store
1834
	 */
1835
	public function getBaseEntryID($prop, $store = false) {
1836
		$storeprops = mapi_getprops($store ? $store : $this->store, [$prop]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1836
		$storeprops = /** @scrutinizer ignore-call */ mapi_getprops($store ? $store : $this->store, [$prop]);
Loading history...
1837
		if (!isset($storeprops[$prop])) {
1838
			return false;
1839
		}
1840
1841
		return $storeprops[$prop];
1842
	}
1843
1844
	/**
1845
	 * Function will return resource of any default folder of store.
1846
	 *
1847
	 * @param int   $prop  proptag of the folder that we want to open
1848
	 * @param mixed $store {optional} user store from which we need to open default folder
1849
	 *
1850
	 * @return resource default folder of store
1851
	 */
1852
	public function openBaseFolder($prop, $store = false) {
1853
		$folder = false;
1854
		$entryid = $this->getBaseEntryID($prop, $store);
1855
1856
		if ($entryid !== false) {
1857
			$folder = mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1857
			$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
Loading history...
1858
		}
1859
1860
		return $folder;
1861
	}
1862
1863
	/**
1864
	 * Function checks whether user has access over the specified folder or not.
1865
	 *
1866
	 * @param string $entryid entryid The entryid of the folder to check
1867
	 * @param mixed  $store   (optional) store from which folder should be opened
1868
	 *
1869
	 * @return bool true if user has an access over the folder, false if not
1870
	 */
1871
	public function checkFolderWriteAccess($entryid, $store = false) {
1872
		$accessToFolder = false;
1873
1874
		if (!empty($entryid)) {
1875
			if ($store === false) {
1876
				$store = $this->store;
1877
			}
1878
1879
			try {
1880
				$folder = mapi_msgstore_openentry($store, $entryid);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1880
				$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
1881
				$folderProps = mapi_getprops($folder, [PR_ACCESS]);
0 ignored issues
show
The constant PR_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1881
				$folderProps = /** @scrutinizer ignore-call */ mapi_getprops($folder, [PR_ACCESS]);
Loading history...
1882
				if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1883
					$accessToFolder = true;
1884
				}
1885
			}
1886
			catch (MAPIException $e) {
1887
				// we don't have rights to open folder, so return false
1888
				if ($e->getCode() == MAPI_E_NO_ACCESS) {
0 ignored issues
show
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1889
					return $accessToFolder;
1890
				}
1891
1892
				// rethrow other errors
1893
				throw $e;
1894
			}
1895
		}
1896
1897
		return $accessToFolder;
1898
	}
1899
1900
	/**
1901
	 * Function checks whether user has access over the specified folder or not.
1902
	 *
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]);
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1910
			$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
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]);
0 ignored issues
show
The constant PR_MDB_PROVIDER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1923
		if (isset($provider[PR_MDB_PROVIDER]) && $provider[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1924
			$entryid = mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
0 ignored issues
show
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1925
			$entryid = $entryid[PR_PARENT_ENTRYID];
1926
		}
1927
		else {
1928
			$entryid = $this->getDefaultFolderEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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 resource store of the user
1947
	 */
1948
	public function openCustomUserStore($ownerentryid) {
1949
		$ab = mapi_openaddressbook($this->session);
0 ignored issues
show
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1949
		$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
1950
1951
		try {
1952
			$mailuser = mapi_ab_openentry($ab, $ownerentryid);
0 ignored issues
show
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1952
			$mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $ownerentryid);
Loading history...
1953
		}
1954
		catch (MAPIException $e) {
1955
			return;
1956
		}
1957
1958
		$mailuserprops = mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1958
		$mailuserprops = /** @scrutinizer ignore-call */ mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
Loading history...
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1959
		$storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
0 ignored issues
show
The function mapi_msgstore_createentryid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1959
		$storeid = /** @scrutinizer ignore-call */ mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
Loading history...
1960
1961
		return mapi_openmsgstore($this->session, $storeid);
0 ignored issues
show
The function mapi_openmsgstore was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1961
		return /** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $storeid);
Loading history...
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 mixed $body
1970
	 * @param mixed $store
1971
	 * @param mixed $basedate            date of occurrence which attendee has responded
1972
	 * @param mixed $calFolder
1973
	 */
1974
	public function createResponse($status, $proposeNewTimeProps, $body, $store, $basedate, $calFolder): void {
1975
		$messageprops = mapi_getprops($this->message, [
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1975
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [
Loading history...
1976
			PR_SENT_REPRESENTING_ENTRYID,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1977
			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1978
			PR_SENT_REPRESENTING_ADDRTYPE,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1979
			PR_SENT_REPRESENTING_NAME,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1980
			PR_SENT_REPRESENTING_SEARCH_KEY,
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1981
			$this->proptags['goid'],
1982
			$this->proptags['goid2'],
1983
			$this->proptags['location'],
1984
			$this->proptags['startdate'],
1985
			$this->proptags['duedate'],
1986
			$this->proptags['recurring'],
1987
			$this->proptags['recurring_pattern'],
1988
			$this->proptags['recurrence_data'],
1989
			$this->proptags['timezone_data'],
1990
			$this->proptags['timezone'],
1991
			$this->proptags['updatecounter'],
1992
			PR_SUBJECT,
0 ignored issues
show
The constant PR_SUBJECT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1993
			PR_MESSAGE_CLASS,
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1994
			PR_OWNER_APPT_ID,
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1995
			$this->proptags['is_exception'],
1996
		]);
1997
1998
		$props = [];
1999
2000
		if ($basedate !== false && !$this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
2001
			// we are creating response from a recurring calendar item object
2002
			// We found basedate,so opened occurrence and get properties.
2003
			$recurr = new Recurrence($store, $this->message);
2004
			$exception = $recurr->getExceptionAttachment($basedate);
2005
2006
			if ($exception) {
2007
				// Exception found, Now retrieve properties
2008
				$imessage = mapi_attach_openobj($exception, 0);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2008
				$imessage = /** @scrutinizer ignore-call */ mapi_attach_openobj($exception, 0);
Loading history...
2009
				$imsgprops = mapi_getprops($imessage);
2010
2011
				// If location is provided, copy it to the response
2012
				if (isset($imsgprops[$this->proptags['location']])) {
2013
					$messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']];
2014
				}
2015
2016
				// Update $messageprops with timings of occurrence
2017
				$messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']];
2018
				$messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']];
2019
2020
				// Meeting related properties
2021
				$props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
2022
				$props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
2023
				$props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
2024
			}
2025
			else {
2026
				// Exceptions is deleted.
2027
				// Update $messageprops with timings of occurrence
2028
				$messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
2029
				$messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
2030
2031
				$props[$this->proptags['meetingstatus']] = olNonMeeting;
2032
				$props[$this->proptags['responsestatus']] = olResponseNone;
2033
			}
2034
2035
			$props[$this->proptags['recurring']] = false;
2036
			$props[$this->proptags['is_exception']] = true;
2037
		}
2038
		else {
2039
			// we are creating a response from meeting request mail (it could be recurring or non-recurring)
2040
			// Send all recurrence info in response, if this is a recurrence meeting.
2041
			$isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
2042
			$isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']];
2043
			if ($isRecurring || $isException) {
2044
				if ($isRecurring) {
2045
					$props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']];
2046
				}
2047
				if ($isException) {
2048
					$props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']];
2049
				}
2050
				$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
2051
2052
				$calendaritem = mapi_msgstore_openentry($store, $calendaritems[0]);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2052
				$calendaritem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $calendaritems[0]);
Loading history...
2053
				$recurr = new Recurrence($store, $calendaritem);
2054
			}
2055
		}
2056
2057
		// we are sending a response for recurring meeting request (or exception), so set some required properties
2058
		if (isset($recurr) && $recurr) {
2059
			if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
2060
				$props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
2061
			}
2062
2063
			if (!empty($messageprops[$this->proptags['recurrence_data']])) {
2064
				$props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
2065
			}
2066
2067
			$props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
2068
			$props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
2069
2070
			$this->generateRecurDates($recurr, $messageprops, $props);
2071
		}
2072
2073
		// Create a response message
2074
		$recip = [];
2075
		$recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
2076
		$recip[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2077
		$recip[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE];
0 ignored issues
show
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2078
		$recip[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2079
		$recip[PR_RECIPIENT_TYPE] = MAPI_TO;
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2080
		$recip[PR_SEARCH_KEY] = $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY];
0 ignored issues
show
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2081
2082
		$subjectprefix = '';
2083
2084
		switch ($status) {
2085
			case olResponseAccepted:
2086
				$classpostfix = 'Pos';
2087
				$subjectprefix = dgettext('zarafa', 'Accepted');
2088
				break;
2089
2090
			case olResponseDeclined:
2091
				$classpostfix = 'Neg';
2092
				$subjectprefix = dgettext('zarafa', 'Declined');
2093
				break;
2094
2095
			case olResponseTentative:
2096
				$classpostfix = 'Tent';
2097
				$subjectprefix = dgettext('zarafa', 'Tentatively accepted');
2098
				break;
2099
		}
2100
2101
		if (!empty($proposeNewTimeProps)) {
2102
			// if attendee has proposed new time then change subject prefix
2103
			$subjectprefix = dgettext('zarafa', 'New Time Proposed');
2104
		}
2105
2106
		$props[PR_SUBJECT] = $subjectprefix . ': ' . $messageprops[PR_SUBJECT];
2107
2108
		$props[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Resp.' . $classpostfix;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $classpostfix does not seem to be defined for all execution paths leading up to this point.
Loading history...
2109
		if (isset($messageprops[PR_OWNER_APPT_ID])) {
2110
			$props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2111
		}
2112
2113
		// Set GlobalId AND CleanGlobalId, if exception then also set basedate into GlobalId(0x3).
2114
		$props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2115
		$props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2116
		$props[$this->proptags['updatecounter']] = isset($messageprops[$this->proptags['updatecounter']]) ? $messageprops[$this->proptags['updatecounter']] : 0;
2117
2118
		if (!empty($proposeNewTimeProps)) {
2119
			// merge proposal properties to message properties which will be sent to organizer
2120
			$props = $proposeNewTimeProps + $props;
2121
		}
2122
2123
		// Set body message in Appointment
2124
		if (isset($body)) {
2125
			$props[PR_BODY] = $this->getMeetingTimeInfo() ? $this->getMeetingTimeInfo() : $body;
0 ignored issues
show
The constant PR_BODY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2126
		}
2127
2128
		// PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message
2129
		$props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
0 ignored issues
show
The constant PR_START_DATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2130
		$props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
0 ignored issues
show
The constant PR_END_DATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2131
2132
		// Set startdate and duedate in response mail.
2133
		$props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']];
2134
		$props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']];
2135
2136
		// responselocation is used in the UI in Outlook on the response message
2137
		if (isset($messageprops[$this->proptags['location']])) {
2138
			$props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']];
2139
			$props[$this->proptags['location']] = $messageprops[$this->proptags['location']];
2140
		}
2141
2142
		$message = $this->createOutgoingMessage($store);
2143
2144
		mapi_setprops($message, $props);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2144
		/** @scrutinizer ignore-call */ 
2145
  mapi_setprops($message, $props);
Loading history...
2145
		mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2145
		/** @scrutinizer ignore-call */ 
2146
  mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
Loading history...
2146
		mapi_savechanges($message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2146
		/** @scrutinizer ignore-call */ 
2147
  mapi_savechanges($message);
Loading history...
2147
		mapi_message_submitmessage($message);
0 ignored issues
show
The function mapi_message_submitmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2147
		/** @scrutinizer ignore-call */ 
2148
  mapi_message_submitmessage($message);
Loading history...
2148
	}
2149
2150
	/**
2151
	 * Function which finds items in calendar based on globalId and cleanGlobalId.
2152
	 *
2153
	 * @param string $goid             GlobalID(0x3) of item
2154
	 * @param mixed  $calendar         MAPI_folder of user (optional)
2155
	 * @param bool   $useCleanGlobalId if true then search should be performed on cleanGlobalId(0x23) else globalId(0x3)
2156
	 *
2157
	 * @return mixed
2158
	 */
2159
	public function findCalendarItems($goid, $calendar = false, $useCleanGlobalId = false) {
2160
		if ($calendar === false) {
2161
			// Open the Calendar
2162
			$calendar = $this->openDefaultCalendar();
2163
		}
2164
2165
		// Find the item by restricting all items to the correct ID
2166
		$restrict = [
2167
			RES_PROPERTY,
2168
			[
2169
				RELOP => RELOP_EQ,
2170
				ULPROPTAG => ($useCleanGlobalId === true ? $this->proptags['goid2'] : $this->proptags['goid']),
2171
				VALUE => $goid,
2172
			],
2173
		];
2174
2175
		$calendarcontents = mapi_folder_getcontentstable($calendar);
0 ignored issues
show
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2175
		$calendarcontents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($calendar);
Loading history...
2176
2177
		$rows = mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2177
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
Loading history...
2178
2179
		if (empty($rows)) {
2180
			return;
2181
		}
2182
2183
		$calendaritems = [];
2184
2185
		// In principle, there should only be one row, but we'll handle them all just in case
2186
		foreach ($rows as $row) {
2187
			$calendaritems[] = $row[PR_ENTRYID];
2188
		}
2189
2190
		return $calendaritems;
2191
	}
2192
2193
	// Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the
2194
	// same SMTP address when converted to SMTP
2195
	public function compareABEntryIDs($entryid1, $entryid2): bool {
2196
		// If the session was not passed, just do a 'normal' compare.
2197
		if (!$this->session) {
2198
			return $entryid1 == $entryid2;
2199
		}
2200
2201
		$smtp1 = $this->getSMTPAddress($entryid1);
2202
		$smtp2 = $this->getSMTPAddress($entryid2);
2203
2204
		if ($smtp1 == $smtp2) {
2205
			return true;
2206
		}
2207
2208
		return false;
2209
	}
2210
2211
	// Gets the SMTP address of the passed addressbook entryid
2212
	public function getSMTPAddress($entryid) {
2213
		if (!$this->session) {
2214
			return false;
2215
		}
2216
2217
		try {
2218
			$ab = mapi_openaddressbook($this->session);
0 ignored issues
show
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2218
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2219
			$abitem = mapi_ab_openentry($ab, $entryid);
0 ignored issues
show
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2219
			$abitem = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $entryid);
Loading history...
2220
2221
			if (!$abitem) {
2222
				return '';
2223
			}
2224
		}
2225
		catch (MAPIException $e) {
2226
			return '';
2227
		}
2228
2229
		$props = mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS]);
0 ignored issues
show
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SMTP_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2229
		$props = /** @scrutinizer ignore-call */ mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS]);
Loading history...
2230
2231
		if ($props[PR_ADDRTYPE] == 'SMTP') {
2232
			return $props[PR_EMAIL_ADDRESS];
2233
		}
2234
2235
		return $props[PR_SMTP_ADDRESS];
2236
	}
2237
2238
	/**
2239
	 * Gets the properties associated with the owner of the passed store:
2240
	 * PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID, PR_SEARCH_KEY.
2241
	 *
2242
	 * @param mixed $store                  message store
2243
	 * @param bool  $fallbackToLoggedInUser If true then return properties of logged in user instead of mailbox owner.
2244
	 *                                      Not used when passed store is public store.
2245
	 *                                      For public store we are always returning logged in user's info.
2246
	 *
2247
	 * @return array|false properties of logged in user in an array in sequence of display_name, email address, address type, entryid and search key
2248
	 *
2249
	 * @psalm-return false|list{mixed, mixed, mixed, mixed, mixed}
2250
	 */
2251
	public function getOwnerAddress($store, $fallbackToLoggedInUser = true) {
2252
		if (!$this->session) {
2253
			return false;
2254
		}
2255
2256
		$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
0 ignored issues
show
The constant PR_USER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2256
		$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
Loading history...
The constant PR_MAILBOX_OWNER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2257
2258
		$ownerEntryId = false;
2259
		if (isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) {
2260
			$ownerEntryId = $storeProps[PR_USER_ENTRYID];
2261
		}
2262
2263
		if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) {
2264
			$ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
2265
		}
2266
2267
		if ($ownerEntryId) {
2268
			$ab = mapi_openaddressbook($this->session);
0 ignored issues
show
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2268
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2269
2270
			$zarafaUser = mapi_ab_openentry($ab, $ownerEntryId);
0 ignored issues
show
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2270
			$zarafaUser = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $ownerEntryId);
Loading history...
2271
			if (!$zarafaUser) {
2272
				return false;
2273
			}
2274
2275
			$ownerProps = mapi_getprops($zarafaUser, [PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SEARCH_KEY]);
0 ignored issues
show
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2276
2277
			$addrType = $ownerProps[PR_ADDRTYPE];
2278
			$name = $ownerProps[PR_DISPLAY_NAME];
2279
			$emailAddr = $ownerProps[PR_EMAIL_ADDRESS];
2280
			$searchKey = $ownerProps[PR_SEARCH_KEY];
2281
			$entryId = $ownerEntryId;
2282
2283
			return [$name, $emailAddr, $addrType, $entryId, $searchKey];
2284
		}
2285
2286
		return false;
2287
	}
2288
2289
	// Opens this session's default message store
2290
	public function openDefaultStore() {
2291
		$storestable = mapi_getmsgstorestable($this->session);
0 ignored issues
show
The function mapi_getmsgstorestable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2291
		$storestable = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2292
		$rows = mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
0 ignored issues
show
The constant PR_DEFAULT_STORE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2292
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
Loading history...
2293
2294
		foreach ($rows as $row) {
2295
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
2296
				$entryid = $row[PR_ENTRYID];
2297
				break;
2298
			}
2299
		}
2300
2301
		if (!$entryid) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $entryid does not seem to be defined for all execution paths leading up to this point.
Loading history...
2302
			return false;
2303
		}
2304
2305
		return mapi_openmsgstore($this->session, $entryid);
0 ignored issues
show
The function mapi_openmsgstore was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2305
		return /** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $entryid);
Loading history...
2306
	}
2307
2308
	/**
2309
	 * Function which adds organizer to recipient list which is passed.
2310
	 * This function also checks if it has organizer.
2311
	 *
2312
	 * @param array $messageProps message properties
2313
	 * @param array $recipients   recipients list of message
2314
	 * @param bool  $isException  true if we are processing recipient of exception
2315
	 */
2316
	public function addOrganizer($messageProps, &$recipients, $isException = false): void {
2317
		$hasOrganizer = false;
2318
		// Check if meeting already has an organizer.
2319
		foreach ($recipients as $key => $recipient) {
2320
			if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) {
0 ignored issues
show
The constant PR_RECIPIENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2321
				$hasOrganizer = true;
2322
			}
2323
			elseif ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])) {
2324
				// Recipients for an occurrence
2325
				$recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse;
2326
			}
2327
		}
2328
2329
		if (!$hasOrganizer) {
2330
			// Create organizer.
2331
			$organizer = [];
2332
			$organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID];
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2333
			$organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2334
			$organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS];
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2335
			$organizer[PR_RECIPIENT_TYPE] = MAPI_TO;
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2336
			$organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_RECIPIENT_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2337
			$organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_SENT_REPRESENTING_ADDRTYPE];
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2338
			$organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2339
			$organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer;
2340
			$organizer[PR_SEARCH_KEY] = $messageProps[PR_SENT_REPRESENTING_SEARCH_KEY];
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2341
2342
			// Add organizer to recipients list.
2343
			array_unshift($recipients, $organizer);
2344
		}
2345
	}
2346
2347
	/**
2348
	 * Function which removes an exception/occurrence from recurrencing meeting
2349
	 * when a meeting cancellation of an occurrence is processed.
2350
	 *
2351
	 * @param mixed    $basedate basedate of an occurrence
2352
	 * @param mixed    $message  recurring item from which occurrence has to be deleted
2353
	 * @param resource $store    MAPI_MSG_Store which contains the item
2354
	 */
2355
	public function doRemoveExceptionFromCalendar($basedate, $message, $store): void {
2356
		$recurr = new Recurrence($store, $message);
2357
		$recurr->createException([], $basedate, true);
2358
		mapi_savechanges($message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2358
		/** @scrutinizer ignore-call */ 
2359
  mapi_savechanges($message);
Loading history...
2359
	}
2360
2361
	/**
2362
	 * Function which returns basedate of an changed occurrence from globalID of meeting request.
2363
	 *
2364
	 * @param string $goid globalID
2365
	 *
2366
	 * @return false|int true if basedate is found else false it not found
2367
	 */
2368
	public function getBasedateFromGlobalID($goid) {
2369
		$hexguid = bin2hex($goid);
2370
		$hexbase = substr($hexguid, 32, 8);
2371
		$day = (int) hexdec(substr($hexbase, 6, 2));
2372
		$month = (int) hexdec(substr($hexbase, 4, 2));
2373
		$year = (int) hexdec(substr($hexbase, 0, 4));
2374
2375
		if ($day && $month && $year) {
2376
			return gmmktime(0, 0, 0, $month, $day, $year);
2377
		}
2378
2379
		return false;
2380
	}
2381
2382
	/**
2383
	 * Function which sets basedate in globalID of changed occurrence which is to be sent.
2384
	 *
2385
	 * @param string $goid     globalID
2386
	 * @param mixed  $basedate of changed occurrence
2387
	 *
2388
	 * @return false|string globalID with basedate in it
2389
	 */
2390
	public function setBasedateInGlobalID($goid, $basedate = false) {
2391
		$hexguid = bin2hex($goid);
2392
		$year = $basedate ? sprintf('%04s', dechex((int) gmdate('Y', $basedate))) : '0000';
2393
		$month = $basedate ? sprintf('%02s', dechex((int) gmdate('m', $basedate))) : '00';
2394
		$day = $basedate ? sprintf('%02s', dechex((int) gmdate('d', $basedate))) : '00';
2395
2396
		return hex2bin(strtoupper(substr($hexguid, 0, 32) . $year . $month . $day . substr($hexguid, 40)));
2397
	}
2398
2399
	/**
2400
	 * Function which replaces attachments with copy_from in copy_to.
2401
	 *
2402
	 * @param mixed $copyFrom       MAPI_message from which attachments are to be copied
2403
	 * @param mixed $copyTo         MAPI_message to which attachment are to be copied
2404
	 * @param bool  $copyExceptions if true then all exceptions should also be sent as attachments
2405
	 */
2406
	public function replaceAttachments($copyFrom, $copyTo, $copyExceptions = true): void {
2407
		/* remove all old attachments */
2408
		$attachmentTableTo = mapi_message_getattachmenttable($copyTo);
0 ignored issues
show
The function mapi_message_getattachmenttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2408
		$attachmentTableTo = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($copyTo);
Loading history...
2409
		if ($attachmentTableTo) {
2410
			$attachments = mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
0 ignored issues
show
The constant PR_ATTACH_NUM was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ATTACH_METHOD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_EXCEPTION_STARTTIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2410
			$attachments = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
Loading history...
2411
2412
			foreach ($attachments as $attachProps) {
2413
				/* remove exceptions too? */
2414
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2415
					continue;
2416
				}
2417
				mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
0 ignored issues
show
The function mapi_message_deleteattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2417
				/** @scrutinizer ignore-call */ 
2418
    mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
Loading history...
2418
			}
2419
		}
2420
2421
		/* copy new attachments */
2422
		$attachmentTableFrom = mapi_message_getattachmenttable($copyFrom);
2423
		if ($attachmentTableFrom) {
2424
			$attachments = mapi_table_queryallrows($attachmentTableFrom, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2425
2426
			foreach ($attachments as $attachProps) {
2427
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2428
					continue;
2429
				}
2430
2431
				$attachOld = mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
0 ignored issues
show
The function mapi_message_openattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2431
				$attachOld = /** @scrutinizer ignore-call */ mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
Loading history...
2432
				$attachNewResourceMsg = mapi_message_createattach($copyTo);
0 ignored issues
show
The function mapi_message_createattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2432
				$attachNewResourceMsg = /** @scrutinizer ignore-call */ mapi_message_createattach($copyTo);
Loading history...
2433
				mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
0 ignored issues
show
The function mapi_copyto was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2433
				/** @scrutinizer ignore-call */ 
2434
    mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
Loading history...
2434
				mapi_savechanges($attachNewResourceMsg);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2434
				/** @scrutinizer ignore-call */ 
2435
    mapi_savechanges($attachNewResourceMsg);
Loading history...
2435
			}
2436
		}
2437
	}
2438
2439
	/**
2440
	 * Function which replaces recipients in copyTo with recipients from copyFrom.
2441
	 *
2442
	 * @param mixed $copyFrom   MAPI_message from which recipients are to be copied
2443
	 * @param mixed $copyTo     MAPI_message to which recipients are to be copied
2444
	 * @param bool  $isDelegate indicates whether delegate is processing
2445
	 *                          so don't copy delegate information to recipient table
2446
	 */
2447
	public function replaceRecipients($copyFrom, $copyTo, $isDelegate = false): void {
2448
		$recipientTable = mapi_message_getrecipienttable($copyFrom);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2448
		$recipientTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($copyFrom);
Loading history...
2449
2450
		// If delegate, then do not add the delegate in recipients
2451
		if ($isDelegate) {
2452
			$delegate = mapi_getprops($copyFrom, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2452
			$delegate = /** @scrutinizer ignore-call */ mapi_getprops($copyFrom, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
Loading history...
The constant PR_RECEIVED_BY_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2453
			$res = [
2454
				RES_PROPERTY,
2455
				[
2456
					RELOP => RELOP_NE,
2457
					ULPROPTAG => PR_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2458
					VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2459
				],
2460
			];
2461
			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2461
			$recipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
Loading history...
2462
		}
2463
		else {
2464
			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops);
2465
		}
2466
2467
		$copyToRecipientTable = mapi_message_getrecipienttable($copyTo);
2468
		$copyToRecipientRows = mapi_table_queryallrows($copyToRecipientTable, [PR_ROWID]);
0 ignored issues
show
The constant PR_ROWID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2469
2470
		mapi_message_modifyrecipients($copyTo, MODRECIP_REMOVE, $copyToRecipientRows);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2470
		/** @scrutinizer ignore-call */ 
2471
  mapi_message_modifyrecipients($copyTo, MODRECIP_REMOVE, $copyToRecipientRows);
Loading history...
2471
		mapi_message_modifyrecipients($copyTo, MODRECIP_ADD, $recipients);
2472
	}
2473
2474
	/**
2475
	 * Function creates meeting item in resource's calendar.
2476
	 *
2477
	 * @param resource $message  MAPI_message which is to create in resource's calendar
2478
	 * @param bool     $cancel   cancel meeting
2479
	 * @param mixed    $prefix   prefix for subject of meeting
2480
	 * @param mixed    $basedate
2481
	 *
2482
	 * @return (mixed|resource)[][]
2483
	 *
2484
	 * @psalm-return list<array{store: resource, folder: mixed, msg: mixed}>
2485
	 */
2486
	public function bookResources($message, $cancel, $prefix, $basedate = false): array {
2487
		if (!$this->enableDirectBooking) {
2488
			return [];
2489
		}
2490
2491
		// Get the properties of the message
2492
		$messageprops = mapi_getprops($message);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2492
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($message);
Loading history...
2493
2494
		if ($basedate) {
2495
			$recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID]);
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2496
2497
			$messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate);
2498
			$messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
2499
2500
			// Delete properties which are not needed.
2501
			$deleteProps = [$this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD];
0 ignored issues
show
The constant PR_ATTACHMENT_LINKID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ATTACHMENT_HIDDEN was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ATTACH_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ATTACH_METHOD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ATTACHMENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2502
			foreach ($deleteProps as $propID) {
2503
				if (isset($messageprops[$propID])) {
2504
					unset($messageprops[$propID]);
2505
				}
2506
			}
2507
2508
			if (isset($messageprops[$this->proptags['recurring']])) {
2509
				$messageprops[$this->proptags['recurring']] = false;
2510
			}
2511
2512
			// Set Outlook properties
2513
			$messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']];
2514
			$messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']];
2515
			$messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']];
2516
			$messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']];
2517
			$messageprops[$this->proptags['attendee_critical_change']] = time();
2518
			$messageprops[$this->proptags['owner_critical_change']] = time();
2519
		}
2520
2521
		// Get resource recipients
2522
		$getResourcesRestriction = [
2523
			RES_PROPERTY,
2524
			[
2525
				RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2526
				ULPROPTAG => PR_RECIPIENT_TYPE,
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2527
				VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2528
			],
2529
		];
2530
		$recipienttable = mapi_message_getrecipienttable($message);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2530
		$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
2531
		$resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2531
		$resourceRecipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
Loading history...
2532
2533
		$this->errorSetResource = false;
2534
		$resourceRecipData = [];
2535
2536
		// Put appointment into store resource users
2537
		$i = 0;
2538
		$len = count($resourceRecipients);
2539
		while (!$this->errorSetResource && $i < $len) {
2540
			$userStore = $this->openCustomUserStore($resourceRecipients[$i][PR_ENTRYID]);
2541
2542
			// Open root folder
2543
			$userRoot = mapi_msgstore_openentry($userStore, null);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2543
			$userRoot = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($userStore, null);
Loading history...
2544
2545
			// Get calendar entryID
2546
			$userRootProps = mapi_getprops($userRoot, [PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_FREEBUSY_ENTRYIDS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_STORE_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2547
2548
			// Open Calendar folder
2549
			$accessToFolder = false;
0 ignored issues
show
The assignment to $accessToFolder is dead and can be removed.
Loading history...
2550
2551
			try {
2552
				// @FIXME this checks delegate has access to resource's calendar folder
2553
				// but it should use boss' credentials
2554
2555
				$accessToFolder = $this->checkCalendarWriteAccess($this->store);
2556
				if ($accessToFolder) {
2557
					$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
2558
				}
2559
			}
2560
			catch (MAPIException $e) {
2561
				$e->setHandled();
2562
				$this->errorSetResource = 1; // No access
2563
			}
2564
2565
			if ($accessToFolder) {
2566
				/**
2567
				 * Get the LocalFreebusy message that contains the properties that
2568
				 * are set to accept or decline resource meeting requests.
2569
				 */
2570
				$localFreebusyMsg = FreeBusy::getLocalFreeBusyMessage($userStore);
2571
				if ($localFreebusyMsg) {
2572
					$props = mapi_getprops($localFreebusyMsg, [PR_SCHDINFO_AUTO_ACCEPT_APPTS, PR_SCHDINFO_DISALLOW_RECURRING_APPTS, PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS]);
0 ignored issues
show
The constant PR_SCHDINFO_AUTO_ACCEPT_APPTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SCHDINFO_DISALLOW_RECURRING_APPTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2573
2574
					$acceptMeetingRequests = isset($props[PR_SCHDINFO_AUTO_ACCEPT_APPTS]) ? $props[PR_SCHDINFO_AUTO_ACCEPT_APPTS] : false;
2575
					$declineRecurringMeetingRequests = isset($props[PR_SCHDINFO_DISALLOW_RECURRING_APPTS]) ? $props[PR_SCHDINFO_DISALLOW_RECURRING_APPTS] : false;
2576
					$declineConflictingMeetingRequests = isset($props[PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS]) ? $props[PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS] : false;
2577
2578
					if (!$acceptMeetingRequests) {
2579
						/*
2580
						 * When a resource has not been set to automatically accept meeting requests,
2581
						 * the meeting request has to be sent to him rather than being put directly into
2582
						 * his calendar. No error should be returned.
2583
						 */
2584
						// $errorSetResource = 2;
2585
						$this->nonAcceptingResources[] = $resourceRecipients[$i];
2586
					}
2587
					else {
2588
						if ($declineRecurringMeetingRequests && !$cancel) {
2589
							// Check if appointment is recurring
2590
							if ($messageprops[$this->proptags['recurring']]) {
2591
								$this->errorSetResource = 3;
2592
							}
2593
						}
2594
						if ($declineConflictingMeetingRequests && !$cancel) {
2595
							// Check for conflicting items
2596
							if ($calFolder && $this->isMeetingConflicting($message, $userStore, $calFolder)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $calFolder does not seem to be defined for all execution paths leading up to this point.
Loading history...
2597
								$this->errorSetResource = 4; // Conflict
2598
							}
2599
						}
2600
					}
2601
				}
2602
			}
2603
2604
			if (!$this->errorSetResource && $accessToFolder) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->errorSetResource of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2605
				/**
2606
				 * First search on GlobalID(0x3)
2607
				 * 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.
2608
				 * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesn't matter if search is based on GlobalID.
2609
				 */
2610
				$rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
2611
2612
				/*
2613
				 * If no entry is found then
2614
				 * 1) Resource doesn't have meeting in Calendar. Seriously!!
2615
				 * OR
2616
				 * 2) We were looking for occurrence item but Resource has whole series
2617
				 */
2618
				if (empty($rows)) {
2619
					/**
2620
					 * Now search on CleanGlobalID(0x23) WHY???
2621
					 * Because we are looking recurring item.
2622
					 *
2623
					 * Possible results of this search
2624
					 * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID
2625
					 * 2) If Resource was booked for whole series then it should return series.
2626
					 */
2627
					$rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
2628
2629
					$newResourceMsg = false;
2630
					if (!empty($rows)) {
2631
						// Since we are looking for recurring item, open every result and check for 'recurring' property.
2632
						foreach ($rows as $row) {
2633
							$ResourceMsg = mapi_msgstore_openentry($userStore, $row);
2634
							$ResourceMsgProps = mapi_getprops($ResourceMsg, [$this->proptags['recurring']]);
2635
2636
							if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2637
								$newResourceMsg = $ResourceMsg;
2638
								break;
2639
							}
2640
						}
2641
					}
2642
2643
					// Still no results found. I giveup, create new message.
2644
					if (!$newResourceMsg) {
2645
						$newResourceMsg = mapi_folder_createmessage($calFolder);
0 ignored issues
show
The function mapi_folder_createmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2645
						$newResourceMsg = /** @scrutinizer ignore-call */ mapi_folder_createmessage($calFolder);
Loading history...
2646
					}
2647
				}
2648
				else {
2649
					$newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]);
2650
				}
2651
2652
				// Prefix the subject if needed
2653
				if ($prefix && isset($messageprops[PR_SUBJECT])) {
0 ignored issues
show
The constant PR_SUBJECT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2654
					$messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT];
2655
				}
2656
2657
				// Set status to cancelled if needed
2658
				$messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy)
2659
				if ($cancel) {
2660
					$messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled
2661
					$messageprops[$this->proptags['busystatus']] = fbFree; // Free
2662
				}
2663
				else {
2664
					$messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
2665
				}
2666
				$messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource automatically accepts the appointment
2667
2668
				$messageprops[PR_MESSAGE_CLASS] = 'IPM.Appointment';
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2669
2670
				// Remove the PR_ICON_INDEX as it is not needed in the sent message.
2671
				$messageprops[PR_ICON_INDEX] = null;
0 ignored issues
show
The constant PR_ICON_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2672
				$messageprops[PR_RESPONSE_REQUESTED] = true;
0 ignored issues
show
The constant PR_RESPONSE_REQUESTED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2673
2674
				// get the store of organizer, in case of delegates it will be delegate store
2675
				$defaultStore = $this->openDefaultStore();
2676
2677
				$storeProps = mapi_getprops($this->store, [PR_ENTRYID]);
2678
				$defaultStoreProps = mapi_getprops($defaultStore, [PR_ENTRYID]);
2679
2680
				// @FIXME use entryid comparison functions here
2681
				if ($storeProps[PR_ENTRYID] !== $defaultStoreProps[PR_ENTRYID]) {
2682
					// get delegate information
2683
					$addrInfo = $this->getOwnerAddress($defaultStore, false);
2684
2685
					if (!empty($addrInfo)) {
2686
						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2687
2688
						$messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
0 ignored issues
show
The constant PR_SENDER_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2689
						$messageprops[PR_SENDER_NAME] = $ownername;
0 ignored issues
show
The constant PR_SENDER_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2690
						$messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
0 ignored issues
show
The constant PR_SENDER_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2691
						$messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
0 ignored issues
show
The constant PR_SENDER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2692
						$messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
0 ignored issues
show
The constant PR_SENDER_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2693
					}
2694
2695
					// get delegator information
2696
					$addrInfo = $this->getOwnerAddress($this->store, false);
2697
2698
					if (!empty($addrInfo)) {
2699
						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2700
2701
						$messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2702
						$messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2703
						$messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2704
						$messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2705
						$messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2706
					}
2707
				}
2708
				else {
2709
					// get organizer information
2710
					$addrInfo = $this->getOwnerAddress($this->store);
2711
2712
					if (!empty($addrInfo)) {
2713
						list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrInfo;
2714
2715
						$messageprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
2716
						$messageprops[PR_SENDER_NAME] = $ownername;
2717
						$messageprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
2718
						$messageprops[PR_SENDER_ENTRYID] = $ownerentryid;
2719
						$messageprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
2720
2721
						$messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
2722
						$messageprops[PR_SENT_REPRESENTING_NAME] = $ownername;
2723
						$messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
2724
						$messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
2725
						$messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
2726
					}
2727
				}
2728
2729
				$messageprops[$this->proptags['replytime']] = time();
2730
2731
				if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2732
					$recurr = new Recurrence($userStore, $newResourceMsg);
2733
2734
					// Copy recipients list
2735
					$reciptable = mapi_message_getrecipienttable($message);
2736
					$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2737
2738
					// add owner to recipient table
2739
					$this->addOrganizer($messageprops, $recips, true);
2740
2741
					// Update occurrence
2742
					if ($recurr->isException($basedate)) {
2743
						$recurr->modifyException($messageprops, $basedate, $recips);
2744
					}
2745
					else {
2746
						$recurr->createException($messageprops, $basedate, false, $recips);
2747
					}
2748
				}
2749
				else {
2750
					mapi_setprops($newResourceMsg, $messageprops);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2750
					/** @scrutinizer ignore-call */ 
2751
     mapi_setprops($newResourceMsg, $messageprops);
Loading history...
2751
2752
					// Copy attachments
2753
					$this->replaceAttachments($message, $newResourceMsg);
2754
2755
					// Copy all recipients too
2756
					$this->replaceRecipients($message, $newResourceMsg);
2757
2758
					// Now add organizer also to recipient table
2759
					$recips = [];
2760
					$this->addOrganizer($messageprops, $recips);
2761
2762
					mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2762
					/** @scrutinizer ignore-call */ 
2763
     mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
Loading history...
2763
				}
2764
2765
				mapi_savechanges($newResourceMsg);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2765
				/** @scrutinizer ignore-call */ 
2766
    mapi_savechanges($newResourceMsg);
Loading history...
2766
2767
				$resourceRecipData[] = [
2768
					'store' => $userStore,
2769
					'folder' => $calFolder,
2770
					'msg' => $newResourceMsg,
2771
				];
2772
				$this->includesResources = true;
2773
			}
2774
			else {
2775
				/*
2776
				 * If no other errors occurred and you have no access to the
2777
				 * folder of the resource, throw an error=1.
2778
				 */
2779
				if (!$this->errorSetResource) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->errorSetResource of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2780
					$this->errorSetResource = 1;
2781
				}
2782
2783
				for ($j = 0, $len = count($resourceRecipData); $j < $len; ++$j) {
2784
					// Get the EntryID
2785
					$props = mapi_message_getprops($resourceRecipData[$j]['msg']);
0 ignored issues
show
The function mapi_message_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2785
					$props = /** @scrutinizer ignore-call */ mapi_message_getprops($resourceRecipData[$j]['msg']);
Loading history...
2786
2787
					mapi_folder_deletemessages($resourceRecipData[$j]['folder'], [$props[PR_ENTRYID]], DELETE_HARD_DELETE);
0 ignored issues
show
The function mapi_folder_deletemessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2787
					/** @scrutinizer ignore-call */ 
2788
     mapi_folder_deletemessages($resourceRecipData[$j]['folder'], [$props[PR_ENTRYID]], DELETE_HARD_DELETE);
Loading history...
2788
				}
2789
				$this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME];
2790
			}
2791
			++$i;
2792
		}
2793
2794
		/*
2795
		 * Set the BCC-recipients (resources) tackstatus to accepted.
2796
		 */
2797
		// Get resource recipients
2798
		$getResourcesRestriction = [
2799
			RES_PROPERTY,
2800
			[
2801
				RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2802
				ULPROPTAG => PR_RECIPIENT_TYPE,
2803
				VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2804
			],
2805
		];
2806
		$recipienttable = mapi_message_getrecipienttable($message);
2807
		$resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
2808
		if (!empty($resourceRecipients)) {
2809
			// Set Tracking status of resource recipients to olResponseAccepted (3)
2810
			for ($i = 0, $len = count($resourceRecipients); $i < $len; ++$i) {
2811
				$resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted;
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2812
				$resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time();
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2813
			}
2814
			mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients);
2815
		}
2816
2817
		return $resourceRecipData;
2818
	}
2819
2820
	/**
2821
	 * Function which save an exception into recurring item.
2822
	 *
2823
	 * @param resource $recurringItem  reference to MAPI_message of recurring item
2824
	 * @param resource $occurrenceItem reference to MAPI_message of occurrence
2825
	 * @param string   $basedate       basedate of occurrence
2826
	 * @param bool     $move           if true then occurrence item is deleted
2827
	 * @param bool     $tentative      true if user has tentatively accepted it or false if user has accepted it
2828
	 * @param bool     $userAction     true if user has manually responded to meeting request
2829
	 * @param resource $store          user store
2830
	 * @param bool     $isDelegate     true if delegate is processing this meeting request
2831
	 */
2832
	public function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move, $tentative, $userAction, $store, $isDelegate = false): void {
2833
		$recurr = new Recurrence($store, $recurringItem);
2834
2835
		// Copy properties from meeting request
2836
		$exception_props = mapi_getprops($occurrenceItem);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2836
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2837
2838
		// Copy recipients list
2839
		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2839
		$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($occurrenceItem);
Loading history...
2840
		// If delegate, then do not add the delegate in recipients
2841
		if ($isDelegate) {
2842
			$delegate = mapi_getprops($this->message, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
0 ignored issues
show
The constant PR_RECEIVED_BY_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2843
			$res = [
2844
				RES_PROPERTY,
2845
				[
2846
					RELOP => RELOP_NE,
2847
					ULPROPTAG => PR_EMAIL_ADDRESS,
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2848
					VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2849
				],
2850
			];
2851
			$recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2851
			$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops, $res);
Loading history...
2852
		}
2853
		else {
2854
			$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2855
		}
2856
2857
		// add owner to recipient table
2858
		$this->addOrganizer($exception_props, $recips, true);
2859
2860
		// add delegator to meetings
2861
		if ($isDelegate) {
2862
			$this->addDelegator($exception_props, $recips);
2863
		}
2864
2865
		$exception_props[$this->proptags['meetingstatus']] = olMeetingReceived;
2866
		$exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
2867
2868
		if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
2869
			if ($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
2870
				$exception_props[$this->proptags['busystatus']] = fbTentative;
2871
			}
2872
			else {
2873
				$exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
2874
			}
2875
		// we already have intendedbusystatus value in $exception_props so no need to copy it
2876
		}
2877
		else {
2878
			$exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
2879
		}
2880
2881
		if ($userAction) {
2882
			$addrInfo = $this->getOwnerAddress($this->store);
2883
2884
			// if user has responded then set replytime and name
2885
			$exception_props[$this->proptags['replytime']] = time();
2886
			if (!empty($addrInfo)) {
2887
				$exception_props[$this->proptags['apptreplyname']] = $addrInfo[0];
2888
			}
2889
		}
2890
2891
		if ($recurr->isException($basedate)) {
2892
			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2893
		}
2894
		else {
2895
			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2896
		}
2897
2898
		// Move the occurrenceItem to the waste basket
2899
		if ($move) {
2900
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2901
			$sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
0 ignored issues
show
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2901
			$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
Loading history...
2902
			mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2902
			/** @scrutinizer ignore-call */ 
2903
   mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2903
		}
2904
2905
		mapi_savechanges($recurringItem);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2905
		/** @scrutinizer ignore-call */ 
2906
  mapi_savechanges($recurringItem);
Loading history...
2906
	}
2907
2908
	/**
2909
	 * Function which merges an exception mapi message to recurring message.
2910
	 * This will be used when we receive recurring meeting request and we already have an exception message
2911
	 * of same meeting in calendar and we need to remove that exception message and add it to attachment table
2912
	 * of recurring meeting.
2913
	 *
2914
	 * @param resource $recurringItem  reference to MAPI_message of recurring item
2915
	 * @param resource $occurrenceItem reference to MAPI_message of occurrence
2916
	 * @param mixed    $basedate       basedate of occurrence
2917
	 * @param resource $store          user store
2918
	 */
2919
	public function mergeException(&$recurringItem, &$occurrenceItem, $basedate, $store): void {
2920
		$recurr = new Recurrence($store, $recurringItem);
2921
2922
		// Copy properties from meeting request
2923
		$exception_props = mapi_getprops($occurrenceItem);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2923
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2924
2925
		// Get recipient list from message and add it to exception attachment
2926
		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2926
		$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($occurrenceItem);
Loading history...
2927
		$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2927
		$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops);
Loading history...
2928
2929
		if ($recurr->isException($basedate)) {
2930
			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2931
		}
2932
		else {
2933
			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2934
		}
2935
2936
		// Move the occurrenceItem to the waste basket
2937
		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2938
		$sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
0 ignored issues
show
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2938
		$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
Loading history...
2939
		mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
The function mapi_folder_copymessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2939
		/** @scrutinizer ignore-call */ 
2940
  mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2940
2941
		mapi_savechanges($recurringItem);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2941
		/** @scrutinizer ignore-call */ 
2942
  mapi_savechanges($recurringItem);
Loading history...
2942
	}
2943
2944
	/**
2945
	 * Function which submits meeting request based on arguments passed to it.
2946
	 *
2947
	 * @param resource $message        MAPI_message whose meeting request is to be sent
2948
	 * @param bool     $cancel         if true send request, else send cancellation
2949
	 * @param mixed    $prefix         subject prefix
2950
	 * @param mixed    $basedate       basedate for an occurrence
2951
	 * @param mixed    $recurObject    recurrence object of mr
2952
	 * @param bool     $copyExceptions When sending update mail for recurring item then we don't send exceptions in attachments
2953
	 * @param mixed    $modifiedRecips
2954
	 * @param mixed    $deletedRecips
2955
	 */
2956
	public function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $modifiedRecips = false, $deletedRecips = false): void {
2957
		$newmessageprops = $messageprops = mapi_getprops($this->message);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2957
		$newmessageprops = $messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message);
Loading history...
2958
		$new = $this->createOutgoingMessage();
2959
2960
		// Copy the entire message into the new meeting request message
2961
		if ($basedate) {
2962
			// messageprops contains properties of whole recurring series
2963
			// and newmessageprops contains properties of exception item
2964
			$newmessageprops = mapi_getprops($message);
2965
2966
			// Ensure that the correct basedate is set in the new message
2967
			$newmessageprops[$this->proptags['basedate']] = $basedate;
2968
2969
			// Set isRecurring to false, because this is an exception
2970
			$newmessageprops[$this->proptags['recurring']] = false;
2971
2972
			// set LID_IS_EXCEPTION to true
2973
			$newmessageprops[$this->proptags['is_exception']] = true;
2974
2975
			// Set to high importance
2976
			if ($cancel) {
2977
				$newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;
0 ignored issues
show
The constant PR_IMPORTANCE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2978
			}
2979
2980
			// Set startdate and enddate of exception
2981
			if ($cancel && $recurObject) {
2982
				$newmessageprops[$this->proptags['startdate']] = $recurObject->getOccurrenceStart($basedate);
2983
				$newmessageprops[$this->proptags['duedate']] = $recurObject->getOccurrenceEnd($basedate);
2984
			}
2985
2986
			// Set basedate in guid (0x3)
2987
			$newmessageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2988
			$newmessageprops[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2989
			$newmessageprops[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
0 ignored issues
show
The constant PR_OWNER_APPT_ID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2990
2991
			// Get deleted recipiets from exception msg
2992
			$restriction = [
2993
				RES_AND,
2994
				[
2995
					[
2996
						RES_BITMASK,
2997
						[
2998
							ULTYPE => BMR_NEZ,
2999
							ULPROPTAG => PR_RECIPIENT_FLAGS,
0 ignored issues
show
The constant PR_RECIPIENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3000
							ULMASK => recipExceptionalDeleted,
3001
						],
3002
					],
3003
					[
3004
						RES_BITMASK,
3005
						[
3006
							ULTYPE => BMR_EQZ,
3007
							ULPROPTAG => PR_RECIPIENT_FLAGS,
3008
							ULMASK => recipOrganizer,
3009
						],
3010
					],
3011
				],
3012
			];
3013
3014
			// In direct-booking mode, we don't need to send cancellations to resources
3015
			if ($this->enableDirectBooking) {
3016
				$restriction[1][] = [
3017
					RES_PROPERTY,
3018
					[
3019
						RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3020
						ULPROPTAG => PR_RECIPIENT_TYPE,
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3021
						VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3022
					],
3023
				];
3024
			}
3025
3026
			$recipienttable = mapi_message_getrecipienttable($message);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3026
			$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3027
			$recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3027
			$recipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
Loading history...
3028
3029
			if (!$deletedRecips) {
3030
				$deletedRecips = array_merge([], $recipients);
3031
			}
3032
			else {
3033
				$deletedRecips = array_merge($deletedRecips, $recipients);
3034
			}
3035
		}
3036
3037
		// Remove the PR_ICON_INDEX as it is not needed in the sent message.
3038
		$newmessageprops[PR_ICON_INDEX] = null;
0 ignored issues
show
The constant PR_ICON_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3039
		$newmessageprops[PR_RESPONSE_REQUESTED] = true;
0 ignored issues
show
The constant PR_RESPONSE_REQUESTED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3040
3041
		// PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3042
		$newmessageprops[PR_START_DATE] = $newmessageprops[$this->proptags['startdate']];
0 ignored issues
show
The constant PR_START_DATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3043
		$newmessageprops[PR_END_DATE] = $newmessageprops[$this->proptags['duedate']];
0 ignored issues
show
The constant PR_END_DATE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3044
3045
		// Set updatecounter/AppointmentSequenceNumber
3046
		// get the value of latest updatecounter for the whole series and use it
3047
		$newmessageprops[$this->proptags['updatecounter']] = $messageprops[$this->proptags['last_updatecounter']];
3048
3049
		$meetingTimeInfo = $this->getMeetingTimeInfo();
3050
3051
		if ($meetingTimeInfo) {
3052
			// Needs to unset PR_HTML and PR_RTF_COMPRESSED props
3053
			// because while canceling meeting requests with edit text
3054
			// will override the PR_BODY because body value is not consistent with
3055
			// PR_HTML and PR_RTF_COMPRESSED value so in this case PR_RTF_COMPRESSED will
3056
			// get priority which override the PR_BODY value.
3057
			unset($newmessageprops[PR_HTML], $newmessageprops[PR_RTF_COMPRESSED]);
0 ignored issues
show
The constant PR_HTML was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RTF_COMPRESSED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3058
3059
			$newmessageprops[PR_BODY] = $meetingTimeInfo;
0 ignored issues
show
The constant PR_BODY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3060
		}
3061
3062
		// Send all recurrence info in mail, if this is a recurrence meeting.
3063
		if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]) {
3064
			if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
3065
				$newmessageprops[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
3066
			}
3067
			$newmessageprops[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
3068
			$newmessageprops[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
3069
			$newmessageprops[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
3070
3071
			if ($recurObject) {
3072
				$this->generateRecurDates($recurObject, $messageprops, $newmessageprops);
3073
			}
3074
		}
3075
3076
		if (isset($newmessageprops[$this->proptags['counter_proposal']])) {
3077
			unset($newmessageprops[$this->proptags['counter_proposal']]);
3078
		}
3079
3080
		// Prefix the subject if needed
3081
		if ($prefix && isset($newmessageprops[PR_SUBJECT])) {
0 ignored issues
show
The constant PR_SUBJECT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3082
			$newmessageprops[PR_SUBJECT] = $prefix . $newmessageprops[PR_SUBJECT];
3083
		}
3084
3085
		if (isset($newmessageprops[$this->proptags['categories']]) &&
3086
			!empty($newmessageprops[$this->proptags['categories']])) {
3087
			unset($newmessageprops[$this->proptags['categories']]);
3088
		}
3089
		mapi_setprops($new, $newmessageprops);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3089
		/** @scrutinizer ignore-call */ 
3090
  mapi_setprops($new, $newmessageprops);
Loading history...
3090
3091
		// Copy attachments
3092
		$this->replaceAttachments($message, $new, $copyExceptions);
3093
3094
		// Retrieve only those recipient who should receive this meeting request.
3095
		$stripResourcesRestriction = [
3096
			RES_AND,
3097
			[
3098
				[
3099
					RES_BITMASK,
3100
					[
3101
						ULTYPE => BMR_EQZ,
3102
						ULPROPTAG => PR_RECIPIENT_FLAGS,
3103
						ULMASK => recipExceptionalDeleted,
3104
					],
3105
				],
3106
				[
3107
					RES_BITMASK,
3108
					[
3109
						ULTYPE => BMR_EQZ,
3110
						ULPROPTAG => PR_RECIPIENT_FLAGS,
3111
						ULMASK => recipOrganizer,
3112
					],
3113
				],
3114
			],
3115
		];
3116
3117
		// In direct-booking mode, resources do not receive a meeting request
3118
		if ($this->enableDirectBooking) {
3119
			$stripResourcesRestriction[1][] = [
3120
				RES_PROPERTY,
3121
				[
3122
					RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3123
					ULPROPTAG => PR_RECIPIENT_TYPE,
3124
					VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3125
				],
3126
			];
3127
		}
3128
3129
		// If no recipients were explicitly provided, we will send the update to all
3130
		// recipients from the meeting.
3131
		if ($modifiedRecips === false) {
3132
			$recipienttable = mapi_message_getrecipienttable($message);
3133
			$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3134
3135
			if ($basedate && empty($modifiedRecips)) {
3136
				// Retrieve full list
3137
				$recipienttable = mapi_message_getrecipienttable($this->message);
3138
				$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops);
3139
3140
				// Save recipients in exceptions
3141
				mapi_message_modifyrecipients($message, MODRECIP_ADD, $modifiedRecips);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3141
				/** @scrutinizer ignore-call */ 
3142
    mapi_message_modifyrecipients($message, MODRECIP_ADD, $modifiedRecips);
Loading history...
3142
3143
				// Now retrieve only those recipient who should receive this meeting request.
3144
				$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3145
			}
3146
		}
3147
3148
		// @TODO: handle nonAcceptingResources
3149
		/*
3150
		 * Add resource recipients that did not automatically accept the meeting request.
3151
		 * (note: meaning that they did not decline the meeting request)
3152
		 */ /*
3153
		for($i=0;$i<count($this->nonAcceptingResources);$i++){
3154
			$recipients[] = $this->nonAcceptingResources[$i];
3155
		}*/
3156
3157
		if (!empty($modifiedRecips)) {
3158
			// Strip out the sender/'owner' recipient
3159
			mapi_message_modifyrecipients($new, MODRECIP_ADD, $modifiedRecips);
3160
3161
			// Set some properties that are different in the sent request than
3162
			// in the item in our calendar
3163
3164
			// we should store busystatus value to intendedbusystatus property, because busystatus for outgoing meeting request
3165
			// should always be fbTentative
3166
			$newmessageprops[$this->proptags['intendedbusystatus']] = isset($newmessageprops[$this->proptags['busystatus']]) ? $newmessageprops[$this->proptags['busystatus']] : $messageprops[$this->proptags['busystatus']];
3167
			$newmessageprops[$this->proptags['busystatus']] = fbTentative; // The default status when not accepted
3168
			$newmessageprops[$this->proptags['responsestatus']] = olResponseNotResponded; // The recipient has not responded yet
3169
			$newmessageprops[$this->proptags['attendee_critical_change']] = time();
3170
			$newmessageprops[$this->proptags['owner_critical_change']] = time();
3171
			$newmessageprops[$this->proptags['meetingtype']] = mtgRequest;
3172
3173
			if ($cancel) {
3174
				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3175
				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3176
				$newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3177
			}
3178
			else {
3179
				$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Request';
3180
				$newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request
3181
			}
3182
3183
			mapi_setprops($new, $newmessageprops);
3184
			mapi_savechanges($new);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3184
			/** @scrutinizer ignore-call */ 
3185
   mapi_savechanges($new);
Loading history...
3185
3186
			// Submit message to non-resource recipients
3187
			mapi_message_submitmessage($new);
0 ignored issues
show
The function mapi_message_submitmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3187
			/** @scrutinizer ignore-call */ 
3188
   mapi_message_submitmessage($new);
Loading history...
3188
		}
3189
3190
		// Search through the deleted recipients, and see if any of them is also
3191
		// listed as a recipient to whom we have sent an update. As we don't
3192
		// want to send a cancellation message to recipients who will also receive
3193
		// an meeting update, we have to filter those recipients out.
3194
		if ($deletedRecips) {
3195
			$tmp = [];
3196
3197
			foreach ($deletedRecips as $delRecip) {
3198
				$found = false;
3199
3200
				// Search if the deleted recipient can be found inside
3201
				// the updated recipients as well.
3202
				foreach ($modifiedRecips as $recip) {
3203
					if ($this->compareABEntryIDs($recip[PR_ENTRYID], $delRecip[PR_ENTRYID])) {
3204
						$found = true;
3205
						break;
3206
					}
3207
				}
3208
3209
				// If the recipient was not found, it truly is deleted,
3210
				// and we can safely send a cancellation message
3211
				if (!$found) {
3212
					$tmp[] = $delRecip;
3213
				}
3214
			}
3215
3216
			$deletedRecips = $tmp;
3217
		}
3218
3219
		// Send cancellation to deleted attendees
3220
		if ($deletedRecips && !empty($deletedRecips)) {
3221
			$new = $this->createOutgoingMessage();
3222
3223
			mapi_message_modifyrecipients($new, MODRECIP_ADD, $deletedRecips);
3224
3225
			$newmessageprops[PR_MESSAGE_CLASS] = 'IPM.Schedule.Meeting.Canceled';
3226
			$newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request
3227
			$newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free
3228
			$newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH;	// HIGH Importance
3229
			if (isset($newmessageprops[PR_SUBJECT])) {
3230
				$newmessageprops[PR_SUBJECT] = dgettext('zarafa', 'Canceled') . ': ' . $newmessageprops[PR_SUBJECT];
3231
			}
3232
3233
			mapi_setprops($new, $newmessageprops);
3234
			mapi_savechanges($new);
3235
3236
			// Submit message to non-resource recipients
3237
			mapi_message_submitmessage($new);
3238
		}
3239
3240
		// Set properties on meeting object in calendar
3241
		// Set requestsent to 'true' (turns on 'tracking', etc)
3242
		$props = [];
3243
		$props[$this->proptags['meetingstatus']] = olMeeting;
3244
		$props[$this->proptags['responsestatus']] = olResponseOrganized;
3245
		// Only set the 'requestsent' property if it wasn't set previously yet,
3246
		// this ensures we will not accidentally set it from true to false.
3247
		if (!isset($messageprops[$this->proptags['requestsent']]) || $messageprops[$this->proptags['requestsent']] !== true) {
3248
			$props[$this->proptags['requestsent']] = !empty($modifiedRecips) || ($this->includesResources && !$this->errorSetResource);
3249
		}
3250
		$props[$this->proptags['attendee_critical_change']] = time();
3251
		$props[$this->proptags['owner_critical_change']] = time();
3252
		$props[$this->proptags['meetingtype']] = mtgRequest;
3253
		// save the new updatecounter to exception/recurring series/normal meeting
3254
		$props[$this->proptags['updatecounter']] = $newmessageprops[$this->proptags['updatecounter']];
3255
3256
		// PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar
3257
		$props[PR_START_DATE] = $messageprops[$this->proptags['startdate']];
3258
		$props[PR_END_DATE] = $messageprops[$this->proptags['duedate']];
3259
3260
		mapi_setprops($message, $props);
3261
3262
		// saving of these properties on calendar item should be handled by caller function
3263
		// based on sending meeting request was successful or not
3264
	}
3265
3266
	/**
3267
	 * OL2007 uses these 4 properties to specify occurrence that should be updated.
3268
	 * ical generates RECURRENCE-ID property based on exception's basedate (PidLidExceptionReplaceTime),
3269
	 * but OL07 doesn't send this property, so ical will generate RECURRENCE-ID property based on date
3270
	 * from GlobalObjId and time from StartRecurTime property, so we are sending basedate property and
3271
	 * also additionally we are sending these properties.
3272
	 * Ref: MS-OXCICAL 2.2.1.20.20 Property: RECURRENCE-ID.
3273
	 *
3274
	 * @param object $recurObject     instance of recurrence class for this message
3275
	 * @param array  $messageprops    properties of meeting object that is going to be sent
3276
	 * @param array  $newmessageprops properties of meeting request/response that is going to be sent
3277
	 */
3278
	public function generateRecurDates($recurObject, $messageprops, &$newmessageprops): void {
3279
		if ($messageprops[$this->proptags['startdate']] && $messageprops[$this->proptags['duedate']]) {
3280
			$startDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['startdate']]));
3281
			$endDate = date('Y:n:j:G:i:s', $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['duedate']]));
3282
3283
			$startDate = explode(':', $startDate);
3284
			$endDate = explode(':', $endDate);
3285
3286
			// [0] => year, [1] => month, [2] => day, [3] => hour, [4] => minutes, [5] => seconds
3287
			// RecurStartDate = year * 512 + month_number * 32 + day_number
3288
			$newmessageprops[$this->proptags['start_recur_date']] = (((int) $startDate[0]) * 512) + (((int) $startDate[1]) * 32) + ((int) $startDate[2]);
3289
			// RecurStartTime = hour * 4096 + minutes * 64 + seconds
3290
			$newmessageprops[$this->proptags['start_recur_time']] = (((int) $startDate[3]) * 4096) + (((int) $startDate[4]) * 64) + ((int) $startDate[5]);
3291
3292
			$newmessageprops[$this->proptags['end_recur_date']] = (((int) $endDate[0]) * 512) + (((int) $endDate[1]) * 32) + ((int) $endDate[2]);
3293
			$newmessageprops[$this->proptags['end_recur_time']] = (((int) $endDate[3]) * 4096) + (((int) $endDate[4]) * 64) + ((int) $endDate[5]);
3294
		}
3295
	}
3296
3297
	/**
3298
	 * Function will create a new outgoing message that will be used to send meeting mail.
3299
	 *
3300
	 * @param mixed $store (optional) store that is used when creating response, if delegate is creating outgoing mail
3301
	 *                     then this would point to delegate store
3302
	 *
3303
	 * @return resource outgoing mail that is created and can be used for sending it
3304
	 */
3305
	public function createOutgoingMessage($store = false) {
3306
		// get logged in user's store that will be used to send mail, for delegate this will be
3307
		// delegate store
3308
		$userStore = $this->openDefaultStore();
3309
3310
		$sentprops = [];
3311
		$outbox = $this->openDefaultOutbox($userStore);
3312
3313
		$outgoing = mapi_folder_createmessage($outbox);
0 ignored issues
show
The function mapi_folder_createmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3313
		$outgoing = /** @scrutinizer ignore-call */ mapi_folder_createmessage($outbox);
Loading history...
3314
3315
		// check if $store is set and it is not equal to $defaultStore (means its the delegation case)
3316
		if ($store !== false) {
3317
			$storeProps = mapi_getprops($store, [PR_ENTRYID]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3317
			$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($store, [PR_ENTRYID]);
Loading history...
3318
			$userStoreProps = mapi_getprops($userStore, [PR_ENTRYID]);
3319
3320
			// @FIXME use entryid comparison functions here
3321
			if ($storeProps[PR_ENTRYID] !== $userStoreProps[PR_ENTRYID]) {
3322
				// get the delegator properties and set it into outgoing mail
3323
				$delegatorDetails = $this->getOwnerAddress($store, false);
3324
3325
				if (!empty($delegatorDetails)) {
3326
					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegatorDetails;
3327
					$sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3328
					$sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3329
					$sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3330
					$sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3331
					$sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
0 ignored issues
show
The constant PR_SENT_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3332
				}
3333
3334
				// get the delegate properties and set it into outgoing mail
3335
				$delegateDetails = $this->getOwnerAddress($userStore, false);
3336
3337
				if (!empty($delegateDetails)) {
3338
					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegateDetails;
3339
					$sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
0 ignored issues
show
The constant PR_SENDER_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3340
					$sentprops[PR_SENDER_NAME] = $ownername;
0 ignored issues
show
The constant PR_SENDER_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3341
					$sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
0 ignored issues
show
The constant PR_SENDER_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3342
					$sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
0 ignored issues
show
The constant PR_SENDER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3343
					$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
0 ignored issues
show
The constant PR_SENDER_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3344
				}
3345
			}
3346
		}
3347
		else {
3348
			// normal user is sending mail, so both set of properties will be same
3349
			$userDetails = $this->getOwnerAddress($userStore);
3350
3351
			if (!empty($userDetails)) {
3352
				list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $userDetails;
3353
				$sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3354
				$sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3355
				$sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3356
				$sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3357
				$sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3358
3359
				$sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3360
				$sentprops[PR_SENDER_NAME] = $ownername;
3361
				$sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3362
				$sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3363
				$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3364
			}
3365
		}
3366
3367
		$sentprops[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($userStore);
0 ignored issues
show
The constant PR_SENTMAIL_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3368
3369
		mapi_setprops($outgoing, $sentprops);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3369
		/** @scrutinizer ignore-call */ 
3370
  mapi_setprops($outgoing, $sentprops);
Loading history...
3370
3371
		return $outgoing;
3372
	}
3373
3374
	/**
3375
	 * Function which checks that meeting in attendee's calendar is already updated
3376
	 * and we are checking an old meeting request. This function also will update property
3377
	 * meetingtype to indicate that its out of date meeting request.
3378
	 *
3379
	 * @return bool true if meeting request is outofdate else false if it is new
3380
	 */
3381
	public function isMeetingOutOfDate() {
3382
		$result = false;
3383
3384
		$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']]);
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3384
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['meetingtype'], $this->proptags['owner_critical_change']]);
Loading history...
3385
3386
		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS])) {
3387
			return $result;
3388
		}
3389
3390
		if (isset($props[$this->proptags['meetingtype']]) && ($props[$this->proptags['meetingtype']] & mtgOutOfDate) == mtgOutOfDate) {
3391
			return true;
3392
		}
3393
3394
		// get the basedate to check for exception
3395
		$basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]);
3396
3397
		$calendarItem = $this->getCorrespondentCalendarItem(true);
3398
3399
		// if basedate is provided and we could not find the item then it could be that we are checking
3400
		// an exception so get the exception and check it
3401
		if ($basedate !== false && $calendarItem !== false) {
3402
			$exception = $this->getExceptionItem($calendarItem, $basedate);
3403
3404
			if ($exception !== false) {
0 ignored issues
show
The condition $exception !== false is always true.
Loading history...
3405
				// we are able to find the exception compare with it
3406
				$calendarItem = $exception;
3407
			}
3408
			// we are not able to find exception, could mean that a significant change has occurred on series
3409
			// and it deleted all exceptions, so compare with series
3410
			// $calendarItem already contains reference to series
3411
		}
3412
3413
		if ($calendarItem !== false) {
3414
			$calendarItemProps = mapi_getprops($calendarItem, [
3415
				$this->proptags['owner_critical_change'],
3416
				$this->proptags['updatecounter'],
3417
			]);
3418
3419
			$updateCounter = (isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]);
3420
3421
			$criticalChange = (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']]);
3422
3423
			if ($updateCounter || $criticalChange) {
3424
				// meeting request is out of date, set properties to indicate this
3425
				mapi_setprops($this->message, [$this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033]);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3425
				/** @scrutinizer ignore-call */ 
3426
    mapi_setprops($this->message, [$this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033]);
Loading history...
The constant PR_ICON_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3426
				mapi_savechanges($this->message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3426
				/** @scrutinizer ignore-call */ 
3427
    mapi_savechanges($this->message);
Loading history...
3427
3428
				$result = true;
3429
			}
3430
		}
3431
3432
		return $result;
3433
	}
3434
3435
	/**
3436
	 * Function which checks that if we have received a meeting response for an updated meeting in organizer's calendar.
3437
	 *
3438
	 * @param mixed $basedate basedate of the exception if we want to compare with exception
3439
	 *
3440
	 * @return bool true if meeting request is updated later
3441
	 */
3442
	public function isMeetingUpdated($basedate = false) {
3443
		$result = false;
3444
3445
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['updatecounter']]);
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3445
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['updatecounter']]);
Loading history...
3446
3447
		if (!$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS])) {
3448
			return $result;
3449
		}
3450
3451
		$calendarItem = $this->getCorrespondentCalendarItem(true);
3452
3453
		if ($calendarItem !== false) {
3454
			// basedate is provided so open exception
3455
			if ($basedate !== false) {
3456
				$exception = $this->getExceptionItem($calendarItem, $basedate);
3457
3458
				if ($exception !== false) {
0 ignored issues
show
The condition $exception !== false is always true.
Loading history...
3459
					// we are able to find the exception compare with it
3460
					$calendarItem = $exception;
3461
				}
3462
				// we are not able to find exception, could mean that a significant change has occurred on series
3463
				// and it deleted all exceptions, so compare with series
3464
				// $calendarItem already contains reference to series
3465
			}
3466
3467
			if ($calendarItem !== false) {
0 ignored issues
show
The condition $calendarItem !== false is always true.
Loading history...
3468
				$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['updatecounter']]);
3469
3470
				/*
3471
				 * if(message_counter < appointment_counter) meeting object is newer then meeting response (meeting is updated)
3472
				 * if(message_counter >= appointment_counter) meeting is not updated, do normal processing
3473
				 */
3474
				if (isset($calendarItemProps[$this->proptags['updatecounter']], $props[$this->proptags['updatecounter']])) {
3475
					if ($props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]) {
3476
						$result = true;
3477
					}
3478
				}
3479
			}
3480
		}
3481
3482
		return $result;
3483
	}
3484
3485
	/**
3486
	 * Checks if there has been any significant changes on appointment/meeting item.
3487
	 * Significant changes be:
3488
	 * 1) startdate has been changed
3489
	 * 2) duedate has been changed OR
3490
	 * 3) recurrence pattern has been created, modified or removed.
3491
	 *
3492
	 * @param mixed $oldProps
3493
	 * @param mixed $basedate
3494
	 * @param mixed $isRecurrenceChanged for change in recurrence pattern.
3495
	 *                                   true means Recurrence pattern has been changed,
3496
	 *                                   so clear all attendees response
3497
	 */
3498
	public function checkSignificantChanges($oldProps, $basedate, $isRecurrenceChanged = false) {
3499
		$message = null;
3500
		$attach = null;
3501
3502
		// If basedate is specified then we need to open exception message to clear recipient responses
3503
		if ($basedate) {
3504
			$recurrence = new Recurrence($this->store, $this->message);
3505
			if ($recurrence->isException($basedate)) {
3506
				$attach = $recurrence->getExceptionAttachment($basedate);
3507
				if ($attach) {
3508
					$message = mapi_attach_openobj($attach, MAPI_MODIFY);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3508
					$message = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
3509
				}
3510
			}
3511
		}
3512
		else {
3513
			// use normal message or recurring series message
3514
			$message = $this->message;
3515
		}
3516
3517
		if (!$message) {
3518
			return;
3519
		}
3520
3521
		$newProps = mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3521
		$newProps = /** @scrutinizer ignore-call */ mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
Loading history...
3522
3523
		// Check whether message is updated or not.
3524
		if (isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
3525
			return;
3526
		}
3527
3528
		if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) ||
3529
				($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) ||
3530
				$isRecurrenceChanged) {
3531
			$this->clearRecipientResponse($message);
3532
3533
			mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
0 ignored issues
show
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3533
			/** @scrutinizer ignore-call */ 
3534
   mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
Loading history...
3534
3535
			mapi_savechanges($message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3535
			/** @scrutinizer ignore-call */ 
3536
   mapi_savechanges($message);
Loading history...
3536
			if ($attach) { // Also save attachment Object.
3537
				mapi_savechanges($attach);
3538
			}
3539
		}
3540
	}
3541
3542
	/**
3543
	 * Clear responses of all attendees who have replied in past.
3544
	 *
3545
	 * @param resource $message on which responses should be cleared
3546
	 */
3547
	public function clearRecipientResponse($message): void {
3548
		$recipTable = mapi_message_getrecipienttable($message);
0 ignored issues
show
The function mapi_message_getrecipienttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3548
		$recipTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3549
		$recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops);
0 ignored issues
show
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3549
		$recipsRows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipTable, $this->recipprops);
Loading history...
3550
3551
		foreach ($recipsRows as $recipient) {
3552
			if (($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer) {
0 ignored issues
show
The constant PR_RECIPIENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3553
				// Recipient is attendee, set the trackstatus to 'Not Responded'
3554
				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3555
			}
3556
			else {
3557
				// Recipient is organizer, this is not possible, but for safety
3558
				// it is best to clear the trackstatus for him as well by setting
3559
				// the trackstatus to 'Organized'.
3560
				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3561
			}
3562
			mapi_message_modifyrecipients($message, MODRECIP_MODIFY, [$recipient]);
0 ignored issues
show
The function mapi_message_modifyrecipients was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3562
			/** @scrutinizer ignore-call */ 
3563
   mapi_message_modifyrecipients($message, MODRECIP_MODIFY, [$recipient]);
Loading history...
3563
		}
3564
	}
3565
3566
	/**
3567
	 * Function returns correspondent calendar item attached with the meeting request/response/cancellation.
3568
	 * This will only check for actual MAPIMessages in calendar folder, so if a meeting request is
3569
	 * for exception then this function will return recurring series for that meeting request
3570
	 * after that you need to use getExceptionItem function to get exception item that will be
3571
	 * fetched from the attachment table of recurring series MAPIMessage.
3572
	 *
3573
	 * @param bool $open boolean to indicate the function should return entryid or MAPIMessage. Defaults to true.
3574
	 *
3575
	 * @return bool|resource resource of calendar item
3576
	 */
3577
	public function getCorrespondentCalendarItem($open = true) {
3578
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3578
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3579
3580
		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
3581
			// can work only with meeting requests/responses/cancellations
3582
			return false;
3583
		}
3584
3585
		$globalId = $props[$this->proptags['goid']];
3586
		$cleanGlobalId = $props[$this->proptags['goid2']];
3587
3588
		// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3589
		if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3590
			$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3591
3592
			$store = $delegatorStore['store'];
3593
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3594
		}
3595
		else {
3596
			$store = $this->store;
3597
			$calFolder = $this->openDefaultCalendar();
3598
		}
3599
3600
		$basedate = $this->getBasedateFromGlobalID($globalId);
3601
3602
		/**
3603
		 * First search for any appointments which correspond to the $globalId,
3604
		 * this can be the entire series (if the Meeting Request refers to the
3605
		 * entire series), or an particular Occurrence (if the meeting Request
3606
		 * contains a basedate).
3607
		 *
3608
		 * If we cannot find a corresponding item, and the $globalId contains
3609
		 * a $basedate, it might imply that a new exception will have to be
3610
		 * created for a series which is present in the calendar, we can look
3611
		 * that one up by searching for the $cleanGlobalId.
3612
		 */
3613
		$entryids = $this->findCalendarItems($globalId, $calFolder);
3614
		if ($basedate !== false && empty($entryids)) {
3615
			$entryids = $this->findCalendarItems($cleanGlobalId, $calFolder, true);
3616
		}
3617
3618
		// there should be only one item returned
3619
		if (!empty($entryids) && count($entryids) === 1) {
3620
			// return only entryid
3621
			if ($open === false) {
3622
				return $entryids[0];
3623
			}
3624
3625
			// open calendar item and return it
3626
			return mapi_msgstore_openentry($store, $entryids[0]);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3626
			return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryids[0]);
Loading history...
3627
		}
3628
3629
		// no items found in calendar
3630
		return false;
3631
	}
3632
3633
	/**
3634
	 * Function returns exception item based on the basedate passed.
3635
	 *
3636
	 * @param mixed $recurringMessage Resource of Recurring meeting from calendar
3637
	 * @param mixed $basedate         basedate of exception that needs to be returned
3638
	 * @param mixed $store            store that contains the recurring calendar item
3639
	 *
3640
	 * @return entryid or MAPIMessage resource of exception item
0 ignored issues
show
The type entryid was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3641
	 */
3642
	public function getExceptionItem($recurringMessage, $basedate, $store = false) {
3643
		$occurItem = false;
3644
3645
		$props = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3645
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
Loading history...
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3646
3647
		// check if the passed item is recurring series
3648
		if (isset($props[$this->proptags['recurring']]) && $props[$this->proptags['recurring']] !== false) {
3649
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type entryid.
Loading history...
3650
		}
3651
3652
		if ($store === false) {
3653
			// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3654
			if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3655
				$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3656
				$store = $delegatorStore['store'];
3657
			}
3658
			else {
3659
				$store = $this->store;
3660
			}
3661
		}
3662
3663
		$recurr = new Recurrence($store, $recurringMessage);
3664
		$attach = $recurr->getExceptionAttachment($basedate);
3665
		if ($attach) {
3666
			$occurItem = mapi_attach_openobj($attach);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3666
			$occurItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach);
Loading history...
3667
		}
3668
3669
		return $occurItem;
3670
	}
3671
3672
	/**
3673
	 * Function which checks whether received meeting request is either conflicting with other appointments or not.
3674
	 *
3675
	 * @param false|resource $message
3676
	 * @param false|resource $userStore
3677
	 * @param mixed          $calFolder calendar folder for conflict checking
3678
	 *
3679
	 * @return bool|int
3680
	 *
3681
	 * @psalm-return bool|int<1, max>
3682
	 */
3683
	public function isMeetingConflicting($message = false, $userStore = false, $calFolder = false) {
3684
		$returnValue = false;
3685
		$noOfInstances = 0;
3686
3687
		if ($message === false) {
3688
			$message = $this->message;
3689
		}
3690
3691
		$messageProps = mapi_getprops(
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3691
		$messageProps = /** @scrutinizer ignore-call */ mapi_getprops(
Loading history...
3692
			$message,
3693
			[
3694
				PR_MESSAGE_CLASS,
0 ignored issues
show
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3695
				$this->proptags['goid'],
3696
				$this->proptags['goid2'],
3697
				$this->proptags['startdate'],
3698
				$this->proptags['duedate'],
3699
				$this->proptags['recurring'],
3700
				$this->proptags['clipstart'],
3701
				$this->proptags['clipend'],
3702
				PR_RCVD_REPRESENTING_ENTRYID,
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3703
				$this->proptags['basedate'],
3704
				PR_RCVD_REPRESENTING_NAME,
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3705
			]
3706
		);
3707
3708
		if ($userStore === false) {
3709
			$userStore = $this->store;
3710
3711
			// check if delegate is processing the response
3712
			if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
3713
				$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID], [PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
The constant PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3714
3715
				$userStore = $delegatorStore['store'];
3716
				$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3717
			}
3718
		}
3719
3720
		if ($calFolder === false) {
3721
			$calFolder = $this->openDefaultCalendar($userStore);
3722
		}
3723
3724
		if ($calFolder) {
3725
			// Meeting request is recurring, so get all occurrence and check for each occurrence whether it conflicts with other appointments in Calendar.
3726
			if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
3727
				// Apply recurrence class and retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3728
				$recurr = new Recurrence($userStore, $message);
3729
				$items = $recurr->getItems($messageProps[$this->proptags['clipstart']], $messageProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3730
3731
				foreach ($items as $item) {
3732
					// Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3733
					$calendarItems = $recurr->getCalendarItems($userStore, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3734
3735
					foreach ($calendarItems as $calendarItem) {
3736
						if ($calendarItem[$this->proptags['busystatus']] !== fbFree) {
3737
							/*
3738
							 * Only meeting requests have globalID, normal appointments do not have globalID
3739
							 * so if any normal appointment if found then it is assumed to be conflict.
3740
							 */
3741
							if (isset($calendarItem[$this->proptags['goid']])) {
3742
								if ($calendarItem[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) {
3743
									++$noOfInstances;
3744
									break;
3745
								}
3746
							}
3747
							else {
3748
								++$noOfInstances;
3749
								break;
3750
							}
3751
						}
3752
					}
3753
				}
3754
3755
				if ($noOfInstances > 0) {
3756
					$returnValue = $noOfInstances;
3757
				}
3758
			}
3759
			else {
3760
				// Get all items in the timeframe that we want to book, and get the goid and busystatus for each item
3761
				$items = getCalendarItems($userStore, $calFolder, $messageProps[$this->proptags['startdate']], $messageProps[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus']]);
3762
3763
				if (isset($messageProps[$this->proptags['basedate']]) && !empty($messageProps[$this->proptags['basedate']])) {
3764
					$basedate = $messageProps[$this->proptags['basedate']];
3765
					// Get the goid2 from recurring MR which further used to
3766
					// check the resource conflicts item.
3767
					$recurrItemProps = mapi_getprops($this->message, [$this->proptags['goid2']]);
3768
					$messageProps[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid2']], $basedate);
3769
					$messageProps[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
3770
				}
3771
3772
				foreach ($items as $item) {
3773
					if ($item[$this->proptags['busystatus']] !== fbFree) {
3774
						if (isset($item[$this->proptags['goid']])) {
3775
							if (($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid']]) &&
3776
								($item[$this->proptags['goid']] !== $messageProps[$this->proptags['goid2']])) {
3777
								$returnValue = true;
3778
								break;
3779
							}
3780
						}
3781
						else {
3782
							$returnValue = true;
3783
							break;
3784
						}
3785
					}
3786
				}
3787
			}
3788
		}
3789
3790
		return $returnValue;
3791
	}
3792
3793
	/**
3794
	 * Function which adds organizer to recipient list which is passed.
3795
	 * This function also checks if it has organizer.
3796
	 *
3797
	 * @param array $messageProps message properties
3798
	 * @param array $recipients   recipients list of message
3799
	 */
3800
	public function addDelegator($messageProps, &$recipients): void {
3801
		$hasDelegator = false;
3802
		// Check if meeting already has an organizer.
3803
		foreach ($recipients as $key => $recipient) {
3804
			if (isset($messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) && $recipient[PR_EMAIL_ADDRESS] == $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) {
0 ignored issues
show
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RCVD_REPRESENTING_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3805
				$hasDelegator = true;
3806
			}
3807
		}
3808
3809
		if (!$hasDelegator) {
3810
			// Create delegator.
3811
			$delegator = [];
3812
			$delegator[PR_ENTRYID] = $messageProps[PR_RCVD_REPRESENTING_ENTRYID];
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3813
			$delegator[PR_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RCVD_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3814
			$delegator[PR_EMAIL_ADDRESS] = $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS];
3815
			$delegator[PR_RECIPIENT_TYPE] = MAPI_TO;
0 ignored issues
show
The constant PR_RECIPIENT_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3816
			$delegator[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME];
0 ignored issues
show
The constant PR_RECIPIENT_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3817
			$delegator[PR_ADDRTYPE] = empty($messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $messageProps[PR_RCVD_REPRESENTING_ADDRTYPE];
0 ignored issues
show
The constant PR_RCVD_REPRESENTING_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_ADDRTYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3818
			$delegator[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
0 ignored issues
show
The constant PR_RECIPIENT_TRACKSTATUS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3819
			$delegator[PR_RECIPIENT_FLAGS] = recipSendable;
0 ignored issues
show
The constant PR_RECIPIENT_FLAGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3820
			$delegator[PR_SEARCH_KEY] = $messageProps[PR_RCVD_REPRESENTING_SEARCH_KEY];
0 ignored issues
show
The constant PR_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant PR_RCVD_REPRESENTING_SEARCH_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3821
3822
			// Add organizer to recipients list.
3823
			array_unshift($recipients, $delegator);
3824
		}
3825
	}
3826
3827
	/**
3828
	 * Function will return delegator's store and calendar folder for processing meetings.
3829
	 *
3830
	 * @param string $receivedRepresentingEntryId entryid of the delegator user
3831
	 * @param array  $foldersToOpen               contains list of folder types that should be returned in result
3832
	 *
3833
	 * @return resource[] contains store of the delegator and resource of folders if $foldersToOpen is not empty
3834
	 *
3835
	 * @psalm-return array<resource>
3836
	 */
3837
	public function getDelegatorStore($receivedRepresentingEntryId, $foldersToOpen = []): array {
3838
		$returnData = [];
3839
3840
		$delegatorStore = $this->openCustomUserStore($receivedRepresentingEntryId);
3841
		$returnData['store'] = $delegatorStore;
3842
3843
		if (!empty($foldersToOpen)) {
3844
			for ($index = 0, $len = count($foldersToOpen); $index < $len; ++$index) {
3845
				$folderType = $foldersToOpen[$index];
3846
3847
				// first try with default folders
3848
				$folder = $this->openDefaultFolder($folderType, $delegatorStore);
3849
3850
				// if folder not found then try with base folders
3851
				if ($folder === false) {
3852
					$folder = $this->openBaseFolder($folderType, $delegatorStore);
3853
				}
3854
3855
				if ($folder === false) {
3856
					// we are still not able to get the folder so give up
3857
					continue;
3858
				}
3859
3860
				$returnData[$folderType] = $folder;
3861
			}
3862
		}
3863
3864
		return $returnData;
3865
	}
3866
3867
	/**
3868
	 * Function returns extra info about meeting timing along with message body
3869
	 * which will be included in body while sending meeting request/response.
3870
	 *
3871
	 * @return false|string $meetingTimeInfo info about meeting timing along with message body
3872
	 */
3873
	public function getMeetingTimeInfo() {
3874
		return $this->meetingTimeInfo;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->meetingTimeInfo also could return the type boolean which is incompatible with the documented return type false|string.
Loading history...
3875
	}
3876
3877
	/**
3878
	 * Function sets extra info about meeting timing along with message body
3879
	 * which will be included in body while sending meeting request/response.
3880
	 *
3881
	 * @param string $meetingTimeInfo info about meeting timing along with message body
3882
	 */
3883
	public function setMeetingTimeInfo($meetingTimeInfo): void {
3884
		$this->meetingTimeInfo = $meetingTimeInfo;
3885
	}
3886
3887
	/**
3888
	 * Helper function which is use to get local categories of all occurrence.
3889
	 *
3890
	 * @param mixed $calendarItem meeting request item
3891
	 * @param mixed $store        store containing calendar folder
3892
	 * @param mixed $calFolder    calendar folder
3893
	 *
3894
	 * @return array $localCategories which contain array of basedate along with categories
3895
	 */
3896
	public function getLocalCategories($calendarItem, $store, $calFolder) {
3897
		$calendarItemProps = mapi_getprops($calendarItem);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3897
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($calendarItem);
Loading history...
3898
		$recurrence = new Recurrence($store, $calendarItem);
3899
3900
		// Retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3901
		$items = $recurrence->getItems($calendarItemProps[$this->proptags['clipstart']], $calendarItemProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3902
		$localCategories = [];
3903
3904
		foreach ($items as $item) {
3905
			$recurrenceItems = $recurrence->getCalendarItems($store, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus'], $this->proptags['categories']]);
3906
			foreach ($recurrenceItems as $recurrenceItem) {
3907
				// Check if occurrence is exception then get the local categories of that occurrence.
3908
				if (isset($recurrenceItem[$this->proptags['goid']]) && $recurrenceItem[$this->proptags['goid']] == $calendarItemProps[$this->proptags['goid']]) {
3909
					$exceptionAttach = $recurrence->getExceptionAttachment($recurrenceItem['basedate']);
3910
3911
					if ($exceptionAttach) {
3912
						$exception = mapi_attach_openobj($exceptionAttach, 0);
0 ignored issues
show
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3912
						$exception = /** @scrutinizer ignore-call */ mapi_attach_openobj($exceptionAttach, 0);
Loading history...
3913
						$exceptionProps = mapi_getprops($exception, [$this->proptags['categories']]);
3914
						if (isset($exceptionProps[$this->proptags['categories']])) {
3915
							$localCategories[$recurrenceItem['basedate']] = $exceptionProps[$this->proptags['categories']];
3916
						}
3917
					}
3918
				}
3919
			}
3920
		}
3921
3922
		return $localCategories;
3923
	}
3924
3925
	/**
3926
	 * Helper function which is use to apply local categories on respective occurrences.
3927
	 *
3928
	 * @param mixed $calendarItem    meeting request item
3929
	 * @param mixed $store           store containing calendar folder
3930
	 * @param array $localCategories array contains basedate and array of categories
3931
	 */
3932
	public function applyLocalCategories($calendarItem, $store, $localCategories): void {
3933
		$calendarItemProps = mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
0 ignored issues
show
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3933
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
Loading history...
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3934
		$message = mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
0 ignored issues
show
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3934
		$message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
Loading history...
3935
		$recurrence = new Recurrence($store, $message);
3936
3937
		// Check for all occurrence if it is exception then modify the exception by setting up categories,
3938
		// Otherwise create new exception with categories.
3939
		foreach ($localCategories as $key => $value) {
3940
			if ($recurrence->isException($key)) {
3941
				$recurrence->modifyException([$this->proptags['categories'] => $value], $key);
3942
			}
3943
			else {
3944
				$recurrence->createException([$this->proptags['categories'] => $value], $key, false);
3945
			}
3946
			mapi_savechanges($message);
0 ignored issues
show
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

3946
			/** @scrutinizer ignore-call */ 
3947
   mapi_savechanges($message);
Loading history...
3947
		}
3948
	}
3949
}
3950