Passed
Push — master ( 3c1f8e...9888b0 )
by
unknown
36:51 queued 24:32
created

class.meetingrequest.php (430 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-2024 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
		PR_SMTP_ADDRESS,
0 ignored issues
show
The constant PR_SMTP_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
96
	];
97
98
	/**
99
	 * Indication whether the setting of resources in a Meeting Request is success (false) or if it
100
	 * has failed (integer).
101
	 *
102
	 * @var null|false|int
103
	 *
104
	 * @psalm-var 1|3|4|false|null
105
	 */
106
	public $errorSetResource;
107
108
	public $proptags;
109
	private $store;
110
	public $message;
111
	private $session;
112
113
	/**
114
	 * @var false|string
115
	 */
116
	private $meetingTimeInfo;
117
	private $enableDirectBooking;
118
119
	/**
120
	 * @var null|bool
121
	 */
122
	private $includesResources;
123
	private $nonAcceptingResources;
124
	private $recipientDisplayname;
125
126
	/**
127
	 * Constructor.
128
	 *
129
	 * Takes a store and a message. The message is an appointment item
130
	 * that should be converted into a meeting request or an incoming
131
	 * e-mail message that is a meeting request.
132
	 *
133
	 * The $session variable is optional, but required if the following features
134
	 * are to be used:
135
	 *
136
	 * - Sending meeting requests for meetings that are not in your own store
137
	 * - Sending meeting requests to resources, resource availability checking and resource freebusy updates
138
	 *
139
	 * @param mixed $store
140
	 * @param mixed $message
141
	 * @param mixed $session
142
	 * @param mixed $enableDirectBooking
143
	 */
144
	public function __construct($store, $message, $session = false, $enableDirectBooking = true) {
145
		$this->store = $store;
146
		$this->message = $message;
147
		$this->session = $session;
148
		// This variable string saves time information for the MR.
149
		$this->meetingTimeInfo = false;
150
		$this->enableDirectBooking = $enableDirectBooking;
151
152
		$properties = [];
153
		$properties['goid'] = 'PT_BINARY:PSETID_Meeting:0x3';
154
		$properties['goid2'] = 'PT_BINARY:PSETID_Meeting:0x23';
155
		$properties['type'] = 'PT_STRING8:PSETID_Meeting:0x24';
156
		$properties['meetingrecurring'] = 'PT_BOOLEAN:PSETID_Meeting:0x5';
157
		$properties['unknown2'] = 'PT_BOOLEAN:PSETID_Meeting:0xa';
158
		$properties['attendee_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1';
159
		$properties['owner_critical_change'] = 'PT_SYSTIME:PSETID_Meeting:0x1a';
160
		$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...
161
		$properties['responsestatus'] = 'PT_LONG:PSETID_Appointment:0x8218';
162
		$properties['unknown6'] = 'PT_LONG:PSETID_Meeting:0x4';
163
		$properties['replytime'] = 'PT_SYSTIME:PSETID_Appointment:0x8220';
164
		$properties['usetnef'] = 'PT_BOOLEAN:PSETID_Common:0x8582';
165
		$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...
166
		$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...
167
		$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...
168
		$properties['sendasical'] = 'PT_BOOLEAN:PSETID_Appointment:0x8200';
169
		$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...
170
		$properties['unknown7'] = 'PT_LONG:PSETID_Appointment:0x8202';
171
		$properties['last_updatecounter'] = 'PT_LONG:PSETID_Appointment:0x8203';			// AppointmentLastSequence
172
		$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...
173
		$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...
174
		$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...
175
		$properties['responselocation'] = 'PT_STRING8:PSETID_Meeting:0x2';
176
		$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...
177
		$properties['requestsent'] = 'PT_BOOLEAN:PSETID_Appointment:0x8229';		// PidLidFInvited, MeetingRequestWasSent
178
		$properties['startdate'] = 'PT_SYSTIME:PSETID_Appointment:' . PidLidAppointmentStartWhole;
179
		$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...
180
		$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...
181
		$properties['commonstart'] = 'PT_SYSTIME:PSETID_Common:0x8516';
182
		$properties['commonend'] = 'PT_SYSTIME:PSETID_Common:0x8517';
183
		$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...
184
		$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...
185
		$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...
186
		$properties['start_recur_date'] = 'PT_LONG:PSETID_Meeting:0xD';				// StartRecurTime
187
		$properties['start_recur_time'] = 'PT_LONG:PSETID_Meeting:0xE';				// StartRecurTime
188
		$properties['end_recur_date'] = 'PT_LONG:PSETID_Meeting:0xF';				// EndRecurDate
189
		$properties['end_recur_time'] = 'PT_LONG:PSETID_Meeting:0x10';				// EndRecurTime
190
		$properties['is_exception'] = 'PT_BOOLEAN:PSETID_Meeting:0xA';				// LID_IS_EXCEPTION
191
		$properties['apptreplyname'] = 'PT_STRING8:PSETID_Appointment:0x8230';
192
		// Propose new time properties
193
		$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...
194
		$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...
195
		$properties['proposed_duration'] = 'PT_LONG:PSETID_Appointment:0x8256';
196
		$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...
197
		$properties['recurring_pattern'] = 'PT_STRING8:PSETID_Appointment:0x8232';
198
		$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...
199
		$properties['meetingtype'] = 'PT_LONG:PSETID_Meeting:0x26';
200
		$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...
201
		$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...
202
		$properties['categories'] = 'PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords';
203
		$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...
204
		$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...
205
		$properties['toattendeesstring'] = 'PT_STRING8:PSETID_Appointment:0x823B';
206
		$properties['ccattendeesstring'] = 'PT_STRING8:PSETID_Appointment:0x823C';
207
208
		$this->proptags = getPropIdsFromStrings($store, $properties);
209
	}
210
211
	/**
212
	 * Sets the direct booking property. This is an alternative to the setting of the direct booking
213
	 * property through the constructor. However, setting it in the constructor is preferred.
214
	 *
215
	 * @param bool $directBookingSetting
216
	 */
217
	public function setDirectBooking($directBookingSetting): void {
218
		$this->enableDirectBooking = $directBookingSetting;
219
	}
220
221
	/**
222
	 * Returns TRUE if the message pointed to is an incoming meeting request and should
223
	 * therefore be replied to with doAccept or doDecline().
224
	 *
225
	 * @param string $messageClass message class to use for checking
226
	 *
227
	 * @return bool returns true if this is a meeting request else false
228
	 */
229
	public function isMeetingRequest($messageClass = false) {
230
		if ($messageClass === false) {
231
			$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

231
			$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...
232
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
233
		}
234
235
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.request') === 0) {
236
			return true;
237
		}
238
239
		return false;
240
	}
241
242
	/**
243
	 * Returns TRUE if the message pointed to is a returning meeting request response.
244
	 *
245
	 * @param string $messageClass message class to use for checking
246
	 *
247
	 * @return bool returns true if this is a meeting request else false
248
	 */
249
	public function isMeetingRequestResponse($messageClass = false) {
250
		if ($messageClass === false) {
251
			$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

251
			$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...
252
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
253
		}
254
255
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.resp') === 0) {
256
			return true;
257
		}
258
259
		return false;
260
	}
261
262
	/**
263
	 * Returns TRUE if the message pointed to is a cancellation request.
264
	 *
265
	 * @param string $messageClass message class to use for checking
266
	 *
267
	 * @return bool returns true if this is a meeting request else false
268
	 */
269
	public function isMeetingCancellation($messageClass = false) {
270
		if ($messageClass === false) {
271
			$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

271
			$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...
272
			$messageClass = isset($props[PR_MESSAGE_CLASS]) ? $props[PR_MESSAGE_CLASS] : false;
273
		}
274
275
		if ($messageClass !== false && stripos($messageClass, 'ipm.schedule.meeting.canceled') === 0) {
276
			return true;
277
		}
278
279
		return false;
280
	}
281
282
	/**
283
	 * Function is used to get the last update counter of meeting request.
284
	 *
285
	 * @return bool|int false when last_updatecounter not found else return last_updatecounter
286
	 */
287
	public function getLastUpdateCounter() {
288
		$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

288
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['last_updatecounter']]);
Loading history...
289
		if (isset($calendarItemProps) && !empty($calendarItemProps)) {
290
			return $calendarItemProps[$this->proptags['last_updatecounter']];
291
		}
292
293
		return false;
294
	}
295
296
	/**
297
	 * Process an incoming meeting request response. This updates the appointment
298
	 * in your calendar to show whether the user has accepted or declined.
299
	 */
300
	public function processMeetingRequestResponse() {
301
		if (!$this->isMeetingRequestResponse()) {
302
			return;
303
		}
304
305
		if (!$this->isLocalOrganiser()) {
306
			return;
307
		}
308
309
		// Get information we need from the response message
310
		$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

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

382
		$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_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
383
384
		// check if meeting response is already processed
385
		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...
386
			// meeting is already processed
387
			return;
388
		}
389
		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

389
		/** @scrutinizer ignore-call */ 
390
  mapi_setprops($this->message, [PR_PROCESSED => true]);
Loading history...
390
		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

390
		/** @scrutinizer ignore-call */ 
391
  mapi_savechanges($this->message);
Loading history...
391
392
		// if meeting is updated in organizer's calendar then we don't need to process
393
		// old response
394
		if ($this->isMeetingUpdated($basedate)) {
395
			return;
396
		}
397
398
		// If basedate is found, then create/modify exception msg and do processing
399
		if ($basedate && isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] === true) {
400
			$recurr = new Recurrence($store, $calendarItem);
401
402
			// Copy properties from meeting request
403
			$exception_props = mapi_getprops($this->message, [
404
				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...
405
				$this->proptags['proposed_start_whole'],
406
				$this->proptags['proposed_end_whole'],
407
				$this->proptags['proposed_duration'],
408
				$this->proptags['counter_proposal'],
409
			]);
410
411
			// Create/modify exception
412
			if ($recurr->isException($basedate)) {
413
				$recurr->modifyException($exception_props, $basedate);
414
			}
415
			else {
416
				// When we are creating an exception we need copy recipients from main recurring item
417
				$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

417
				$recipTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($calendarItem);
Loading history...
418
				$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

418
				$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipTable, $this->recipprops);
Loading history...
419
420
				// Retrieve actual start/due dates from calendar item.
421
				$exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
422
				$exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
423
424
				$recurr->createException($exception_props, $basedate, false, $recips);
425
			}
426
427
			mapi_savechanges($calendarItem);
428
429
			$attach = $recurr->getExceptionAttachment($basedate);
430
			if ($attach) {
431
				$recurringItem = $calendarItem;
432
				$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

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

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

565
		$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, $listProperties);
Loading history...
566
567
		$goid = $messageProps[$this->proptags['goid']];	// GlobalID (0x3)
568
		if (!isset($goid)) {
569
			return;
570
		}
571
572
		// get delegator store, if delegate is processing this cancellation
573
		if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
574
			$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...
575
576
			$store = $delegatorStore['store'];
577
		}
578
		else {
579
			$store = $this->store;
580
		}
581
582
		// check for calendar access
583
		if ($this->checkCalendarWriteAccess($store) !== true) {
584
			// Throw an exception that we don't have write permissions on calendar folder,
585
			// allow caller to fill the error message
586
			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...
587
		}
588
589
		$calendarItem = $this->getCorrespondentCalendarItem(true);
590
		$basedate = $this->getBasedateFromGlobalID($goid);
591
592
		if ($calendarItem !== false) {
593
			// if basedate is provided and we could not find the item then it could be that we are processing
594
			// an exception so get the exception and process it
595
			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...
596
				$calendarItemProps = mapi_getprops($calendarItem, [$this->proptags['recurring']]);
597
				if ($calendarItemProps[$this->proptags['recurring']] === true) {
598
					$recurr = new Recurrence($store, $calendarItem);
599
600
					// Set message class
601
					$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...
602
603
					if ($recurr->isException($basedate)) {
604
						$recurr->modifyException($messageProps, $basedate);
605
					}
606
					else {
607
						$recurr->createException($messageProps, $basedate);
608
					}
609
				}
610
			}
611
			else {
612
				// set the properties of the cancellation object
613
				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

613
				/** @scrutinizer ignore-call */ 
614
    mapi_setprops($calendarItem, $messageProps);
Loading history...
614
			}
615
616
			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

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

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

703
					/** @scrutinizer ignore-call */ 
704
     mapi_setprops($this->message, [PR_PROCESSED => true]);
Loading history...
704
					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

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

757
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message);
Loading history...
758
		$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...
759
760
		if ($sendresponse) {
761
			$this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
762
		}
763
764
		/*
765
		 * Further processing depends on what user is receiving. User can receive recurring item, a single occurrence or a normal meeting.
766
		 * 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.
767
		 * 2) If single occurrence then find occurrence itself using globalID and if item is not found then use cleanGlobalID to find main recurring item
768
		 * 3) Normal meeting req are handled normally as they were handled previously.
769
		 *
770
		 * 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
771
		 * 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.
772
		 * If user is responding from calendar then item is opened and properties are set such as meetingstatus, responsestatus, busystatus etc.
773
		 */
774
		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...
775
			// This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
776
			if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] == true && $basedate == false) {
777
				$calendarItem = false;
778
779
				// Find main recurring item based on GlobalID (0x3)
780
				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
781
				if (is_array($items)) {
782
					foreach ($items as $entryid) {
783
						$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

783
						$calendarItem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
784
					}
785
				}
786
787
				$processed = false;
788
				if (!$calendarItem) {
789
					// Recurring item not found, so create new meeting in Calendar
790
					$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

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

856
				/** @scrutinizer ignore-call */ 
857
    mapi_setprops($calendarItem, $props);
Loading history...
857
858
				// we have already processed attachments and recipients, so no need to do it again
859
				if (!$processed) {
860
					// Copy attachments too
861
					$this->replaceAttachments($this->message, $calendarItem);
862
					// Copy recipients too
863
					$this->replaceRecipients($this->message, $calendarItem, $isDelegate);
864
				}
865
866
				// Find all occurrences based on CleanGlobalID (0x23)
867
				// there will be no exceptions left if $processed is true, but even if it doesn't hurt to recheck
868
				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
869
				if (is_array($items)) {
870
					// Save all existing occurrence as exceptions
871
					foreach ($items as $entryid) {
872
						// Open occurrence
873
						$occurrenceItem = mapi_msgstore_openentry($store, $entryid);
874
875
						// Save occurrence into main recurring item as exception
876
						if ($occurrenceItem) {
877
							$occurrenceItemProps = mapi_getprops($occurrenceItem, [$this->proptags['goid'], $this->proptags['recurring']]);
878
879
							// Find basedate of occurrence item
880
							$basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]);
881
							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...
882
								$this->mergeException($calendarItem, $occurrenceItem, $basedate, $store);
883
							}
884
						}
885
					}
886
				}
887
888
				if(!isset($props[$this->proptags["recurring_pattern"]])) {
889
					$recurr = new Recurrence($store, $calendarItem);
890
					$recurr->saveRecurrencePattern();
891
				}
892
893
				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

893
				/** @scrutinizer ignore-call */ 
894
    mapi_savechanges($calendarItem);
Loading history...
894
895
				// After applying update of organiser all local categories of occurrence was removed,
896
				// So if local categories exist then apply it on respective occurrence.
897
				if (!empty($localCategories)) {
898
					$this->applyLocalCategories($calendarItem, $store, $localCategories);
899
				}
900
901
				if ($move) {
902
					// open wastebasket of currently logged in user and move the meeting request to it
903
					// for delegates this will be delegate's wastebasket folder
904
					$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
905
					$sourcefolder = $this->openParentFolder();
906
					mapi_folder_copymessages($sourcefolder, [$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

906
					/** @scrutinizer ignore-call */ 
907
     mapi_folder_copymessages($sourcefolder, [$props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
907
				}
908
909
				$entryid = $props[PR_ENTRYID];
910
			}
911
			else {
912
				/**
913
				 * This meeting request is not recurring, so can be an exception or normal meeting.
914
				 * If exception then find main recurring item and update exception
915
				 * If main recurring item is not found then put exception into Calendar as normal meeting.
916
				 */
917
				$calendarItem = false;
918
919
				// We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence.
920
				if ($basedate) {
921
					// Find main recurring item from CleanGlobalID of this meeting request
922
					$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
923
					if (is_array($items)) {
924
						foreach ($items as $entryid) {
925
							$calendarItem = mapi_msgstore_openentry($store, $entryid);
926
						}
927
					}
928
929
					// Main recurring item is found, so now update exception
930
					if ($calendarItem) {
931
						$this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate);
932
						$calendarItemProps = mapi_getprops($calendarItem, [PR_ENTRYID]);
933
						$entryid = $calendarItemProps[PR_ENTRYID];
934
					}
935
				}
936
937
				if (!$calendarItem) {
938
					$items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
939
					if (is_array($items)) {
940
						// Get local categories before deleting MR.
941
						$message = mapi_msgstore_openentry($store, $items[0]);
942
						$localCategories = mapi_getprops($message, [$this->proptags['categories']]);
943
						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

943
						/** @scrutinizer ignore-call */ 
944
      mapi_folder_deletemessages($calFolder, $items);
Loading history...
944
					}
945
946
					if ($move) {
947
						// All we have to do is open the default calendar,
948
						// set the message class correctly to be an appointment item
949
						// and move it to the calendar folder
950
						$sourcefolder = $this->openParentFolder();
951
952
						// create a new calendar message, and copy the message to there,
953
						// since we want to delete (move to wastebasket) the original message
954
						$old_entryid = mapi_getprops($this->message, [PR_ENTRYID]);
955
						$calmsg = mapi_folder_createmessage($calFolder);
956
						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

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

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

1110
						$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($this->message);
Loading history...
1111
1112
						$recips = [];
1113
						// If delegate, then do not add the delegate in recipients
1114
						if ($isDelegate) {
1115
							$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...
1116
							$res = [
1117
								RES_PROPERTY,
1118
								[
1119
									RELOP => RELOP_NE,
1120
									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...
1121
									VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
1122
								],
1123
							];
1124
							$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

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

1224
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
1225
1226
		// If this meeting request is received by a delegate then open delegator's store.
1227
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1228
			$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...
1229
1230
			$store = $delegatorStore['store'];
1231
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1232
		}
1233
		else {
1234
			$calFolder = $this->openDefaultCalendar();
1235
			$store = $this->store;
1236
		}
1237
1238
		// check for calendar access before deleting the calendar item
1239
		if ($this->checkCalendarWriteAccess($store) !== true) {
1240
			// Throw an exception that we don't have write permissions on calendar folder,
1241
			// allow caller to fill the error message
1242
			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...
1243
		}
1244
1245
		$goid = $messageprops[$this->proptags['goid']];
1246
1247
		// First, find the items in the calendar by GlobalObjid (0x3)
1248
		$entryids = $this->findCalendarItems($goid, $calFolder);
1249
1250
		if (!$basedate) {
1251
			$basedate = $this->getBasedateFromGlobalID($goid);
1252
		}
1253
1254
		if ($sendresponse) {
1255
			$this->createResponse(olResponseDeclined, [], $body, $store, $basedate, $calFolder);
1256
		}
1257
1258
		if ($basedate) {
1259
			// use CleanGlobalObjid (0x23)
1260
			$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
1261
1262
			if (is_array($calendaritems)) {
1263
				foreach ($calendaritems as $entryid) {
1264
					// Open each calendar item and set the properties of the cancellation object
1265
					$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

1265
					$calendaritem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
1266
1267
					// Recurring item is found, now delete exception
1268
					if ($calendaritem) {
1269
						$this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store);
1270
						$result = true;
1271
					}
1272
				}
1273
			}
1274
1275
			if ($this->isMeetingRequest()) {
1276
				$calendaritem = false;
1277
			}
1278
		}
1279
1280
		if (!$calendaritem) {
1281
			$calendar = $this->openDefaultCalendar($store);
1282
1283
			if (!empty($entryids)) {
1284
				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

1284
				/** @scrutinizer ignore-call */ 
1285
    mapi_folder_deletemessages($calendar, $entryids);
Loading history...
1285
			}
1286
1287
			// All we have to do to decline, is to move the item to the waste basket
1288
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1289
			$sourcefolder = $this->openParentFolder();
1290
1291
			$messageprops = mapi_getprops($this->message, [PR_ENTRYID]);
1292
1293
			// Release the message
1294
			$this->message = null;
1295
1296
			// Move the message to the waste basket
1297
			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

1297
			/** @scrutinizer ignore-call */ 
1298
   mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
1298
1299
			$result = true;
1300
		}
1301
1302
		return $result;
1303
	}
1304
1305
	/**
1306
	 * Removes a meeting request from the calendar when the user presses the
1307
	 * 'remove from calendar' button in response to a meeting cancellation.
1308
	 *
1309
	 * @param mixed $basedate if specified contains starttime of day of an occurrence
1310
	 *
1311
	 * @return null|false
1312
	 */
1313
	public function doRemoveFromCalendar($basedate) {
1314
		if ($this->isLocalOrganiser()) {
1315
			return false;
1316
		}
1317
1318
		$messageprops = mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_ENTRYID, 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

1318
		$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...
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1319
1320
		$goid = $messageprops[$this->proptags['goid']];
1321
1322
		if (isset($messageprops[PR_RCVD_REPRESENTING_ENTRYID])) {
1323
			$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...
1324
1325
			$store = $delegatorStore['store'];
1326
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
1327
		}
1328
		else {
1329
			$store = $this->store;
1330
			$calFolder = $this->openDefaultCalendar();
1331
		}
1332
1333
		// check for calendar access before deleting the calendar item
1334
		if ($this->checkCalendarWriteAccess($store) !== true) {
1335
			// Throw an exception that we don't have write permissions on calendar folder,
1336
			// allow caller to fill the error message
1337
			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...
1338
		}
1339
1340
		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1341
		// get the source folder of the meeting message
1342
		$sourcefolder = $this->openParentFolder();
1343
1344
		// Check if the message is a meeting request in the inbox or a calendaritem by checking the message class
1345
		if ($this->isMeetingCancellation($messageprops[PR_MESSAGE_CLASS])) {
1346
			// get the basedate to check for exception
1347
			$basedate = $this->getBasedateFromGlobalID($goid);
1348
1349
			$calendarItem = $this->getCorrespondentCalendarItem(true);
1350
1351
			if ($calendarItem !== false) {
1352
				// basedate is provided so open exception
1353
				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...
1354
					$exception = $this->getExceptionItem($calendarItem, $basedate);
1355
1356
					if ($exception !== false) {
0 ignored issues
show
The condition $exception !== false is always true.
Loading history...
1357
						// exception found, remove it from calendar
1358
						$this->doRemoveExceptionFromCalendar($basedate, $calendarItem, $store);
1359
					}
1360
				}
1361
				else {
1362
					// remove normal / recurring series from calendar
1363
					$entryids = mapi_getprops($calendarItem, [PR_ENTRYID]);
1364
1365
					$entryids = [$entryids[PR_ENTRYID]];
1366
1367
					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

1367
					/** @scrutinizer ignore-call */ 
1368
     mapi_folder_copymessages($calFolder, $entryids, $wastebasket, MESSAGE_MOVE);
Loading history...
1368
				}
1369
			}
1370
1371
			// Release the message, because we are going to move it to wastebasket
1372
			$this->message = null;
1373
1374
			// Move the cancellation mail to wastebasket
1375
			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1376
		}
1377
		else {
1378
			// Here only properties are set on calendaritem, because user is responding from calendar.
1379
			if ($basedate) {
1380
				// remove the occurrence
1381
				$this->doRemoveExceptionFromCalendar($basedate, $this->message, $store);
1382
			}
1383
			else {
1384
				// remove normal/recurring meeting item.
1385
				// Move the message to the waste basket
1386
				mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
1387
			}
1388
		}
1389
	}
1390
1391
	/**
1392
	 * Function can be used to cancel any existing meeting and send cancellation mails to attendees.
1393
	 * Should only be called from meeting object from calendar.
1394
	 *
1395
	 * @param mixed $basedate (optional) basedate of occurrence which should be cancelled
1396
	 *
1397
	 * @FIXME cancellation mail is also sent to attendee which has declined the meeting
1398
	 * @FIXME don't send canellation mail when cancelling meeting from past
1399
	 */
1400
	public function doCancelInvitation($basedate = false) {
1401
		if (!$this->isLocalOrganiser()) {
1402
			return;
1403
		}
1404
1405
		// check write access for delegate
1406
		if ($this->checkCalendarWriteAccess($this->store) !== true) {
1407
			// Throw an exception that we don't have write permissions on calendar folder,
1408
			// error message will be filled by module
1409
			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...
1410
		}
1411
1412
		$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

1412
		$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_ENTRYID, $this->proptags['recurring']]);
Loading history...
1413
1414
		if (isset($messageProps[$this->proptags['recurring']]) && $messageProps[$this->proptags['recurring']] === true) {
1415
			// cancellation of recurring series or one occurrence
1416
			$recurrence = new Recurrence($this->store, $this->message);
1417
1418
			// if basedate is specified then we are cancelling only one occurrence, so create exception for that occurrence
1419
			if ($basedate) {
1420
				$recurrence->createException([], $basedate, true);
1421
			}
1422
1423
			// update the meeting request
1424
			$this->updateMeetingRequest();
1425
1426
			// send cancellation mails
1427
			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ', $basedate);
1428
1429
			// save changes in the message
1430
			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

1430
			/** @scrutinizer ignore-call */ 
1431
   mapi_savechanges($this->message);
Loading history...
1431
		}
1432
		else {
1433
			// cancellation of normal meeting request
1434
			// Send the cancellation
1435
			$this->updateMeetingRequest();
1436
			$this->sendMeetingRequest(true, dgettext('zarafa', 'Canceled') . ': ');
1437
1438
			// save changes in the message
1439
			mapi_savechanges($this->message);
1440
		}
1441
1442
		// if basedate is specified then we have already created exception of it so nothing should be done now
1443
		// but when cancelling normal / recurring meeting request we need to remove meeting from calendar
1444
		if ($basedate === false) {
1445
			// get the wastebasket folder, for delegate this will give wastebasket of delegate
1446
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1447
1448
			// get the source folder of the meeting message
1449
			$sourcefolder = $this->openParentFolder();
1450
1451
			// Move the message to the deleted items
1452
			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

1452
			/** @scrutinizer ignore-call */ 
1453
   mapi_folder_copymessages($sourcefolder, [$messageProps[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
1453
		}
1454
	}
1455
1456
	/**
1457
	 * Convert epoch to MAPI FileTime, number of 100-nanosecond units since
1458
	 * the start of January 1, 1601.
1459
	 * https://msdn.microsoft.com/en-us/library/office/cc765906.aspx.
1460
	 *
1461
	 * @param int $epoch the current epoch
1462
	 *
1463
	 * @return int the MAPI FileTime equalevent to the given epoch time
1464
	 */
1465
	public function epochToMapiFileTime($epoch) {
1466
		$nanoseconds_between_epoch = 116444736000000000;
1467
1468
		return ($epoch * 10000000) + $nanoseconds_between_epoch;
1469
	}
1470
1471
	/**
1472
	 * Sets the properties in the message so that is can be sent
1473
	 * as a meeting request. The caller has to submit the message. This
1474
	 * is only used for new MeetingRequests. Pass the appointment item as $message
1475
	 * in the constructor to do this.
1476
	 *
1477
	 * @param mixed $basedate
1478
	 */
1479
	public function setMeetingRequest($basedate = false): void {
1480
		$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

1480
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['updatecounter']]);
Loading history...
1481
1482
		// Create a new global id for this item
1483
		// https://msdn.microsoft.com/en-us/library/ee160198(v=exchg.80).aspx
1484
		$goid = pack('H*', '040000008200E00074C5B7101A82E00800000000');
1485
		/*
1486
		$year = gmdate('Y');
1487
		$month = gmdate('n');
1488
		$day = gmdate('j');
1489
		$goid .= pack('n', $year);
1490
		$goid .= pack('C', $month);
1491
		$goid .= pack('C', $day);
1492
		*/
1493
		// Creation Time
1494
		$time = $this->epochToMapiFileTime(time());
1495
		$goid .= pack('V', $time & 0xFFFFFFFF);
1496
		$goid .= pack('V', $time >> 32);
1497
		// 8 Zeros
1498
		$goid .= pack('H*', '0000000000000000');
1499
		// Length of the random data
1500
		$goid .= pack('V', 16);
1501
		// Random data.
1502
		for ($i = 0; $i < 16; ++$i) {
1503
			$goid .= chr(rand(0, 255));
1504
		}
1505
1506
		// Create a new appointment id for this item
1507
		$apptid = rand();
1508
1509
		$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...
1510
		$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...
1511
		$props[$this->proptags['goid']] = $goid;
1512
		$props[$this->proptags['goid2']] = $goid;
1513
1514
		if (!isset($props[$this->proptags['updatecounter']])) {
1515
			$props[$this->proptags['updatecounter']] = 0;			// OL also starts sequence no with zero.
1516
			$props[$this->proptags['last_updatecounter']] = 0;
1517
		}
1518
1519
		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

1519
		/** @scrutinizer ignore-call */ 
1520
  mapi_setprops($this->message, $props);
Loading history...
1520
	}
1521
1522
	/**
1523
	 * Sends a meeting request by copying it to the outbox, converting
1524
	 * the message class, adding some properties that are required only
1525
	 * for sending the message and submitting the message. Set cancel to
1526
	 * true if you wish to completely cancel the meeting request. You can
1527
	 * specify an optional 'prefix' to prefix the sent message, which is normally
1528
	 * 'Canceled: '.
1529
	 *
1530
	 * @param mixed $cancel
1531
	 * @param mixed $prefix
1532
	 * @param mixed $basedate
1533
	 * @param mixed $modifiedRecips
1534
	 * @param mixed $deletedRecips
1535
	 *
1536
	 * @return (int|mixed)[]|true
1537
	 *
1538
	 * @psalm-return array{error: 1|3|4, displayname: mixed}|true
1539
	 */
1540
	public function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $modifiedRecips = false, $deletedRecips = false) {
1541
		$this->includesResources = false;
1542
		$this->nonAcceptingResources = [];
1543
1544
		// Get the properties of the message
1545
		$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

1545
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['recurring']]);
Loading history...
1546
1547
		/*
1548
		 * Submit message to non-resource recipients
1549
		 */
1550
		// Set BusyStatus to olTentative (1)
1551
		// Set MeetingStatus to olMeetingReceived
1552
		// Set ResponseStatus to olResponseNotResponded
1553
1554
		/*
1555
		 * While sending recurrence meeting exceptions are not sent as attachments
1556
		 * because first all exceptions are sent and then recurrence meeting is sent.
1557
		 */
1558
		if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) {
1559
			// Book resource
1560
			$this->bookResources($this->message, $cancel, $prefix);
1561
1562
			if (!$this->errorSetResource) {
1563
				$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

1563
				$recurr = new Recurrence(/** @scrutinizer ignore-type */ $this->openDefaultStore(), $this->message);
Loading history...
1564
1565
				// First send meetingrequest for recurring item
1566
				$this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $modifiedRecips, $deletedRecips);
1567
1568
				// Then send all meeting request for all exceptions
1569
				$exceptions = $recurr->getAllExceptions();
1570
				if ($exceptions) {
1571
					foreach ($exceptions as $exceptionBasedate) {
1572
						$attach = $recurr->getExceptionAttachment($exceptionBasedate);
1573
1574
						if ($attach) {
1575
							$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

1575
							$occurrenceItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
1576
							$this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $modifiedRecips, $deletedRecips);
1577
							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

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

1642
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [$this->proptags['last_updatecounter'], $this->proptags['goid']]);
Loading history...
1643
1644
		if (!isset($messageprops[$this->proptags['goid']])) {
1645
			$this->setMeetingRequest($basedate);
1646
		}
1647
		else {
1648
			$counter = (isset($messageprops[$this->proptags['last_updatecounter']]) ?? 0) + 1;
1649
1650
			// increment value of last_updatecounter, last_updatecounter will be common for recurring series
1651
			// so even if you sending an exception only you need to update the last_updatecounter in the recurring series message
1652
			// this way we can make sure that every time we will be using a uniwue number for every operation
1653
			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

1653
			/** @scrutinizer ignore-call */ 
1654
   mapi_setprops($this->message, [$this->proptags['last_updatecounter'] => $counter]);
Loading history...
1654
		}
1655
	}
1656
1657
	/**
1658
	 * Returns TRUE if we are the organiser of the meeting. Can be used with any type of meeting object.
1659
	 */
1660
	public function isLocalOrganiser(): bool {
1661
		$props = mapi_getprops($this->message, [$this->proptags['goid'], 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

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

1722
		$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...
1723
1724
		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

1724
		return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
Loading history...
1725
	}
1726
1727
	/**
1728
	 * Function will return resource of the default calendar folder of store.
1729
	 *
1730
	 * @param mixed $store {optional} user store whose default calendar should be opened
1731
	 *
1732
	 * @return resource default calendar folder of store
1733
	 */
1734
	public function openDefaultCalendar($store = false) {
1735
		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...
1736
	}
1737
1738
	/**
1739
	 * Function will return resource of the default outbox folder of store.
1740
	 *
1741
	 * @param mixed $store {optional} user store whose default outbox should be opened
1742
	 *
1743
	 * @return resource default outbox folder of store
1744
	 */
1745
	public function openDefaultOutbox($store = false) {
1746
		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...
1747
	}
1748
1749
	/**
1750
	 * Function will return resource of the default wastebasket folder of store.
1751
	 *
1752
	 * @param mixed $store {optional} user store whose default wastebasket should be opened
1753
	 *
1754
	 * @return resource default wastebasket folder of store
1755
	 */
1756
	public function openDefaultWastebasket($store = false) {
1757
		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...
1758
	}
1759
1760
	/**
1761
	 * Function will return resource of the default calendar folder of store.
1762
	 *
1763
	 * @param mixed $store {optional} user store whose default calendar should be opened
1764
	 *
1765
	 * @return bool|string default calendar folder of store
1766
	 */
1767
	public function getDefaultWastebasketEntryID($store = false) {
1768
		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...
1769
	}
1770
1771
	/**
1772
	 * Function will return resource of the default sent mail folder of store.
1773
	 *
1774
	 * @param mixed $store {optional} user store whose default sent mail should be opened
1775
	 *
1776
	 * @return bool|string default sent mail folder of store
1777
	 */
1778
	public function getDefaultSentmailEntryID($store = false) {
1779
		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...
1780
	}
1781
1782
	/**
1783
	 * Function will return entryid of any default folder of store. This method is useful when you want
1784
	 * to get entryid of folder which is stored as properties of inbox folder
1785
	 * (PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID).
1786
	 *
1787
	 * @param int   $prop  proptag of the folder for which we want to get entryid
1788
	 * @param mixed $store {optional} user store from which we need to get entryid of default folder
1789
	 *
1790
	 * @return bool|string entryid of folder pointed by $prop
1791
	 */
1792
	public function getDefaultFolderEntryID($prop, $store = false) {
1793
		try {
1794
			$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

1794
			$inbox = /** @scrutinizer ignore-call */ mapi_msgstore_getreceivefolder($store ? $store : $this->store);
Loading history...
1795
			$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

1795
			$inboxprops = /** @scrutinizer ignore-call */ mapi_getprops($inbox, [$prop]);
Loading history...
1796
			if (isset($inboxprops[$prop])) {
1797
				return $inboxprops[$prop];
1798
			}
1799
		}
1800
		catch (MAPIException $e) {
1801
			// public store doesn't support this method
1802
			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...
1803
				// don't propagate this error to parent handlers, if store doesn't support it
1804
				$e->setHandled();
1805
			}
1806
		}
1807
1808
		return false;
1809
	}
1810
1811
	/**
1812
	 * Function will return resource of any default folder of store.
1813
	 *
1814
	 * @param int   $prop  proptag of the folder that we want to open
1815
	 * @param mixed $store {optional} user store from which we need to open default folder
1816
	 *
1817
	 * @return resource default folder of store
1818
	 */
1819
	public function openDefaultFolder($prop, $store = false) {
1820
		$folder = false;
1821
		$entryid = $this->getDefaultFolderEntryID($prop, $store);
1822
1823
		if ($entryid !== false) {
1824
			$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

1824
			$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
Loading history...
1825
		}
1826
1827
		return $folder;
1828
	}
1829
1830
	/**
1831
	 * Function will return entryid of default folder from store. This method is useful when you want
1832
	 * to get entryid of folder which is stored as store properties
1833
	 * (PR_IPM_FAVORITES_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID).
1834
	 *
1835
	 * @param int   $prop  proptag of the folder whose entryid we want to get
1836
	 * @param mixed $store {optional} user store from which we need to get entryid of default folder
1837
	 *
1838
	 * @return bool|string entryid of default folder from store
1839
	 */
1840
	public function getBaseEntryID($prop, $store = false) {
1841
		$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

1841
		$storeprops = /** @scrutinizer ignore-call */ mapi_getprops($store ? $store : $this->store, [$prop]);
Loading history...
1842
		if (!isset($storeprops[$prop])) {
1843
			return false;
1844
		}
1845
1846
		return $storeprops[$prop];
1847
	}
1848
1849
	/**
1850
	 * Function will return resource of any default folder of store.
1851
	 *
1852
	 * @param int   $prop  proptag of the folder that we want to open
1853
	 * @param mixed $store {optional} user store from which we need to open default folder
1854
	 *
1855
	 * @return resource default folder of store
1856
	 */
1857
	public function openBaseFolder($prop, $store = false) {
1858
		$folder = false;
1859
		$entryid = $this->getBaseEntryID($prop, $store);
1860
1861
		if ($entryid !== false) {
1862
			$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

1862
			$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store ? $store : $this->store, $entryid);
Loading history...
1863
		}
1864
1865
		return $folder;
1866
	}
1867
1868
	/**
1869
	 * Function checks whether user has access over the specified folder or not.
1870
	 *
1871
	 * @param string $entryid entryid The entryid of the folder to check
1872
	 * @param mixed  $store   (optional) store from which folder should be opened
1873
	 *
1874
	 * @return bool true if user has an access over the folder, false if not
1875
	 */
1876
	public function checkFolderWriteAccess($entryid, $store = false) {
1877
		$accessToFolder = false;
1878
1879
		if (!empty($entryid)) {
1880
			if ($store === false) {
1881
				$store = $this->store;
1882
			}
1883
1884
			try {
1885
				$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

1885
				$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
1886
				$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

1886
				$folderProps = /** @scrutinizer ignore-call */ mapi_getprops($folder, [PR_ACCESS]);
Loading history...
1887
				if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1888
					$accessToFolder = true;
1889
				}
1890
			}
1891
			catch (MAPIException $e) {
1892
				// we don't have rights to open folder, so return false
1893
				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...
1894
					return $accessToFolder;
1895
				}
1896
1897
				// rethrow other errors
1898
				throw $e;
1899
			}
1900
		}
1901
1902
		return $accessToFolder;
1903
	}
1904
1905
	/**
1906
	 * Function checks whether user has access over the specified folder or not.
1907
	 *
1908
	 * @param mixed $store
1909
	 *
1910
	 * @return bool true if user has an access over the folder, false if not
1911
	 */
1912
	public function checkCalendarWriteAccess($store = false) {
1913
		if ($store === false) {
1914
			// If this meeting request is received by a delegate then open delegator's store.
1915
			$messageProps = mapi_getprops($this->message, [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

1915
			$messageProps = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1916
			if (isset($messageProps[PR_RCVD_REPRESENTING_ENTRYID])) {
1917
				$delegatorStore = $this->getDelegatorStore($messageProps[PR_RCVD_REPRESENTING_ENTRYID]);
1918
1919
				$store = $delegatorStore['store'];
1920
			}
1921
			else {
1922
				$store = $this->store;
1923
			}
1924
		}
1925
1926
		// If the store is a public folder, the calendar folder is the PARENT_ENTRYID of the calendar item
1927
		$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...
1928
		if (isset($provider[PR_MDB_PROVIDER]) && $provider[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1929
			$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...
1930
			$entryid = $entryid[PR_PARENT_ENTRYID];
1931
		}
1932
		else {
1933
			$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...
1934
			if ($entryid === false) {
1935
				$entryid = $this->getBaseEntryID(PR_IPM_APPOINTMENT_ENTRYID, $store);
1936
			}
1937
1938
			if ($entryid === false) {
1939
				return false;
1940
			}
1941
		}
1942
1943
		return $this->checkFolderWriteAccess($entryid, $store);
1944
	}
1945
1946
	/**
1947
	 * Function will resolve the user and open its store.
1948
	 *
1949
	 * @param string $ownerentryid the entryid of the user
1950
	 *
1951
	 * @return resource store of the user
1952
	 */
1953
	public function openCustomUserStore($ownerentryid) {
1954
		$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

1954
		$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
1955
1956
		try {
1957
			$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

1957
			$mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $ownerentryid);
Loading history...
1958
		}
1959
		catch (MAPIException $e) {
1960
			return;
1961
		}
1962
1963
		$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

1963
		$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...
1964
		$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

1964
		$storeid = /** @scrutinizer ignore-call */ mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
Loading history...
1965
1966
		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

1966
		return /** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $storeid);
Loading history...
1967
	}
1968
1969
	/**
1970
	 * Function which sends response to organizer when attendee accepts, declines or proposes new time to a received meeting request.
1971
	 *
1972
	 * @param int   $status              response status of attendee
1973
	 * @param array $proposeNewTimeProps properties of attendee's proposal
1974
	 * @param mixed $body
1975
	 * @param mixed $store
1976
	 * @param mixed $basedate            date of occurrence which attendee has responded
1977
	 * @param mixed $calFolder
1978
	 */
1979
	public function createResponse($status, $proposeNewTimeProps, $body, $store, $basedate, $calFolder): void {
1980
		$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

1980
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [
Loading history...
1981
			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...
1982
			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...
1983
			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...
1984
			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...
1985
			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...
1986
			$this->proptags['goid'],
1987
			$this->proptags['goid2'],
1988
			$this->proptags['location'],
1989
			$this->proptags['startdate'],
1990
			$this->proptags['duedate'],
1991
			$this->proptags['recurring'],
1992
			$this->proptags['recurring_pattern'],
1993
			$this->proptags['recurrence_data'],
1994
			$this->proptags['timezone_data'],
1995
			$this->proptags['timezone'],
1996
			$this->proptags['updatecounter'],
1997
			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...
1998
			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...
1999
			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...
2000
			$this->proptags['is_exception'],
2001
		]);
2002
2003
		$props = [];
2004
2005
		if ($basedate !== false && !$this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
2006
			// we are creating response from a recurring calendar item object
2007
			// We found basedate,so opened occurrence and get properties.
2008
			$recurr = new Recurrence($store, $this->message);
2009
			$exception = $recurr->getExceptionAttachment($basedate);
2010
2011
			if ($exception) {
2012
				// Exception found, Now retrieve properties
2013
				$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

2013
				$imessage = /** @scrutinizer ignore-call */ mapi_attach_openobj($exception, 0);
Loading history...
2014
				$imsgprops = mapi_getprops($imessage);
2015
2016
				// If location is provided, copy it to the response
2017
				if (isset($imsgprops[$this->proptags['location']])) {
2018
					$messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']];
2019
				}
2020
2021
				// Update $messageprops with timings of occurrence
2022
				$messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']];
2023
				$messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']];
2024
2025
				// Meeting related properties
2026
				$props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']];
2027
				$props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']];
2028
				$props[PR_SUBJECT] = $imsgprops[PR_SUBJECT];
2029
			}
2030
			else {
2031
				// Exceptions is deleted.
2032
				// Update $messageprops with timings of occurrence
2033
				$messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate);
2034
				$messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate);
2035
2036
				$props[$this->proptags['meetingstatus']] = olNonMeeting;
2037
				$props[$this->proptags['responsestatus']] = olResponseNone;
2038
			}
2039
2040
			$props[$this->proptags['recurring']] = false;
2041
			$props[$this->proptags['is_exception']] = true;
2042
		}
2043
		else {
2044
			// we are creating a response from meeting request mail (it could be recurring or non-recurring)
2045
			// Send all recurrence info in response, if this is a recurrence meeting.
2046
			$isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']];
2047
			$isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']];
2048
			if ($isRecurring || $isException) {
2049
				if ($isRecurring) {
2050
					$props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']];
2051
				}
2052
				if ($isException) {
2053
					$props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']];
2054
				}
2055
				$calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
2056
2057
				$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

2057
				$calendaritem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $calendaritems[0]);
Loading history...
2058
				$recurr = new Recurrence($store, $calendaritem);
2059
			}
2060
		}
2061
2062
		// we are sending a response for recurring meeting request (or exception), so set some required properties
2063
		if (isset($recurr) && $recurr) {
2064
			if (!empty($messageprops[$this->proptags['recurring_pattern']])) {
2065
				$props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']];
2066
			}
2067
2068
			if (!empty($messageprops[$this->proptags['recurrence_data']])) {
2069
				$props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']];
2070
			}
2071
2072
			$props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']];
2073
			$props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']];
2074
2075
			$this->generateRecurDates($recurr, $messageprops, $props);
2076
		}
2077
2078
		// Create a response message
2079
		$recip = [];
2080
		$recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID];
2081
		$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...
2082
		$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...
2083
		$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...
2084
		$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...
2085
		$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...
2086
2087
		$subjectprefix = '';
2088
2089
		switch ($status) {
2090
			case olResponseAccepted:
2091
				$classpostfix = 'Pos';
2092
				$subjectprefix = dgettext('zarafa', 'Accepted');
2093
				break;
2094
2095
			case olResponseDeclined:
2096
				$classpostfix = 'Neg';
2097
				$subjectprefix = dgettext('zarafa', 'Declined');
2098
				break;
2099
2100
			case olResponseTentative:
2101
				$classpostfix = 'Tent';
2102
				$subjectprefix = dgettext('zarafa', 'Tentatively accepted');
2103
				break;
2104
		}
2105
2106
		if (!empty($proposeNewTimeProps)) {
2107
			// if attendee has proposed new time then change subject prefix
2108
			$subjectprefix = dgettext('zarafa', 'New Time Proposed');
2109
		}
2110
2111
		$props[PR_SUBJECT] = $subjectprefix . ': ' . $messageprops[PR_SUBJECT];
2112
2113
		$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...
2114
		if (isset($messageprops[PR_OWNER_APPT_ID])) {
2115
			$props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID];
2116
		}
2117
2118
		// Set GlobalId AND CleanGlobalId, if exception then also set basedate into GlobalId(0x3).
2119
		$props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate);
2120
		$props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']];
2121
		$props[$this->proptags['updatecounter']] = isset($messageprops[$this->proptags['updatecounter']]) ? $messageprops[$this->proptags['updatecounter']] : 0;
2122
2123
		if (!empty($proposeNewTimeProps)) {
2124
			// merge proposal properties to message properties which will be sent to organizer
2125
			$props = $proposeNewTimeProps + $props;
2126
		}
2127
2128
		// Set body message in Appointment
2129
		if (isset($body)) {
2130
			$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...
2131
		}
2132
2133
		// PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message
2134
		$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...
2135
		$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...
2136
2137
		// Set startdate and duedate in response mail.
2138
		$props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']];
2139
		$props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']];
2140
2141
		// responselocation is used in the UI in Outlook on the response message
2142
		if (isset($messageprops[$this->proptags['location']])) {
2143
			$props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']];
2144
			$props[$this->proptags['location']] = $messageprops[$this->proptags['location']];
2145
		}
2146
2147
		$message = $this->createOutgoingMessage($store);
2148
2149
		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

2149
		/** @scrutinizer ignore-call */ 
2150
  mapi_setprops($message, $props);
Loading history...
2150
		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

2150
		/** @scrutinizer ignore-call */ 
2151
  mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
Loading history...
2151
		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

2151
		/** @scrutinizer ignore-call */ 
2152
  mapi_savechanges($message);
Loading history...
2152
		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

2152
		/** @scrutinizer ignore-call */ 
2153
  mapi_message_submitmessage($message);
Loading history...
2153
	}
2154
2155
	/**
2156
	 * Function which finds items in calendar based on globalId and cleanGlobalId.
2157
	 *
2158
	 * @param string $goid             GlobalID(0x3) of item
2159
	 * @param mixed  $calendar         MAPI_folder of user (optional)
2160
	 * @param bool   $useCleanGlobalId if true then search should be performed on cleanGlobalId(0x23) else globalId(0x3)
2161
	 *
2162
	 * @return mixed
2163
	 */
2164
	public function findCalendarItems($goid, $calendar = false, $useCleanGlobalId = false) {
2165
		if ($calendar === false) {
2166
			// Open the Calendar
2167
			$calendar = $this->openDefaultCalendar();
2168
		}
2169
2170
		// Find the item by restricting all items to the correct ID
2171
		$restrict = [
2172
			RES_PROPERTY,
2173
			[
2174
				RELOP => RELOP_EQ,
2175
				ULPROPTAG => ($useCleanGlobalId === true ? $this->proptags['goid2'] : $this->proptags['goid']),
2176
				VALUE => $goid,
2177
			],
2178
		];
2179
2180
		$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

2180
		$calendarcontents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($calendar);
Loading history...
2181
2182
		$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

2182
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
Loading history...
2183
2184
		if (empty($rows)) {
2185
			return;
2186
		}
2187
2188
		$calendaritems = [];
2189
2190
		// In principle, there should only be one row, but we'll handle them all just in case
2191
		foreach ($rows as $row) {
2192
			$calendaritems[] = $row[PR_ENTRYID];
2193
		}
2194
2195
		return $calendaritems;
2196
	}
2197
2198
	// Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the
2199
	// same SMTP address when converted to SMTP
2200
	public function compareABEntryIDs($entryid1, $entryid2): bool {
2201
		// If the session was not passed, just do a 'normal' compare.
2202
		if (!$this->session) {
2203
			return $entryid1 == $entryid2;
2204
		}
2205
2206
		$smtp1 = $this->getSMTPAddress($entryid1);
2207
		$smtp2 = $this->getSMTPAddress($entryid2);
2208
2209
		if ($smtp1 == $smtp2) {
2210
			return true;
2211
		}
2212
2213
		return false;
2214
	}
2215
2216
	// Gets the SMTP address of the passed addressbook entryid
2217
	public function getSMTPAddress($entryid) {
2218
		if (!$this->session) {
2219
			return false;
2220
		}
2221
2222
		try {
2223
			$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

2223
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2224
			$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

2224
			$abitem = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $entryid);
Loading history...
2225
2226
			if (!$abitem) {
2227
				return '';
2228
			}
2229
		}
2230
		catch (MAPIException $e) {
2231
			return '';
2232
		}
2233
2234
		$props = mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_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_ADDRTYPE 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

2234
		$props = /** @scrutinizer ignore-call */ mapi_getprops($abitem, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS]);
Loading history...
2235
2236
		if ($props[PR_ADDRTYPE] == 'SMTP') {
2237
			return $props[PR_EMAIL_ADDRESS];
2238
		}
2239
2240
		return $props[PR_SMTP_ADDRESS];
2241
	}
2242
2243
	/**
2244
	 * Gets the properties associated with the owner of the passed store:
2245
	 * PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID, PR_SEARCH_KEY.
2246
	 *
2247
	 * @param mixed $store                  message store
2248
	 * @param bool  $fallbackToLoggedInUser If true then return properties of logged in user instead of mailbox owner.
2249
	 *                                      Not used when passed store is public store.
2250
	 *                                      For public store we are always returning logged in user's info.
2251
	 *
2252
	 * @return array|false properties of logged in user in an array in sequence of display_name, email address, address type, entryid and search key
2253
	 *
2254
	 * @psalm-return false|list{mixed, mixed, mixed, mixed, mixed}
2255
	 */
2256
	public function getOwnerAddress($store, $fallbackToLoggedInUser = true) {
2257
		if (!$this->session) {
2258
			return false;
2259
		}
2260
2261
		$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
0 ignored issues
show
The constant PR_MAILBOX_OWNER_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

2261
		$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID]);
Loading history...
The constant PR_USER_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2262
2263
		$ownerEntryId = false;
2264
		if (isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) {
2265
			$ownerEntryId = $storeProps[PR_USER_ENTRYID];
2266
		}
2267
2268
		if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) {
2269
			$ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID];
2270
		}
2271
2272
		if ($ownerEntryId) {
2273
			$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

2273
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2274
2275
			$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

2275
			$zarafaUser = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $ownerEntryId);
Loading history...
2276
			if (!$zarafaUser) {
2277
				return false;
2278
			}
2279
2280
			$ownerProps = mapi_getprops($zarafaUser, [PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SEARCH_KEY]);
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_DISPLAY_NAME 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...
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2281
2282
			$addrType = $ownerProps[PR_ADDRTYPE];
2283
			$name = $ownerProps[PR_DISPLAY_NAME];
2284
			$emailAddr = $ownerProps[PR_EMAIL_ADDRESS];
2285
			$searchKey = $ownerProps[PR_SEARCH_KEY];
2286
			$entryId = $ownerEntryId;
2287
2288
			return [$name, $emailAddr, $addrType, $entryId, $searchKey];
2289
		}
2290
2291
		return false;
2292
	}
2293
2294
	// Opens this session's default message store
2295
	public function openDefaultStore() {
2296
		$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

2296
		$storestable = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2297
		$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

2297
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
Loading history...
2298
2299
		foreach ($rows as $row) {
2300
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
2301
				$entryid = $row[PR_ENTRYID];
2302
				break;
2303
			}
2304
		}
2305
2306
		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...
2307
			return false;
2308
		}
2309
2310
		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

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

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

2414
		$attachmentTableTo = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($copyTo);
Loading history...
2415
		if ($attachmentTableTo) {
2416
			$attachments = mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
0 ignored issues
show
The constant PR_ATTACH_METHOD 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

2416
			$attachments = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
Loading history...
The constant PR_ATTACH_NUM 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...
2417
2418
			foreach ($attachments as $attachProps) {
2419
				/* remove exceptions too? */
2420
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2421
					continue;
2422
				}
2423
				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

2423
				/** @scrutinizer ignore-call */ 
2424
    mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
Loading history...
2424
			}
2425
		}
2426
2427
		/* copy new attachments */
2428
		$attachmentTableFrom = mapi_message_getattachmenttable($copyFrom);
2429
		if ($attachmentTableFrom) {
2430
			$attachments = mapi_table_queryallrows($attachmentTableFrom, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2431
2432
			foreach ($attachments as $attachProps) {
2433
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2434
					continue;
2435
				}
2436
2437
				$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

2437
				$attachOld = /** @scrutinizer ignore-call */ mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
Loading history...
2438
				$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

2438
				$attachNewResourceMsg = /** @scrutinizer ignore-call */ mapi_message_createattach($copyTo);
Loading history...
2439
				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

2439
				/** @scrutinizer ignore-call */ 
2440
    mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
Loading history...
2440
				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

2440
				/** @scrutinizer ignore-call */ 
2441
    mapi_savechanges($attachNewResourceMsg);
Loading history...
2441
			}
2442
		}
2443
	}
2444
2445
	/**
2446
	 * Function which replaces recipients in copyTo with recipients from copyFrom.
2447
	 *
2448
	 * @param mixed $copyFrom   MAPI_message from which recipients are to be copied
2449
	 * @param mixed $copyTo     MAPI_message to which recipients are to be copied
2450
	 * @param bool  $isDelegate indicates whether delegate is processing
2451
	 *                          so don't copy delegate information to recipient table
2452
	 */
2453
	public function replaceRecipients($copyFrom, $copyTo, $isDelegate = false): void {
2454
		$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

2454
		$recipientTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($copyFrom);
Loading history...
2455
2456
		// If delegate, then do not add the delegate in recipients
2457
		if ($isDelegate) {
2458
			$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

2458
			$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...
2459
			$res = [
2460
				RES_PROPERTY,
2461
				[
2462
					RELOP => RELOP_NE,
2463
					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...
2464
					VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2465
				],
2466
			];
2467
			$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

2467
			$recipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipientTable, $this->recipprops, $res);
Loading history...
2468
		}
2469
		else {
2470
			$recipients = mapi_table_queryallrows($recipientTable, $this->recipprops);
2471
		}
2472
2473
		$copyToRecipientTable = mapi_message_getrecipienttable($copyTo);
2474
		$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...
2475
2476
		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

2476
		/** @scrutinizer ignore-call */ 
2477
  mapi_message_modifyrecipients($copyTo, MODRECIP_REMOVE, $copyToRecipientRows);
Loading history...
2477
		mapi_message_modifyrecipients($copyTo, MODRECIP_ADD, $recipients);
2478
	}
2479
2480
	/**
2481
	 * Function creates meeting item in resource's calendar.
2482
	 *
2483
	 * @param resource $message  MAPI_message which is to create in resource's calendar
2484
	 * @param bool     $cancel   cancel meeting
2485
	 * @param mixed    $prefix   prefix for subject of meeting
2486
	 * @param mixed    $basedate
2487
	 *
2488
	 * @return (mixed|resource)[][]
2489
	 *
2490
	 * @psalm-return list<array{store: resource, folder: mixed, msg: mixed}>
2491
	 */
2492
	public function bookResources($message, $cancel, $prefix, $basedate = false): array {
2493
		if (!$this->enableDirectBooking) {
2494
			return [];
2495
		}
2496
2497
		// Get the properties of the message
2498
		$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

2498
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($message);
Loading history...
2499
2500
		if ($basedate) {
2501
			$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...
2502
2503
			$messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate);
2504
			$messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']];
2505
2506
			// Delete properties which are not needed.
2507
			$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_HIDDEN 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_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_LINKID 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...
2508
			foreach ($deleteProps as $propID) {
2509
				if (isset($messageprops[$propID])) {
2510
					unset($messageprops[$propID]);
2511
				}
2512
			}
2513
2514
			if (isset($messageprops[$this->proptags['recurring']])) {
2515
				$messageprops[$this->proptags['recurring']] = false;
2516
			}
2517
2518
			// Set Outlook properties
2519
			$messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']];
2520
			$messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']];
2521
			$messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']];
2522
			$messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']];
2523
			$messageprops[$this->proptags['attendee_critical_change']] = time();
2524
			$messageprops[$this->proptags['owner_critical_change']] = time();
2525
		}
2526
2527
		// Get resource recipients
2528
		$getResourcesRestriction = [
2529
			RES_PROPERTY,
2530
			[
2531
				RELOP => RELOP_EQ,	// Equals recipient type 3: Resource
2532
				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...
2533
				VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
2534
			],
2535
		];
2536
		$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

2536
		$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
2537
		$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

2537
		$resourceRecipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
Loading history...
2538
2539
		$this->errorSetResource = false;
2540
		$resourceRecipData = [];
2541
2542
		// Put appointment into store resource users
2543
		$i = 0;
2544
		$len = count($resourceRecipients);
2545
		while (!$this->errorSetResource && $i < $len) {
2546
			$userStore = $this->openCustomUserStore($resourceRecipients[$i][PR_ENTRYID]);
2547
2548
			// Open root folder
2549
			$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

2549
			$userRoot = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($userStore, null);
Loading history...
2550
2551
			// Get calendar entryID
2552
			$userRootProps = mapi_getprops($userRoot, [PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS]);
0 ignored issues
show
The constant PR_STORE_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_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2553
2554
			// Open Calendar folder
2555
			$accessToFolder = false;
0 ignored issues
show
The assignment to $accessToFolder is dead and can be removed.
Loading history...
2556
2557
			try {
2558
				// @FIXME this checks delegate has access to resource's calendar folder
2559
				// but it should use boss' credentials
2560
2561
				$accessToFolder = $this->checkCalendarWriteAccess($this->store);
2562
				if ($accessToFolder) {
2563
					$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
2564
				}
2565
			}
2566
			catch (MAPIException $e) {
2567
				$e->setHandled();
2568
				$this->errorSetResource = 1; // No access
2569
			}
2570
2571
			if ($accessToFolder) {
2572
				/**
2573
				 * Get the LocalFreebusy message that contains the properties that
2574
				 * are set to accept or decline resource meeting requests.
2575
				 */
2576
				$localFreebusyMsg = FreeBusy::getLocalFreeBusyMessage($userStore);
2577
				if ($localFreebusyMsg) {
2578
					$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_DISALLOW_OVERLAPPING_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_AUTO_ACCEPT_APPTS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2579
2580
					$acceptMeetingRequests = isset($props[PR_SCHDINFO_AUTO_ACCEPT_APPTS]) ? $props[PR_SCHDINFO_AUTO_ACCEPT_APPTS] : false;
2581
					$declineRecurringMeetingRequests = isset($props[PR_SCHDINFO_DISALLOW_RECURRING_APPTS]) ? $props[PR_SCHDINFO_DISALLOW_RECURRING_APPTS] : false;
2582
					$declineConflictingMeetingRequests = isset($props[PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS]) ? $props[PR_SCHDINFO_DISALLOW_OVERLAPPING_APPTS] : false;
2583
2584
					if (!$acceptMeetingRequests) {
2585
						/*
2586
						 * When a resource has not been set to automatically accept meeting requests,
2587
						 * the meeting request has to be sent to him rather than being put directly into
2588
						 * his calendar. No error should be returned.
2589
						 */
2590
						// $errorSetResource = 2;
2591
						$this->nonAcceptingResources[] = $resourceRecipients[$i];
2592
					}
2593
					else {
2594
						if ($declineRecurringMeetingRequests && !$cancel) {
2595
							// Check if appointment is recurring
2596
							if ($messageprops[$this->proptags['recurring']]) {
2597
								$this->errorSetResource = 3;
2598
							}
2599
						}
2600
						if ($declineConflictingMeetingRequests && !$cancel) {
2601
							// Check for conflicting items
2602
							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...
2603
								$this->errorSetResource = 4; // Conflict
2604
							}
2605
						}
2606
					}
2607
				}
2608
			}
2609
2610
			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...
2611
				/**
2612
				 * First search on GlobalID(0x3)
2613
				 * 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.
2614
				 * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesn't matter if search is based on GlobalID.
2615
				 */
2616
				$rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder);
2617
2618
				/*
2619
				 * If no entry is found then
2620
				 * 1) Resource doesn't have meeting in Calendar. Seriously!!
2621
				 * OR
2622
				 * 2) We were looking for occurrence item but Resource has whole series
2623
				 */
2624
				if (empty($rows)) {
2625
					/**
2626
					 * Now search on CleanGlobalID(0x23) WHY???
2627
					 * Because we are looking recurring item.
2628
					 *
2629
					 * Possible results of this search
2630
					 * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID
2631
					 * 2) If Resource was booked for whole series then it should return series.
2632
					 */
2633
					$rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true);
2634
2635
					$newResourceMsg = false;
2636
					if (!empty($rows)) {
2637
						// Since we are looking for recurring item, open every result and check for 'recurring' property.
2638
						foreach ($rows as $row) {
2639
							$ResourceMsg = mapi_msgstore_openentry($userStore, $row);
2640
							$ResourceMsgProps = mapi_getprops($ResourceMsg, [$this->proptags['recurring']]);
2641
2642
							if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) {
2643
								$newResourceMsg = $ResourceMsg;
2644
								break;
2645
							}
2646
						}
2647
					}
2648
2649
					// Still no results found. I giveup, create new message.
2650
					if (!$newResourceMsg) {
2651
						$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

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

2756
					/** @scrutinizer ignore-call */ 
2757
     mapi_setprops($newResourceMsg, $messageprops);
Loading history...
2757
2758
					// Copy attachments
2759
					$this->replaceAttachments($message, $newResourceMsg);
2760
2761
					// Copy all recipients too
2762
					$this->replaceRecipients($message, $newResourceMsg);
2763
2764
					// Now add organizer also to recipient table
2765
					$recips = [];
2766
					$this->addOrganizer($messageprops, $recips);
2767
2768
					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

2768
					/** @scrutinizer ignore-call */ 
2769
     mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
Loading history...
2769
				}
2770
2771
				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

2771
				/** @scrutinizer ignore-call */ 
2772
    mapi_savechanges($newResourceMsg);
Loading history...
2772
2773
				$resourceRecipData[] = [
2774
					'store' => $userStore,
2775
					'folder' => $calFolder,
2776
					'msg' => $newResourceMsg,
2777
				];
2778
				$this->includesResources = true;
2779
			}
2780
			else {
2781
				/*
2782
				 * If no other errors occurred and you have no access to the
2783
				 * folder of the resource, throw an error=1.
2784
				 */
2785
				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...
2786
					$this->errorSetResource = 1;
2787
				}
2788
2789
				for ($j = 0, $len = count($resourceRecipData); $j < $len; ++$j) {
2790
					// Get the EntryID
2791
					$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

2791
					$props = /** @scrutinizer ignore-call */ mapi_message_getprops($resourceRecipData[$j]['msg']);
Loading history...
2792
2793
					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

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

2842
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2843
2844
		// Copy recipients list
2845
		$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

2845
		$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($occurrenceItem);
Loading history...
2846
		// If delegate, then do not add the delegate in recipients
2847
		if ($isDelegate) {
2848
			$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...
2849
			$res = [
2850
				RES_PROPERTY,
2851
				[
2852
					RELOP => RELOP_NE,
2853
					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...
2854
					VALUE => [PR_EMAIL_ADDRESS => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS]],
2855
				],
2856
			];
2857
			$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

2857
			$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops, $res);
Loading history...
2858
		}
2859
		else {
2860
			$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
2861
		}
2862
2863
		// add owner to recipient table
2864
		$this->addOrganizer($exception_props, $recips, true);
2865
2866
		// add delegator to meetings
2867
		if ($isDelegate) {
2868
			$this->addDelegator($exception_props, $recips);
2869
		}
2870
2871
		$exception_props[$this->proptags['meetingstatus']] = olMeetingReceived;
2872
		$exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded;
2873
2874
		if (isset($exception_props[$this->proptags['intendedbusystatus']])) {
2875
			if ($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) {
2876
				$exception_props[$this->proptags['busystatus']] = fbTentative;
2877
			}
2878
			else {
2879
				$exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']];
2880
			}
2881
		// we already have intendedbusystatus value in $exception_props so no need to copy it
2882
		}
2883
		else {
2884
			$exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy;
2885
		}
2886
2887
		if ($userAction) {
2888
			$addrInfo = $this->getOwnerAddress($this->store);
2889
2890
			// if user has responded then set replytime and name
2891
			$exception_props[$this->proptags['replytime']] = time();
2892
			if (!empty($addrInfo)) {
2893
				$exception_props[$this->proptags['apptreplyname']] = $addrInfo[0];
2894
			}
2895
		}
2896
2897
		if ($recurr->isException($basedate)) {
2898
			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2899
		}
2900
		else {
2901
			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2902
		}
2903
2904
		// Move the occurrenceItem to the waste basket
2905
		if ($move) {
2906
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2907
			$sourcefolder = mapi_msgstore_openentry($store, $exception_props[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

2907
			$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[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...
2908
			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

2908
			/** @scrutinizer ignore-call */ 
2909
   mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2909
		}
2910
2911
		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

2911
		/** @scrutinizer ignore-call */ 
2912
  mapi_savechanges($recurringItem);
Loading history...
2912
	}
2913
2914
	/**
2915
	 * Function which merges an exception mapi message to recurring message.
2916
	 * This will be used when we receive recurring meeting request and we already have an exception message
2917
	 * of same meeting in calendar and we need to remove that exception message and add it to attachment table
2918
	 * of recurring meeting.
2919
	 *
2920
	 * @param resource $recurringItem  reference to MAPI_message of recurring item
2921
	 * @param resource $occurrenceItem reference to MAPI_message of occurrence
2922
	 * @param mixed    $basedate       basedate of occurrence
2923
	 * @param resource $store          user store
2924
	 */
2925
	public function mergeException(&$recurringItem, &$occurrenceItem, $basedate, $store): void {
2926
		$recurr = new Recurrence($store, $recurringItem);
2927
2928
		// Copy properties from meeting request
2929
		$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

2929
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2930
2931
		// Get recipient list from message and add it to exception attachment
2932
		$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

2932
		$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($occurrenceItem);
Loading history...
2933
		$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

2933
		$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops);
Loading history...
2934
2935
		if ($recurr->isException($basedate)) {
2936
			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2937
		}
2938
		else {
2939
			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2940
		}
2941
2942
		// Move the occurrenceItem to the waste basket
2943
		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2944
		$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

2944
		$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
Loading history...
2945
		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

2945
		/** @scrutinizer ignore-call */ 
2946
  mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2946
2947
		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

2947
		/** @scrutinizer ignore-call */ 
2948
  mapi_savechanges($recurringItem);
Loading history...
2948
	}
2949
2950
	/**
2951
	 * Function which submits meeting request based on arguments passed to it.
2952
	 *
2953
	 * @param resource $message        MAPI_message whose meeting request is to be sent
2954
	 * @param bool     $cancel         if true send request, else send cancellation
2955
	 * @param mixed    $prefix         subject prefix
2956
	 * @param mixed    $basedate       basedate for an occurrence
2957
	 * @param mixed    $recurObject    recurrence object of mr
2958
	 * @param bool     $copyExceptions When sending update mail for recurring item then we don't send exceptions in attachments
2959
	 * @param mixed    $modifiedRecips
2960
	 * @param mixed    $deletedRecips
2961
	 */
2962
	public function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $modifiedRecips = false, $deletedRecips = false): void {
2963
		$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

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

3032
			$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3033
			$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

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

3095
		/** @scrutinizer ignore-call */ 
3096
  mapi_setprops($new, $newmessageprops);
Loading history...
3096
3097
		// Copy attachments
3098
		$this->replaceAttachments($message, $new, $copyExceptions);
3099
3100
		// Retrieve only those recipient who should receive this meeting request.
3101
		$stripResourcesRestriction = [
3102
			RES_AND,
3103
			[
3104
				[
3105
					RES_BITMASK,
3106
					[
3107
						ULTYPE => BMR_EQZ,
3108
						ULPROPTAG => PR_RECIPIENT_FLAGS,
3109
						ULMASK => recipExceptionalDeleted,
3110
					],
3111
				],
3112
				[
3113
					RES_BITMASK,
3114
					[
3115
						ULTYPE => BMR_EQZ,
3116
						ULPROPTAG => PR_RECIPIENT_FLAGS,
3117
						ULMASK => recipOrganizer,
3118
					],
3119
				],
3120
			],
3121
		];
3122
3123
		// In direct-booking mode, resources do not receive a meeting request
3124
		if ($this->enableDirectBooking) {
3125
			$stripResourcesRestriction[1][] = [
3126
				RES_PROPERTY,
3127
				[
3128
					RELOP => RELOP_NE,	// Does not equal recipient type: MAPI_BCC (Resource)
3129
					ULPROPTAG => PR_RECIPIENT_TYPE,
3130
					VALUE => [PR_RECIPIENT_TYPE => MAPI_BCC],
3131
				],
3132
			];
3133
		}
3134
3135
		// If no recipients were explicitly provided, we will send the update to all
3136
		// recipients from the meeting.
3137
		if ($modifiedRecips === false) {
3138
			$recipienttable = mapi_message_getrecipienttable($message);
3139
			$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction);
3140
3141
			if ($basedate && empty($modifiedRecips)) {
3142
				// Retrieve full list
3143
				$recipienttable = mapi_message_getrecipienttable($this->message);
3144
				$modifiedRecips = mapi_table_queryallrows($recipienttable, $this->recipprops);
3145
3146
				// Save recipients in exceptions
3147
				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

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

3190
			/** @scrutinizer ignore-call */ 
3191
   mapi_savechanges($new);
Loading history...
3191
3192
			// Submit message to non-resource recipients
3193
			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

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

3319
		$outgoing = /** @scrutinizer ignore-call */ mapi_folder_createmessage($outbox);
Loading history...
3320
3321
		// check if $store is set and it is not equal to $defaultStore (means its the delegation case)
3322
		if ($store !== false) {
3323
			$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

3323
			$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($store, [PR_ENTRYID]);
Loading history...
3324
			$userStoreProps = mapi_getprops($userStore, [PR_ENTRYID]);
3325
3326
			// @FIXME use entryid comparison functions here
3327
			if ($storeProps[PR_ENTRYID] !== $userStoreProps[PR_ENTRYID]) {
3328
				// get the delegator properties and set it into outgoing mail
3329
				$delegatorDetails = $this->getOwnerAddress($store, false);
3330
3331
				if (!empty($delegatorDetails)) {
3332
					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegatorDetails;
3333
					$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...
3334
					$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...
3335
					$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...
3336
					$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...
3337
					$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...
3338
				}
3339
3340
				// get the delegate properties and set it into outgoing mail
3341
				$delegateDetails = $this->getOwnerAddress($userStore, false);
3342
3343
				if (!empty($delegateDetails)) {
3344
					list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $delegateDetails;
3345
					$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...
3346
					$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...
3347
					$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...
3348
					$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...
3349
					$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...
3350
				}
3351
			}
3352
		}
3353
		else {
3354
			// normal user is sending mail, so both set of properties will be same
3355
			$userDetails = $this->getOwnerAddress($userStore);
3356
3357
			if (!empty($userDetails)) {
3358
				list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $userDetails;
3359
				$sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr;
3360
				$sentprops[PR_SENT_REPRESENTING_NAME] = $ownername;
3361
				$sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype;
3362
				$sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid;
3363
				$sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey;
3364
3365
				$sentprops[PR_SENDER_EMAIL_ADDRESS] = $owneremailaddr;
3366
				$sentprops[PR_SENDER_NAME] = $ownername;
3367
				$sentprops[PR_SENDER_ADDRTYPE] = $owneraddrtype;
3368
				$sentprops[PR_SENDER_ENTRYID] = $ownerentryid;
3369
				$sentprops[PR_SENDER_SEARCH_KEY] = $ownersearchkey;
3370
			}
3371
		}
3372
3373
		$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...
3374
3375
		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

3375
		/** @scrutinizer ignore-call */ 
3376
  mapi_setprops($outgoing, $sentprops);
Loading history...
3376
3377
		return $outgoing;
3378
	}
3379
3380
	/**
3381
	 * Function which checks that meeting in attendee's calendar is already updated
3382
	 * and we are checking an old meeting request. This function also will update property
3383
	 * meetingtype to indicate that its out of date meeting request.
3384
	 *
3385
	 * @return bool true if meeting request is outofdate else false if it is new
3386
	 */
3387
	public function isMeetingOutOfDate() {
3388
		$result = false;
3389
3390
		$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 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

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

3431
				/** @scrutinizer ignore-call */ 
3432
    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...
3432
				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

3432
				/** @scrutinizer ignore-call */ 
3433
    mapi_savechanges($this->message);
Loading history...
3433
3434
				$result = true;
3435
			}
3436
		}
3437
3438
		return $result;
3439
	}
3440
3441
	/**
3442
	 * Function which checks that if we have received a meeting response for an updated meeting in organizer's calendar.
3443
	 *
3444
	 * @param mixed $basedate basedate of the exception if we want to compare with exception
3445
	 *
3446
	 * @return bool true if meeting request is updated later
3447
	 */
3448
	public function isMeetingUpdated($basedate = false) {
3449
		$result = false;
3450
3451
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $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

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

3514
					$message = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
3515
				}
3516
			}
3517
		}
3518
		else {
3519
			// use normal message or recurring series message
3520
			$message = $this->message;
3521
		}
3522
3523
		if (!$message) {
3524
			return;
3525
		}
3526
3527
		$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

3527
		$newProps = /** @scrutinizer ignore-call */ mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
Loading history...
3528
3529
		// Check whether message is updated or not.
3530
		if (isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
3531
			return;
3532
		}
3533
3534
		if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) ||
3535
				($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) ||
3536
				$isRecurrenceChanged) {
3537
			$this->clearRecipientResponse($message);
3538
3539
			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

3539
			/** @scrutinizer ignore-call */ 
3540
   mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
Loading history...
3540
3541
			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

3541
			/** @scrutinizer ignore-call */ 
3542
   mapi_savechanges($message);
Loading history...
3542
			if ($attach) { // Also save attachment Object.
3543
				mapi_savechanges($attach);
3544
			}
3545
		}
3546
	}
3547
3548
	/**
3549
	 * Clear responses of all attendees who have replied in past.
3550
	 *
3551
	 * @param resource $message on which responses should be cleared
3552
	 */
3553
	public function clearRecipientResponse($message): void {
3554
		$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

3554
		$recipTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3555
		$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

3555
		$recipsRows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipTable, $this->recipprops);
Loading history...
3556
3557
		foreach ($recipsRows as $recipient) {
3558
			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...
3559
				// Recipient is attendee, set the trackstatus to 'Not Responded'
3560
				$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...
3561
			}
3562
			else {
3563
				// Recipient is organizer, this is not possible, but for safety
3564
				// it is best to clear the trackstatus for him as well by setting
3565
				// the trackstatus to 'Organized'.
3566
				$recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone;
3567
			}
3568
			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

3568
			/** @scrutinizer ignore-call */ 
3569
   mapi_message_modifyrecipients($message, MODRECIP_MODIFY, [$recipient]);
Loading history...
3569
		}
3570
	}
3571
3572
	/**
3573
	 * Function returns correspondent calendar item attached with the meeting request/response/cancellation.
3574
	 * This will only check for actual MAPIMessages in calendar folder, so if a meeting request is
3575
	 * for exception then this function will return recurring series for that meeting request
3576
	 * after that you need to use getExceptionItem function to get exception item that will be
3577
	 * fetched from the attachment table of recurring series MAPIMessage.
3578
	 *
3579
	 * @param bool $open boolean to indicate the function should return entryid or MAPIMessage. Defaults to true.
3580
	 *
3581
	 * @return bool|resource resource of calendar item
3582
	 */
3583
	public function getCorrespondentCalendarItem($open = true) {
3584
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_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_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

3584
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
3585
3586
		if (!$this->isMeetingRequest($props[PR_MESSAGE_CLASS]) && !$this->isMeetingRequestResponse($props[PR_MESSAGE_CLASS]) && !$this->isMeetingCancellation($props[PR_MESSAGE_CLASS])) {
3587
			// can work only with meeting requests/responses/cancellations
3588
			return false;
3589
		}
3590
3591
		// there is no goid - no items can be found - aborting
3592
		if (empty($props[$this->proptags['goid']])) {
3593
			return false;
3594
		}
3595
		$globalId = $props[$this->proptags['goid']];
3596
3597
		// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3598
		if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3599
			$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...
3600
3601
			$store = $delegatorStore['store'];
3602
			$calFolder = $delegatorStore[PR_IPM_APPOINTMENT_ENTRYID];
3603
		}
3604
		else {
3605
			$store = $this->store;
3606
			$calFolder = $this->openDefaultCalendar();
3607
		}
3608
3609
		$basedate = $this->getBasedateFromGlobalID($globalId);
3610
3611
		/**
3612
		 * First search for any appointments which correspond to the $globalId,
3613
		 * this can be the entire series (if the Meeting Request refers to the
3614
		 * entire series), or an particular Occurrence (if the meeting Request
3615
		 * contains a basedate).
3616
		 *
3617
		 * If we cannot find a corresponding item, and the $globalId contains
3618
		 * a $basedate, it might imply that a new exception will have to be
3619
		 * created for a series which is present in the calendar, we can look
3620
		 * that one up by searching for the $cleanGlobalId.
3621
		 */
3622
		$entryids = $this->findCalendarItems($globalId, $calFolder);
3623
		if ($basedate !== false && empty($entryids)) {
3624
			// only search if a goid2 is available
3625
			if (!empty($props[$this->proptags['goid2']])) {
3626
				$cleanGlobalId = $props[$this->proptags['goid2']];
3627
				$entryids = $this->findCalendarItems($cleanGlobalId, $calFolder, true);
3628
			}
3629
		}
3630
3631
		// there should be only one item returned
3632
		if (!empty($entryids) && count($entryids) === 1) {
3633
			// return only entryid
3634
			if ($open === false) {
3635
				return $entryids[0];
3636
			}
3637
3638
			// open calendar item and return it
3639
			if ($store) {
3640
				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

3640
				return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryids[0]);
Loading history...
3641
			}
3642
		}
3643
3644
		// no items found in calendar
3645
		return false;
3646
	}
3647
3648
	/**
3649
	 * Function returns exception item based on the basedate passed.
3650
	 *
3651
	 * @param mixed $recurringMessage Resource of Recurring meeting from calendar
3652
	 * @param mixed $basedate         basedate of exception that needs to be returned
3653
	 * @param mixed $store            store that contains the recurring calendar item
3654
	 *
3655
	 * @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...
3656
	 */
3657
	public function getExceptionItem($recurringMessage, $basedate, $store = false) {
3658
		$occurItem = false;
3659
3660
		$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

3660
		$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...
3661
3662
		// check if the passed item is recurring series
3663
		if (isset($props[$this->proptags['recurring']]) && $props[$this->proptags['recurring']] !== false) {
3664
			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...
3665
		}
3666
3667
		if ($store === false) {
3668
			// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3669
			if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3670
				$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3671
				$store = $delegatorStore['store'];
3672
			}
3673
			else {
3674
				$store = $this->store;
3675
			}
3676
		}
3677
3678
		$recurr = new Recurrence($store, $recurringMessage);
3679
		$attach = $recurr->getExceptionAttachment($basedate);
3680
		if ($attach) {
3681
			$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

3681
			$occurItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach);
Loading history...
3682
		}
3683
3684
		return $occurItem;
3685
	}
3686
3687
	/**
3688
	 * Function which checks whether received meeting request is either conflicting with other appointments or not.
3689
	 *
3690
	 * @param false|resource $message
3691
	 * @param false|resource $userStore
3692
	 * @param mixed          $calFolder calendar folder for conflict checking
3693
	 *
3694
	 * @return bool|int
3695
	 *
3696
	 * @psalm-return bool|int<1, max>
3697
	 */
3698
	public function isMeetingConflicting($message = false, $userStore = false, $calFolder = false) {
3699
		$returnValue = false;
3700
		$noOfInstances = 0;
3701
3702
		if ($message === false) {
3703
			$message = $this->message;
3704
		}
3705
3706
		$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

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

3912
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($calendarItem);
Loading history...
3913
		$recurrence = new Recurrence($store, $calendarItem);
3914
3915
		// Retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date')
3916
		$items = $recurrence->getItems($calendarItemProps[$this->proptags['clipstart']], $calendarItemProps[$this->proptags['clipend']] * (24 * 24 * 60), 30);
3917
		$localCategories = [];
3918
3919
		foreach ($items as $item) {
3920
			$recurrenceItems = $recurrence->getCalendarItems($store, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], [$this->proptags['goid'], $this->proptags['busystatus'], $this->proptags['categories']]);
3921
			foreach ($recurrenceItems as $recurrenceItem) {
3922
				// Check if occurrence is exception then get the local categories of that occurrence.
3923
				if (isset($recurrenceItem[$this->proptags['goid']]) && $recurrenceItem[$this->proptags['goid']] == $calendarItemProps[$this->proptags['goid']]) {
3924
					$exceptionAttach = $recurrence->getExceptionAttachment($recurrenceItem['basedate']);
3925
3926
					if ($exceptionAttach) {
3927
						$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

3927
						$exception = /** @scrutinizer ignore-call */ mapi_attach_openobj($exceptionAttach, 0);
Loading history...
3928
						$exceptionProps = mapi_getprops($exception, [$this->proptags['categories']]);
3929
						if (isset($exceptionProps[$this->proptags['categories']])) {
3930
							$localCategories[$recurrenceItem['basedate']] = $exceptionProps[$this->proptags['categories']];
3931
						}
3932
					}
3933
				}
3934
			}
3935
		}
3936
3937
		return $localCategories;
3938
	}
3939
3940
	/**
3941
	 * Helper function which is use to apply local categories on respective occurrences.
3942
	 *
3943
	 * @param mixed $calendarItem    meeting request item
3944
	 * @param mixed $store           store containing calendar folder
3945
	 * @param array $localCategories array contains basedate and array of categories
3946
	 */
3947
	public function applyLocalCategories($calendarItem, $store, $localCategories): void {
3948
		$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

3948
		$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...
3949
		$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

3949
		$message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
Loading history...
3950
		$recurrence = new Recurrence($store, $message);
3951
3952
		// Check for all occurrence if it is exception then modify the exception by setting up categories,
3953
		// Otherwise create new exception with categories.
3954
		foreach ($localCategories as $key => $value) {
3955
			if ($recurrence->isException($key)) {
3956
				$recurrence->modifyException([$this->proptags['categories'] => $value], $key);
3957
			}
3958
			else {
3959
				$recurrence->createException([$this->proptags['categories'] => $value], $key, false);
3960
			}
3961
			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

3961
			/** @scrutinizer ignore-call */ 
3962
   mapi_savechanges($message);
Loading history...
3962
		}
3963
	}
3964
}
3965