Passed
Push — master ( 99b1c3...4f1194 )
by
unknown
32:21 queued 20:25
created

Meetingrequest::getCorrespondentCalendarItem()   C

Complexity

Conditions 13
Paths 26

Size

Total Lines 63
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 13
eloc 25
c 3
b 1
f 0
nc 26
nop 1
dl 0
loc 63
rs 6.6166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

755
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message);
Loading history...
756
		$isDelegate = isset($messageprops[PR_RCVD_REPRESENTING_NAME]);
0 ignored issues
show
Bug introduced by
The constant PR_RCVD_REPRESENTING_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
757
758
		if ($sendresponse) {
759
			$this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $proposeNewTimeProps, $body, $store, $basedate, $calFolder);
760
		}
761
762
		/*
763
		 * Further processing depends on what user is receiving. User can receive recurring item, a single occurrence or a normal meeting.
764
		 * 1) If meeting req is of recurrence then we find all the occurrence in calendar because in past user might have received one or few occurrences.
765
		 * 2) If single occurrence then find occurrence itself using globalID and if item is not found then use cleanGlobalID to find main recurring item
766
		 * 3) Normal meeting req are handled normally as they were handled previously.
767
		 *
768
		 * Also user can respond(accept/decline) to item either from previewpane or from calendar by opening the item. If user is responding the meeting from previewpane
769
		 * and that item is not found in calendar then item is move else item is opened and all properties, attachments and recipient are copied from meeting request.
770
		 * If user is responding from calendar then item is opened and properties are set such as meetingstatus, responsestatus, busystatus etc.
771
		 */
772
		if ($this->isMeetingRequest($messageprops[PR_MESSAGE_CLASS])) {
0 ignored issues
show
Bug introduced by
The constant PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
773
			// This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item.
774
			if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] == true) {
775
				$calendarItem = false;
776
777
				// Find main recurring item based on GlobalID (0x3)
778
				$items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder);
779
				if (is_array($items)) {
780
					foreach ($items as $key => $entryid) {
781
						$calendarItem = mapi_msgstore_openentry($store, $entryid);
0 ignored issues
show
Bug introduced by
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

781
						$calendarItem = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
782
					}
783
				}
784
785
				$processed = false;
786
				if (!$calendarItem) {
787
					// Recurring item not found, so create new meeting in Calendar
788
					$calendarItem = mapi_folder_createmessage($calFolder);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

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

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

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

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

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

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

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

1280
				/** @scrutinizer ignore-call */ 
1281
    mapi_folder_deletemessages($calendar, $entryids);
Loading history...
1281
			}
1282
1283
			// All we have to do to decline, is to move the item to the waste basket
1284
			$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
1285
			$sourcefolder = $this->openParentFolder();
1286
1287
			$messageprops = mapi_getprops($this->message, [PR_ENTRYID]);
1288
1289
			// Release the message
1290
			$this->message = null;
1291
1292
			// Move the message to the waste basket
1293
			mapi_folder_copymessages($sourcefolder, [$messageprops[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

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

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

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

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

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

1559
				$recurr = new Recurrence(/** @scrutinizer ignore-type */ $this->openDefaultStore(), $this->message);
Loading history...
1560
1561
				// First send meetingrequest for recurring item
1562
				$this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $modifiedRecips, $deletedRecips);
1563
1564
				// Then send all meeting request for all exceptions
1565
				$exceptions = $recurr->getAllExceptions();
1566
				if ($exceptions) {
1567
					foreach ($exceptions as $exceptionBasedate) {
1568
						$attach = $recurr->getExceptionAttachment($exceptionBasedate);
1569
1570
						if ($attach) {
1571
							$occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY);
0 ignored issues
show
Bug introduced by
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

1571
							$occurrenceItem = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
1572
							$this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $modifiedRecips, $deletedRecips);
1573
							mapi_savechanges($attach);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

1718
		$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_PARENT_ENTRYID]);
Loading history...
Bug introduced by
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1719
1720
		return mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
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

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

1790
			$inbox = /** @scrutinizer ignore-call */ mapi_msgstore_getreceivefolder($store ? $store : $this->store);
Loading history...
1791
			$inboxprops = mapi_getprops($inbox, [$prop]);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

1881
				$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
1882
				$folderProps = mapi_getprops($folder, [PR_ACCESS]);
0 ignored issues
show
Bug introduced by
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

1882
				$folderProps = /** @scrutinizer ignore-call */ mapi_getprops($folder, [PR_ACCESS]);
Loading history...
Bug introduced by
The constant PR_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1883
				if (($folderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) === MAPI_ACCESS_CREATE_CONTENTS) {
1884
					$accessToFolder = true;
1885
				}
1886
			}
1887
			catch (MAPIException $e) {
1888
				// we don't have rights to open folder, so return false
1889
				if ($e->getCode() == MAPI_E_NO_ACCESS) {
0 ignored issues
show
Bug introduced by
The constant MAPI_E_NO_ACCESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1890
					return $accessToFolder;
1891
				}
1892
1893
				// rethrow other errors
1894
				throw $e;
1895
			}
1896
		}
1897
1898
		return $accessToFolder;
1899
	}
1900
1901
	/**
1902
	 * Function checks whether user has access over the specified folder or not.
1903
	 *
1904
	 * @param mixed $store
1905
	 *
1906
	 * @return bool true if user has an access over the folder, false if not
1907
	 */
1908
	public function checkCalendarWriteAccess($store = false) {
1909
		if ($store === false) {
1910
			// If this meeting request is received by a delegate then open delegator's store.
1911
			$messageProps = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
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

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

1950
		$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
1951
1952
		try {
1953
			$mailuser = mapi_ab_openentry($ab, $ownerentryid);
0 ignored issues
show
Bug introduced by
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

1953
			$mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($ab, $ownerentryid);
Loading history...
1954
		}
1955
		catch (MAPIException $e) {
1956
			return;
1957
		}
1958
1959
		$mailuserprops = mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
0 ignored issues
show
Bug introduced by
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

1959
		$mailuserprops = /** @scrutinizer ignore-call */ mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
Loading history...
Bug introduced by
The constant PR_EMAIL_ADDRESS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1960
		$storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
0 ignored issues
show
Bug introduced by
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

1960
		$storeid = /** @scrutinizer ignore-call */ mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
Loading history...
1961
1962
		return mapi_openmsgstore($this->session, $storeid);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

2145
		/** @scrutinizer ignore-call */ 
2146
  mapi_setprops($message, $props);
Loading history...
2146
		mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
0 ignored issues
show
Bug introduced by
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

2146
		/** @scrutinizer ignore-call */ 
2147
  mapi_message_modifyrecipients($message, MODRECIP_ADD, [$recip]);
Loading history...
2147
		mapi_savechanges($message);
0 ignored issues
show
Bug introduced by
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

2147
		/** @scrutinizer ignore-call */ 
2148
  mapi_savechanges($message);
Loading history...
2148
		mapi_message_submitmessage($message);
0 ignored issues
show
Bug introduced by
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

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

2176
		$calendarcontents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($calendar);
Loading history...
2177
2178
		$rows = mapi_table_queryallrows($calendarcontents, [PR_ENTRYID], $restrict);
0 ignored issues
show
Bug introduced by
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

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

2219
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2220
			$abitem = mapi_ab_openentry($ab, $entryid);
0 ignored issues
show
Bug introduced by
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

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

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

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

2269
			$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2270
2271
			$zarafaUser = mapi_ab_openentry($ab, $ownerEntryId);
0 ignored issues
show
Bug introduced by
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

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

2292
		$storestable = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2293
		$rows = mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
0 ignored issues
show
Bug introduced by
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

2293
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestable, [PR_ENTRYID, PR_DEFAULT_STORE]);
Loading history...
Bug introduced by
The constant PR_DEFAULT_STORE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2294
2295
		foreach ($rows as $row) {
2296
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
2297
				$entryid = $row[PR_ENTRYID];
2298
				break;
2299
			}
2300
		}
2301
2302
		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...
2303
			return false;
2304
		}
2305
2306
		return mapi_openmsgstore($this->session, $entryid);
0 ignored issues
show
Bug introduced by
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

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

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

2409
		$attachmentTableTo = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($copyTo);
Loading history...
2410
		if ($attachmentTableTo) {
2411
			$attachments = mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
0 ignored issues
show
Bug introduced by
The constant PR_EXCEPTION_STARTTIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant PR_ATTACH_METHOD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant PR_ATTACH_NUM was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
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

2411
			$attachments = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachmentTableTo, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
Loading history...
2412
2413
			foreach ($attachments as $attachProps) {
2414
				/* remove exceptions too? */
2415
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2416
					continue;
2417
				}
2418
				mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
0 ignored issues
show
Bug introduced by
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

2418
				/** @scrutinizer ignore-call */ 
2419
    mapi_message_deleteattach($copyTo, $attachProps[PR_ATTACH_NUM]);
Loading history...
2419
			}
2420
		}
2421
2422
		/* copy new attachments */
2423
		$attachmentTableFrom = mapi_message_getattachmenttable($copyFrom);
2424
		if ($attachmentTableFrom) {
2425
			$attachments = mapi_table_queryallrows($attachmentTableFrom, [PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME]);
2426
2427
			foreach ($attachments as $attachProps) {
2428
				if (!$copyExceptions && $attachProps[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG && isset($attachProps[PR_EXCEPTION_STARTTIME])) {
2429
					continue;
2430
				}
2431
2432
				$attachOld = mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
0 ignored issues
show
Bug introduced by
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

2432
				$attachOld = /** @scrutinizer ignore-call */ mapi_message_openattach($copyFrom, (int) $attachProps[PR_ATTACH_NUM]);
Loading history...
2433
				$attachNewResourceMsg = mapi_message_createattach($copyTo);
0 ignored issues
show
Bug introduced by
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

2433
				$attachNewResourceMsg = /** @scrutinizer ignore-call */ mapi_message_createattach($copyTo);
Loading history...
2434
				mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
0 ignored issues
show
Bug introduced by
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

2434
				/** @scrutinizer ignore-call */ 
2435
    mapi_copyto($attachOld, [], [], $attachNewResourceMsg, 0);
Loading history...
2435
				mapi_savechanges($attachNewResourceMsg);
0 ignored issues
show
Bug introduced by
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

2435
				/** @scrutinizer ignore-call */ 
2436
    mapi_savechanges($attachNewResourceMsg);
Loading history...
2436
			}
2437
		}
2438
	}
2439
2440
	/**
2441
	 * Function which replaces recipients in copyTo with recipients from copyFrom.
2442
	 *
2443
	 * @param mixed $copyFrom   MAPI_message from which recipients are to be copied
2444
	 * @param mixed $copyTo     MAPI_message to which recipients are to be copied
2445
	 * @param bool  $isDelegate indicates whether delegate is processing
2446
	 *                          so don't copy delegate information to recipient table
2447
	 */
2448
	public function replaceRecipients($copyFrom, $copyTo, $isDelegate = false): void {
2449
		$recipientTable = mapi_message_getrecipienttable($copyFrom);
0 ignored issues
show
Bug introduced by
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

2449
		$recipientTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($copyFrom);
Loading history...
2450
2451
		// If delegate, then do not add the delegate in recipients
2452
		if ($isDelegate) {
2453
			$delegate = mapi_getprops($copyFrom, [PR_RECEIVED_BY_EMAIL_ADDRESS]);
0 ignored issues
show
Bug introduced by
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

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

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

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

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

2531
		$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
2532
		$resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
0 ignored issues
show
Bug introduced by
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

2532
		$resourceRecipients = /** @scrutinizer ignore-call */ mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction);
Loading history...
2533
2534
		$this->errorSetResource = false;
2535
		$resourceRecipData = [];
2536
2537
		// Put appointment into store resource users
2538
		$i = 0;
2539
		$len = count($resourceRecipients);
2540
		while (!$this->errorSetResource && $i < $len) {
2541
			$userStore = $this->openCustomUserStore($resourceRecipients[$i][PR_ENTRYID]);
2542
2543
			// Open root folder
2544
			$userRoot = mapi_msgstore_openentry($userStore, null);
0 ignored issues
show
Bug introduced by
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

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

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

2751
					/** @scrutinizer ignore-call */ 
2752
     mapi_setprops($newResourceMsg, $messageprops);
Loading history...
2752
2753
					// Copy attachments
2754
					$this->replaceAttachments($message, $newResourceMsg);
2755
2756
					// Copy all recipients too
2757
					$this->replaceRecipients($message, $newResourceMsg);
2758
2759
					// Now add organizer also to recipient table
2760
					$recips = [];
2761
					$this->addOrganizer($messageprops, $recips);
2762
2763
					mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
0 ignored issues
show
Bug introduced by
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

2763
					/** @scrutinizer ignore-call */ 
2764
     mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips);
Loading history...
2764
				}
2765
2766
				mapi_savechanges($newResourceMsg);
0 ignored issues
show
Bug introduced by
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

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

2786
					$props = /** @scrutinizer ignore-call */ mapi_message_getprops($resourceRecipData[$j]['msg']);
Loading history...
2787
2788
					mapi_folder_deletemessages($resourceRecipData[$j]['folder'], [$props[PR_ENTRYID]], DELETE_HARD_DELETE);
0 ignored issues
show
Bug introduced by
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

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

2837
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2838
2839
		// Copy recipients list
2840
		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
0 ignored issues
show
Bug introduced by
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

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

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

2902
			$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
Loading history...
Bug introduced by
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2903
			mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
Bug introduced by
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

2903
			/** @scrutinizer ignore-call */ 
2904
   mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2904
		}
2905
2906
		mapi_savechanges($recurringItem);
0 ignored issues
show
Bug introduced by
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

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

2924
		$exception_props = /** @scrutinizer ignore-call */ mapi_getprops($occurrenceItem);
Loading history...
2925
2926
		// Get recipient list from message and add it to exception attachment
2927
		$reciptable = mapi_message_getrecipienttable($occurrenceItem);
0 ignored issues
show
Bug introduced by
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

2927
		$reciptable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($occurrenceItem);
Loading history...
2928
		$recips = mapi_table_queryallrows($reciptable, $this->recipprops);
0 ignored issues
show
Bug introduced by
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

2928
		$recips = /** @scrutinizer ignore-call */ mapi_table_queryallrows($reciptable, $this->recipprops);
Loading history...
2929
2930
		if ($recurr->isException($basedate)) {
2931
			$recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem);
2932
		}
2933
		else {
2934
			$recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem);
2935
		}
2936
2937
		// Move the occurrenceItem to the waste basket
2938
		$wastebasket = $this->openDefaultWastebasket($this->openDefaultStore());
2939
		$sourcefolder = mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
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

2939
		$sourcefolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $exception_props[PR_PARENT_ENTRYID]);
Loading history...
Bug introduced by
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2940
		mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
0 ignored issues
show
Bug introduced by
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

2940
		/** @scrutinizer ignore-call */ 
2941
  mapi_folder_copymessages($sourcefolder, [$exception_props[PR_ENTRYID]], $wastebasket, MESSAGE_MOVE);
Loading history...
2941
2942
		mapi_savechanges($recurringItem);
0 ignored issues
show
Bug introduced by
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

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

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

3027
			$recipienttable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3028
			$recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction);
0 ignored issues
show
Bug introduced by
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

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

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

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

3185
			/** @scrutinizer ignore-call */ 
3186
   mapi_savechanges($new);
Loading history...
3186
3187
			// Submit message to non-resource recipients
3188
			mapi_message_submitmessage($new);
0 ignored issues
show
Bug introduced by
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

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

3314
		$outgoing = /** @scrutinizer ignore-call */ mapi_folder_createmessage($outbox);
Loading history...
3315
3316
		// check if $store is set and it is not equal to $defaultStore (means its the delegation case)
3317
		if ($store !== false) {
3318
			$storeProps = mapi_getprops($store, [PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
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

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

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

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

3426
				/** @scrutinizer ignore-call */ 
3427
    mapi_setprops($this->message, [$this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033]);
Loading history...
Bug introduced by
The constant PR_ICON_INDEX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3427
				mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
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

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

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

3509
					$message = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach, MAPI_MODIFY);
Loading history...
3510
				}
3511
			}
3512
		}
3513
		else {
3514
			// use normal message or recurring series message
3515
			$message = $this->message;
3516
		}
3517
3518
		if (!$message) {
3519
			return;
3520
		}
3521
3522
		$newProps = mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
0 ignored issues
show
Bug introduced by
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

3522
		$newProps = /** @scrutinizer ignore-call */ mapi_getprops($message, [$this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter']]);
Loading history...
3523
3524
		// Check whether message is updated or not.
3525
		if (isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) {
3526
			return;
3527
		}
3528
3529
		if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) ||
3530
				($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) ||
3531
				$isRecurrenceChanged) {
3532
			$this->clearRecipientResponse($message);
3533
3534
			mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
0 ignored issues
show
Bug introduced by
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

3534
			/** @scrutinizer ignore-call */ 
3535
   mapi_setprops($message, [$this->proptags['owner_critical_change'] => time()]);
Loading history...
3535
3536
			mapi_savechanges($message);
0 ignored issues
show
Bug introduced by
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

3536
			/** @scrutinizer ignore-call */ 
3537
   mapi_savechanges($message);
Loading history...
3537
			if ($attach) { // Also save attachment Object.
3538
				mapi_savechanges($attach);
3539
			}
3540
		}
3541
	}
3542
3543
	/**
3544
	 * Clear responses of all attendees who have replied in past.
3545
	 *
3546
	 * @param resource $message on which responses should be cleared
3547
	 */
3548
	public function clearRecipientResponse($message): void {
3549
		$recipTable = mapi_message_getrecipienttable($message);
0 ignored issues
show
Bug introduced by
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

3549
		$recipTable = /** @scrutinizer ignore-call */ mapi_message_getrecipienttable($message);
Loading history...
3550
		$recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops);
0 ignored issues
show
Bug introduced by
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

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

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

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

3635
				return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryids[0]);
Loading history...
3636
			}
3637
		}
3638
3639
		// no items found in calendar
3640
		return false;
3641
	}
3642
3643
	/**
3644
	 * Function returns exception item based on the basedate passed.
3645
	 *
3646
	 * @param mixed $recurringMessage Resource of Recurring meeting from calendar
3647
	 * @param mixed $basedate         basedate of exception that needs to be returned
3648
	 * @param mixed $store            store that contains the recurring calendar item
3649
	 *
3650
	 * @return entryid or MAPIMessage resource of exception item
0 ignored issues
show
Bug introduced by
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...
3651
	 */
3652
	public function getExceptionItem($recurringMessage, $basedate, $store = false) {
3653
		$occurItem = false;
3654
3655
		$props = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
0 ignored issues
show
Bug introduced by
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

3655
		$props = /** @scrutinizer ignore-call */ mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID, $this->proptags['recurring']]);
Loading history...
Bug introduced by
The constant PR_RCVD_REPRESENTING_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3656
3657
		// check if the passed item is recurring series
3658
		if (isset($props[$this->proptags['recurring']]) && $props[$this->proptags['recurring']] !== false) {
3659
			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...
3660
		}
3661
3662
		if ($store === false) {
3663
			// If Delegate is processing Meeting Request/Response for Delegator then retrieve Delegator's store and calendar.
3664
			if (isset($props[PR_RCVD_REPRESENTING_ENTRYID])) {
3665
				$delegatorStore = $this->getDelegatorStore($props[PR_RCVD_REPRESENTING_ENTRYID]);
3666
				$store = $delegatorStore['store'];
3667
			}
3668
			else {
3669
				$store = $this->store;
3670
			}
3671
		}
3672
3673
		$recurr = new Recurrence($store, $recurringMessage);
3674
		$attach = $recurr->getExceptionAttachment($basedate);
3675
		if ($attach) {
3676
			$occurItem = mapi_attach_openobj($attach);
0 ignored issues
show
Bug introduced by
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

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

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

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

3922
						$exception = /** @scrutinizer ignore-call */ mapi_attach_openobj($exceptionAttach, 0);
Loading history...
3923
						$exceptionProps = mapi_getprops($exception, [$this->proptags['categories']]);
3924
						if (isset($exceptionProps[$this->proptags['categories']])) {
3925
							$localCategories[$recurrenceItem['basedate']] = $exceptionProps[$this->proptags['categories']];
3926
						}
3927
					}
3928
				}
3929
			}
3930
		}
3931
3932
		return $localCategories;
3933
	}
3934
3935
	/**
3936
	 * Helper function which is use to apply local categories on respective occurrences.
3937
	 *
3938
	 * @param mixed $calendarItem    meeting request item
3939
	 * @param mixed $store           store containing calendar folder
3940
	 * @param array $localCategories array contains basedate and array of categories
3941
	 */
3942
	public function applyLocalCategories($calendarItem, $store, $localCategories): void {
3943
		$calendarItemProps = mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
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

3943
		$calendarItemProps = /** @scrutinizer ignore-call */ mapi_getprops($calendarItem, [PR_PARENT_ENTRYID, PR_ENTRYID]);
Loading history...
3944
		$message = mapi_msgstore_openentry($store, $calendarItemProps[PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
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

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

3956
			/** @scrutinizer ignore-call */ 
3957
   mapi_savechanges($message);
Loading history...
3957
		}
3958
	}
3959
}
3960