Passed
Push — master ( 31a6e6...fe8fd2 )
by
unknown
08:54 queued 06:48
created

TaskRequest   F

Complexity

Total Complexity 143

Size/Duplication

Total Lines 1301
Duplicated Lines 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 641
c 6
b 2
f 0
dl 0
loc 1301
rs 1.959
wmc 143

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 37 1
A isReceivedItem() 0 2 1
A isTaskRequest() 0 11 4
A isTaskRequestResponse() 0 11 4
A updateSentTaskRequest() 0 42 4
F processTaskResponse() 0 123 21
B getAssociatedTask() 0 73 6
B isTaskRequestUpdated() 0 42 7
A getDefaultTasksFolder() 0 10 2
A doAccept() 0 39 5
A createTGOID() 0 7 2
A createOutgoingMessage() 0 27 4
A sendCompleteUpdate() 0 15 3
B doUpdate() 0 24 8
A updateTaskRequest() 0 11 2
A getDefaultStore() 0 11 3
A getSentReprProps() 0 18 2
B setAssignorInRecipients() 0 56 9
B sendResponse() 0 68 8
A deleteReceivedTR() 0 36 3
A getSubProperties() 0 19 1
A setTaskCommentsInfo() 0 2 1
A doDecline() 0 30 4
A getTaskFolderStore() 0 33 6
A setLastUser() 0 25 2
A getTaskCommentsInfo() 0 2 1
A getEmbeddedTask() 0 34 5
B processTaskRequest() 0 49 11
A sendTaskRequest() 0 64 3
A deleteAllRecipients() 0 6 2
A setRecipientsForResponse() 0 50 5
A setOwnerForAssignor() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like TaskRequest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TaskRequest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2005-2016 Zarafa Deutschland GmbH
6
 * SPDX-FileCopyrightText: Copyright 2020-2024 grommunio GmbH
7
 */
8
9
/*
10
* In general
11
*
12
* This class never actually modifies a task item unless we receive a task request update. This means
13
* that setting all the properties to make the task item itself behave like a task request is up to the
14
* caller.
15
*
16
* The only exception to this is the generation of the TaskGlobalObjId, the unique identifier identifying
17
* this task request to both the organizer and the assignee. The globalobjectid is generated when the
18
* task request is sent via sendTaskRequest.
19
*/
20
21
/* The TaskMode value is only used for the IPM.TaskRequest items.
22
 * It must 0 (tdmtNothing) on IPM.Task items.
23
 *
24
 * It is used to indicate the type of change that is being
25
 * carried in the IPM.TaskRequest item (although this information seems
26
 * redundant due to that information already being available in PR_MESSAGE_CLASS).
27
 */
28
define('tdmtNothing', 0);			// Value in IPM.Task items
29
define('tdmtTaskReq', 1);			// Assigner -> Assignee
30
define('tdmtTaskAcc', 2);			// Assignee -> Assigner
31
define('tdmtTaskDec', 3);			// Assignee -> Assigner
32
define('tdmtTaskUpd', 4);			// Assignee -> Assigner
33
define('tdmtTaskSELF', 5);			// Assigner -> Assigner (?)
34
35
/* The TaskHistory is used to show the last action on the task
36
 * on both the assigner and the assignee's side.
37
 *
38
 * It is used in combination with 'task_assigned_time' and 'tasklastdelegate'
39
 * or 'tasklastuser' to show the information at the top of the task request in
40
 * the format 'Accepted by <user> on 01-01-2010 11:00'.
41
 */
42
define('thNone', 0);
43
define('thAccepted', 1);			// Set by assignee
44
define('thDeclined', 2);			// Set by assignee
45
define('thUpdated', 3);				// Set by assignee
46
define('thDueDateChanged', 4);
47
define('thAssigned', 5);			// Set by assigner
48
49
/* The TaskState value is used to differentiate the version of a task
50
 * in the assigner's folder and the version in the
51
 * assignee's folder. The buttons shown depend on this and
52
 * the 'taskaccepted' boolean (for the assignee)
53
 */
54
define('tdsNOM', 0);		// Got a response to a deleted task, and re-created the task for the assigner
55
define('tdsOWNNEW', 1);		// Not assigned
56
define('tdsOWN', 2);		// Assignee version
57
define('tdsACC', 3);		// Assigner version
58
define('tdsDEC', 4);		// Assigner version, but assignee declined
59
60
/* The TaskAcceptanceState is used for the assigner to indicate state */
61
define('olTaskNotDelegated', 0);
62
define('olTaskDelegationUnknown', 1); // After sending req
63
define('olTaskDelegationAccepted', 2); // After receiving accept
64
define('olTaskDelegationDeclined', 3); // After receiving decline
65
66
/* The task ownership indicates the role of the current user relative to the task. */
67
define('olNewTask', 0);
68
define('olDelegatedTask', 1);	// Task has been assigned
69
define('olOwnTask', 2);			// Task owned
70
71
/* taskmultrecips indicates whether the task request sent or received has multiple assignees or not. */
72
define('tmrNone', 0);
73
define('tmrSent', 1);		// Task has been sent to multiple assignee
74
define('tmrReceived', 2);	// Task Request received has multiple assignee
75
76
// Task icon index.
77
define('ICON_TASK_ASSIGNEE', 0x00000502);
78
define('ICON_TASK_DECLINE', 0x00000506);
79
define('ICON_TASK_ASSIGNER', 0x00000503);
80
81
class TaskRequest {
82
	private $props;
83
84
	/**
85
	 * @var string
86
	 */
87
	private $taskCommentsInfo;
88
89
	// All recipient properties
90
	public $recipProps = [
91
		PR_ENTRYID,
92
		PR_DISPLAY_NAME,
93
		PR_EMAIL_ADDRESS,
94
		PR_RECIPIENT_ENTRYID,
95
		PR_RECIPIENT_TYPE,
96
		PR_SEND_INTERNET_ENCODING,
97
		PR_SEND_RICH_INFO,
98
		PR_RECIPIENT_DISPLAY_NAME,
99
		PR_ADDRTYPE,
100
		PR_DISPLAY_TYPE,
101
		PR_RECIPIENT_TRACKSTATUS,
102
		PR_RECIPIENT_TRACKSTATUS_TIME,
103
		PR_RECIPIENT_FLAGS,
104
		PR_ROWID,
105
		PR_SEARCH_KEY,
106
	];
107
108
	/**
109
	 * Constructor.
110
	 *
111
	 * Constructs a TaskRequest object for the specified message. This can be either the task request
112
	 * message itself (in the inbox) or the task in the tasks folder, depending on the action to be performed.
113
	 *
114
	 * As a general rule, the object message passed is the object 'in view' when the user performs one of the
115
	 * actions in this class.
116
	 *
117
	 * @param resource $store   MAPI Store in which $message resides. This is also the store where the tasks folder is assumed to be in
118
	 * @param resource $message MAPI Message to which the task request refers (can be an email or a task)
119
	 * @param resource $session MAPI Session which is used to open tasks folders for delegated task requests or responses
120
	 */
121
	public function __construct(private $store, private $message, private $session) {
122
		$this->taskCommentsInfo = '';
123
124
		$properties = [];
125
		$properties["owner"] = "PT_STRING8:PSETID_Task:0x811f";
126
		$properties["updatecount"] = "PT_LONG:PSETID_Task:0x8112";
127
		$properties["taskstate"] = "PT_LONG:PSETID_Task:0x8113";
128
		$properties["taskmultrecips"] = "PT_LONG:PSETID_Task:0x8120";
129
		$properties["taskupdates"] = "PT_BOOLEAN:PSETID_Task:0x811b";
130
		$properties["tasksoc"] = "PT_BOOLEAN:PSETID_Task:0x8119";
131
		$properties["taskhistory"] = "PT_LONG:PSETID_Task:0x811a";
132
		$properties["taskmode"] = "PT_LONG:PSETID_Common:0x8518";
133
		$properties["task_goid"] = "PT_BINARY:PSETID_Common:0x8519";
134
		$properties["complete"] = "PT_BOOLEAN:PSETID_Common:" . PidLidTaskComplete;
135
		$properties["task_assigned_time"] = "PT_SYSTIME:PSETID_Task:0x8115";
136
		$properties["taskfcreator"] = "PT_BOOLEAN:PSETID_Task:0x0x811e";
137
		$properties["tasklastuser"] = "PT_STRING8:PSETID_Task:0x8122";
138
		$properties["tasklastdelegate"] = "PT_STRING8:PSETID_Task:0x8125";
139
		$properties["taskaccepted"] = "PT_BOOLEAN:PSETID_Task:0x8108";
140
		$properties["task_acceptance_state"] = "PT_LONG:PSETID_Task:0x812a";
141
		$properties["ownership"] = "PT_LONG:PSETID_Task:0x8129";
142
143
		$properties["complete"] = "PT_BOOLEAN:PSETID_Task:" . PidLidTaskComplete;
144
		$properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDateCompleted;
145
		$properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
146
		$properties["startdate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskStartDate;
147
		$properties["duedate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDueDate;
148
		$properties["status"] = "PT_LONG:PSETID_Task:" . PidLidTaskStatus;
149
		$properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:" . PidLidPercentComplete;
150
		$properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
151
		$properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
152
		$properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
153
		$properties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
154
		$properties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
155
		$properties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
156
157
		$this->props = getPropIdsFromStrings($this->store, $properties);
158
	}
159
160
	// General functions
161
162
	/**
163
	 * Returns TRUE if the message pointed to is an incoming task request and should
164
	 * therefore be replied to with doAccept or doDecline().
165
	 *
166
	 * @param mixed $messageClass message class to use for checking
167
	 *
168
	 * @return bool true if this is a task request else false
169
	 */
170
	public function isTaskRequest($messageClass = false) {
171
		if ($messageClass === false) {
172
			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

172
			$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_CLASS]);
Loading history...
173
			$messageClass = $props[PR_MESSAGE_CLASS] ?? false;
174
		}
175
176
		if ($messageClass !== false && $messageClass === "IPM.TaskRequest") {
177
			return true;
178
		}
179
180
		return false;
181
	}
182
183
	/**
184
	 * Returns TRUE if the message pointed to is a returning task request response.
185
	 *
186
	 * @param mixed $messageClass message class to use for checking
187
	 *
188
	 * @return bool true if this is a task request else false
189
	 */
190
	public function isTaskRequestResponse($messageClass = false) {
191
		if ($messageClass === false) {
192
			$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

192
			$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_CLASS]);
Loading history...
193
			$messageClass = $props[PR_MESSAGE_CLASS] ?? false;
194
		}
195
196
		if ($messageClass !== false && str_starts_with((string) $messageClass, "IPM.TaskRequest.")) {
197
			return true;
198
		}
199
200
		return false;
201
	}
202
203
	/**
204
	 * Returns TRUE if the message pointed to is an incoming task request/response.
205
	 *
206
	 * @param array $props The MAPI properties to check message is an incoming task request/response
207
	 *
208
	 * @return bool true if this is an incoming task request/response else false
209
	 */
210
	public function isReceivedItem($props) {
211
		return $props[PR_MESSAGE_TO_ME] ?? false;
212
	}
213
214
	/**
215
	 * Gets the task associated with an IPM.TaskRequest message.
216
	 *
217
	 * If the task does not exist yet, it is created, using the attachment object in the
218
	 * task request item.
219
	 *
220
	 * @param bool $create true - try create task in user's task folder if task does not exist
221
	 *                     false - find the associated task in user's task folder
222
	 *
223
	 * @return bool|resource associated task of task request else false
224
	 */
225
	public function getAssociatedTask($create) {
226
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->props['task_goid']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

226
		$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_CLASS, $this->props['task_goid']]);
Loading history...
227
228
		if ($props[PR_MESSAGE_CLASS] == "IPM.Task") {
229
			// Message itself is task, so return that
230
			return $this->message;
231
		}
232
233
		$taskFolder = $this->getDefaultTasksFolder();
234
		$goid = $props[$this->props['task_goid']];
235
236
		// Find the task by looking for the task_goid
237
		$restriction = [
238
			RES_PROPERTY,
239
			[
240
				RELOP => RELOP_EQ,
241
				ULPROPTAG => $this->props['task_goid'],
242
				VALUE => $goid,
243
			],
244
		];
245
246
		$contents = mapi_folder_getcontentstable($taskFolder);
0 ignored issues
show
Bug introduced by
It seems like $taskFolder can also be of type false; however, parameter $fld of mapi_folder_getcontentstable() 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

246
		$contents = mapi_folder_getcontentstable(/** @scrutinizer ignore-type */ $taskFolder);
Loading history...
247
248
		$rows = mapi_table_queryallrows($contents, [PR_ENTRYID], $restriction);
249
250
		if (empty($rows)) {
251
			// None found, create one if possible
252
			if (!$create) {
253
				return false;
254
			}
255
256
			$task = mapi_folder_createmessage($taskFolder);
0 ignored issues
show
Bug introduced by
It seems like $taskFolder can also be of type false; however, parameter $fld of mapi_folder_createmessage() 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

256
			$task = mapi_folder_createmessage(/** @scrutinizer ignore-type */ $taskFolder);
Loading history...
257
258
			$sub = $this->getEmbeddedTask();
259
			try {
260
				mapi_copyto($sub, [], [$this->props['categories']], $task);
0 ignored issues
show
Bug introduced by
$sub of type boolean|resource is incompatible with the type resource expected by parameter $src of mapi_copyto(). ( Ignorable by Annotation )

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

260
				mapi_copyto(/** @scrutinizer ignore-type */ $sub, [], [$this->props['categories']], $task);
Loading history...
261
			}
262
			catch (MAPIException $e) {
263
				if ($e->getCode() !== MAPI_E_INVALID_PARAMETER) {
264
					throw $e;
265
				}
266
				// Some stores reject excluding categories during copy when attachments are present.
267
				// Retry without exclusions to keep attachments intact and drop categories afterwards.
268
				mapi_copyto($sub, [], [], $task);
269
				mapi_deleteprops($task, [$this->props['categories']]);
270
			}
271
272
			$senderProps = [
273
				PR_SENT_REPRESENTING_NAME,
274
				PR_SENT_REPRESENTING_EMAIL_ADDRESS,
275
				PR_SENT_REPRESENTING_ENTRYID,
276
				PR_SENT_REPRESENTING_ADDRTYPE,
277
				PR_SENT_REPRESENTING_SEARCH_KEY,
278
				PR_SENDER_NAME,
279
				PR_SENDER_EMAIL_ADDRESS,
280
				PR_SENDER_ENTRYID,
281
				PR_SENDER_ADDRTYPE,
282
				PR_SENDER_SEARCH_KEY, ];
283
284
			// Copy sender information from the e-mail
285
			$props = mapi_getprops($this->message, $senderProps);
286
			$props[PR_MESSAGE_CLASS] = 'IPM.Task';
287
			mapi_setprops($task, $props);
288
		}
289
		else {
290
			// If there are multiple, just use the first
291
			$entryid = $rows[0][PR_ENTRYID];
292
293
			$store = $this->getTaskFolderStore();
294
			$task = mapi_msgstore_openentry($store, $entryid);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $store of mapi_msgstore_openentry() 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

294
			$task = mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $store, $entryid);
Loading history...
295
		}
296
297
		return $task;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $task returns the type resource which is incompatible with the documented return type boolean|resource.
Loading history...
298
	}
299
300
	/**
301
	 * Function which checks that if we have received a task request/response
302
	 * for an already updated task in task folder.
303
	 *
304
	 * @return bool true if task request is updated later
305
	 */
306
	public function isTaskRequestUpdated() {
307
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->props['task_goid'], $this->props['updatecount']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

307
		$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_CLASS, $this->props['task_goid'], $this->props['updatecount']]);
Loading history...
308
		$result = false;
309
		$associatedTask = $this->getAssociatedTask(false);
310
		if ($this->isTaskRequest($props[PR_MESSAGE_CLASS])) {
311
			if ($associatedTask) {
0 ignored issues
show
introduced by
$associatedTask is of type resource, thus it always evaluated to false.
Loading history...
312
				return true;
313
			}
314
			$folder = $this->getDefaultTasksFolder();
315
			$goid = $props[$this->props['task_goid']];
316
317
			// Find the task by looking for the task_goid
318
			$restriction = [
319
				RES_PROPERTY,
320
				[
321
					RELOP => RELOP_EQ,
322
					ULPROPTAG => $this->props['task_goid'],
323
					VALUE => $goid,
324
				],
325
			];
326
327
			$table = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS | SHOW_SOFT_DELETES);
0 ignored issues
show
Bug introduced by
It seems like $folder can also be of type false; however, parameter $fld of mapi_folder_getcontentstable() 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

327
			$table = mapi_folder_getcontentstable(/** @scrutinizer ignore-type */ $folder, MAPI_DEFERRED_ERRORS | SHOW_SOFT_DELETES);
Loading history...
328
			$softDeletedItems = mapi_table_queryallrows($table, [PR_ENTRYID], $restriction);
329
			if (!empty($softDeletedItems)) {
330
				return true;
331
			}
332
		}
333
334
		if ($associatedTask !== false) {
0 ignored issues
show
introduced by
The condition $associatedTask !== false is always true.
Loading history...
335
			$taskItemProps = mapi_getprops($associatedTask, [$this->props['updatecount']]);
336
			/*
337
			 * if(message_counter < task_counter) task object is newer then task response (task is updated)
338
			 * if(message_counter >= task_counter) task is not updated, do normal processing
339
			 */
340
			if (isset($taskItemProps[$this->props['updatecount']], $props[$this->props['updatecount']])) {
341
				if ($props[$this->props['updatecount']] < $taskItemProps[$this->props['updatecount']]) {
342
					$result = true;
343
				}
344
			}
345
		}
346
347
		return $result;
348
	}
349
350
	// Organizer functions (called by the organizer)
351
352
	/**
353
	 * Processes a task request response, which can be any of the following:
354
	 * - Task accept (task history is marked as accepted)
355
	 * - Task decline (task history is marked as declined)
356
	 * - Task update (updates completion %, etc).
357
	 *
358
	 * @return true
359
	 */
360
	public function processTaskResponse(): bool {
361
		$messageProps = mapi_getprops($this->message, [PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

361
		$messageProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME]);
Loading history...
362
		if (isset($messageProps[PR_PROCESSED]) && $messageProps[PR_PROCESSED]) {
363
			return true;
364
		}
365
		mapi_setprops($this->message, [PR_PROCESSED => true]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

365
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, [PR_PROCESSED => true]);
Loading history...
366
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

366
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
367
368
		// Get the embedded task information.
369
		$sub = $this->getEmbeddedTask();
370
		// OL saves the task related properties in the embedded message
371
		$subProps = mapi_getprops($sub, [$this->props["taskupdates"]]);
0 ignored issues
show
Bug introduced by
$sub of type boolean|resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

371
		$subProps = mapi_getprops(/** @scrutinizer ignore-type */ $sub, [$this->props["taskupdates"]]);
Loading history...
372
373
		// If task is updated in task folder then we don't need to process
374
		// old response
375
		if ($this->isTaskRequestUpdated()) {
376
			return true;
377
		}
378
379
		$isReceivedItem = $this->isReceivedItem($messageProps);
380
381
		$taskHistory = 0;
382
		$taskState = 0;
383
		$taskAcceptanceState = 0;
384
		$taskOwner = 0;
385
		$isCreateAssociatedTask = false;
386
		$isAllowUpdateAssociatedTask = $subProps[$this->props["taskupdates"]];
387
		$props = mapi_getprops($this->message, [PR_MESSAGE_CLASS]);
388
389
		// Set correct taskmode and taskhistory depending on response type
390
		switch ($props[PR_MESSAGE_CLASS]) {
391
			case 'IPM.TaskRequest.Accept':
392
				$taskHistory = thAccepted;
393
				$taskState = $isReceivedItem ? tdsACC : tdsOWN;
394
				$taskOwner = $isReceivedItem ? olDelegatedTask : olOwnTask;
395
				$taskAcceptanceState = $isReceivedItem ? olTaskDelegationAccepted : olTaskNotDelegated;
396
				break;
397
398
			case 'IPM.TaskRequest.Decline':
399
				$isCreateAssociatedTask = $isReceivedItem;
400
				$isAllowUpdateAssociatedTask = $isReceivedItem;
401
				$taskHistory = thDeclined;
402
				$taskState = $isReceivedItem ? tdsDEC : tdsACC;
403
				$taskOwner = $isReceivedItem ? olOwnTask : olDelegatedTask;
404
				$taskAcceptanceState = $isReceivedItem ? olTaskDelegationDeclined : olTaskDelegationUnknown;
405
				break;
406
407
			case 'IPM.TaskRequest.Update':
408
			case 'IPM.TaskRequest.Complete':
409
				$taskHistory = thUpdated;
410
				$taskState = $isReceivedItem ? tdsACC : tdsOWN;
411
				$taskAcceptanceState = olTaskNotDelegated;
412
				$taskOwner = $isReceivedItem ? olDelegatedTask : olOwnTask;
413
				break;
414
		}
415
416
		$props = [
417
			$this->props['taskhistory'] => $taskHistory,
418
			$this->props['taskstate'] => $taskState,
419
			$this->props['task_acceptance_state'] => $taskAcceptanceState,
420
			$this->props['ownership'] => $taskOwner,
421
		];
422
423
		// Get the task for this response
424
		$task = $this->getAssociatedTask($isCreateAssociatedTask);
425
		if ($task && $isAllowUpdateAssociatedTask) {
0 ignored issues
show
introduced by
$task is of type resource, thus it always evaluated to false.
Loading history...
426
			// To avoid duplication of attachments in associated task. we simple remove the
427
			// all attachments from associated task.
428
			$taskAttachTable = mapi_message_getattachmenttable($task);
429
			$taskAttachments = mapi_table_queryallrows($taskAttachTable, [PR_ATTACH_NUM]);
430
			foreach ($taskAttachments as $taskAttach) {
431
				mapi_message_deleteattach($task, $taskAttach[PR_ATTACH_NUM]);
432
			}
433
434
			$ignoreProps = [
435
				$this->props['taskstate'],
436
				$this->props['taskhistory'],
437
				$this->props['taskmode'],
438
				$this->props['taskfcreator'],
439
			];
440
			// Ignore PR_ICON_INDEX when task request response
441
			// is not received item.
442
			if ($isReceivedItem === false) {
443
				$ignoreProps[] = PR_ICON_INDEX;
444
			}
445
446
			// We copy all properties except taskstate, taskhistory, taskmode and taskfcreator properties
447
			// from $sub message to $task even also we copy all attachments from $sub to $task message.
448
			mapi_copyto($sub, [], $ignoreProps, $task);
449
			$senderProps = mapi_getprops($this->message, [
450
				PR_SENDER_NAME,
451
				PR_SENDER_EMAIL_ADDRESS,
452
				PR_SENDER_ENTRYID,
453
				PR_SENDER_ADDRTYPE,
454
				PR_SENDER_SEARCH_KEY,
455
				PR_MESSAGE_DELIVERY_TIME,
456
				PR_SENT_REPRESENTING_NAME,
457
				PR_SENT_REPRESENTING_EMAIL_ADDRESS,
458
				PR_SENT_REPRESENTING_ADDRTYPE,
459
				PR_SENT_REPRESENTING_ENTRYID,
460
				PR_SENT_REPRESENTING_SEARCH_KEY, ]);
461
462
			mapi_setprops($task, $senderProps);
463
464
			// Update taskstate and task history (last action done by the assignee)
465
			mapi_setprops($task, $props);
466
467
			// Copy missing properties from embedded task
468
			$subProperties = $this->getSubProperties();
469
			$subprops = mapi_getprops($sub, $subProperties);
470
			mapi_setprops($task, $subprops);
471
472
			mapi_savechanges($task);
473
		}
474
475
		mapi_setprops($this->message, $props);
476
		mapi_savechanges($this->message);
477
478
		if ($isReceivedItem) {
479
			$this->updateSentTaskRequest();
480
		}
481
482
		return true;
483
	}
484
485
	/**
486
	 * Update the sent task request in sent items folder.
487
	 *
488
	 * @return bool
489
	 */
490
	public function updateSentTaskRequest() {
491
		$props = mapi_getprops($this->message, [
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

491
		$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [
Loading history...
492
			$this->props['taskhistory'],
493
			$this->props["taskstate"],
494
			$this->props["ownership"],
495
			$this->props['task_goid'],
496
			$this->props['task_acceptance_state'],
497
			$this->props["tasklastuser"],
498
			$this->props["tasklastdelegate"], ]);
499
500
		$store = $this->getDefaultStore();
501
		$storeProps = mapi_getprops($store, [PR_IPM_SENTMAIL_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false; however, parameter $any of mapi_getprops() 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

501
		$storeProps = mapi_getprops(/** @scrutinizer ignore-type */ $store, [PR_IPM_SENTMAIL_ENTRYID]);
Loading history...
502
503
		$sentFolder = mapi_msgstore_openentry($store, $storeProps[PR_IPM_SENTMAIL_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false; however, parameter $store of mapi_msgstore_openentry() 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

503
		$sentFolder = mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $store, $storeProps[PR_IPM_SENTMAIL_ENTRYID]);
Loading history...
504
		if (!$sentFolder) {
0 ignored issues
show
introduced by
$sentFolder is of type resource, thus it always evaluated to true.
Loading history...
505
			return false;
506
		}
507
508
		// Find the task by looking for the task_goid
509
		$restriction = [
510
			RES_PROPERTY,
511
			[
512
				RELOP => RELOP_EQ,
513
				ULPROPTAG => $this->props['task_goid'],
514
				VALUE => $props[$this->props['task_goid']],
515
			],
516
		];
517
518
		$contentsTable = mapi_folder_getcontentstable($sentFolder);
519
520
		$rows = mapi_table_queryallrows($contentsTable, [PR_ENTRYID], $restriction);
521
522
		if (!empty($rows)) {
523
			foreach ($rows as $row) {
524
				$sentTaskRequest = mapi_msgstore_openentry($store, $row[PR_ENTRYID]);
525
				mapi_setprops($sentTaskRequest, $props);
526
				mapi_setprops($sentTaskRequest, [PR_PROCESSED => true]);
527
				mapi_savechanges($sentTaskRequest);
528
			}
529
		}
530
531
		return true;
532
	}
533
534
	/**
535
	 * Creates a new message in the current user's outbox and submits it.
536
	 *
537
	 * Takes the task passed in the constructor as the task to be sent; recipient should
538
	 * be pre-existing. The task request will be sent to all recipients.
539
	 *
540
	 * @param string $prefix
541
	 *
542
	 * @return true
543
	 */
544
	public function sendTaskRequest($prefix): bool {
545
		// Check if the task is in a public folder (which is not mail-enabled)
546
		$taskFolderStore = $this->getTaskFolderStore();
547
		$storeType = mapi_getprops($taskFolderStore, [PR_MDB_PROVIDER]);
0 ignored issues
show
Bug introduced by
It seems like $taskFolderStore can also be of type false and resource; however, parameter $any of mapi_getprops() 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

547
		$storeType = mapi_getprops(/** @scrutinizer ignore-type */ $taskFolderStore, [PR_MDB_PROVIDER]);
Loading history...
548
		if (isset($storeType[PR_MDB_PROVIDER]) && $storeType[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
549
			throw new MAPIException(_("Cannot assign tasks in public folders. Public folders are not mail-enabled and cannot send task assignment notifications."), MAPI_E_NO_SUPPORT);
550
		}
551
552
		// Generate a TaskGlobalObjectId
553
		$taskid = $this->createTGOID();
554
		$messageprops = mapi_getprops($this->message, [PR_SUBJECT]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

554
		$messageprops = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_SUBJECT]);
Loading history...
555
556
		// Set properties on Task Request
557
		mapi_setprops($this->message, [
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

557
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, [
Loading history...
558
			$this->props['task_goid'] => $taskid, // our new task_goid
559
			$this->props['taskstate'] => tdsACC, // state for our outgoing request
560
			$this->props['taskmode'] => tdmtNothing, // we're not sending a change
561
			$this->props['updatecount'] => 2, // version 2 (no idea)
562
			$this->props['task_acceptance_state'] => olTaskDelegationUnknown, // no reply yet
563
			$this->props['ownership'] => olDelegatedTask, // Task has been assigned
564
			$this->props['taskhistory'] => thAssigned, // Task has been assigned
565
			PR_CONVERSATION_TOPIC => $messageprops[PR_SUBJECT],
566
			PR_ICON_INDEX => ICON_TASK_ASSIGNER,
567
		]);
568
		$this->setLastUser();
569
		$this->setOwnerForAssignor();
570
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

570
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
571
572
		// Create outgoing task request message
573
		$outgoing = $this->createOutgoingMessage();
574
575
		// No need to copy PR_ICON_INDEX and  PR_SENT_* information in to outgoing message.
576
		$ignoreProps = [PR_ICON_INDEX, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY];
577
		mapi_copyto($this->message, [], $ignoreProps, $outgoing);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $src of mapi_copyto(). ( Ignorable by Annotation )

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

577
		mapi_copyto(/** @scrutinizer ignore-type */ $this->message, [], $ignoreProps, $outgoing);
Loading history...
578
579
		// Make it a task request, and put it in sent items after it is sent
580
		mapi_setprops($outgoing, [
581
			PR_MESSAGE_CLASS => "IPM.TaskRequest", // class is task request
582
			$this->props['taskstate'] => tdsOWN, // for the recipient he is the task owner
583
			$this->props['taskmode'] => tdmtTaskReq, // for the recipient it's a request
584
			$this->props['updatecount'] => 1, // version 2 is in the attachment
585
			PR_SUBJECT_PREFIX => $prefix,
586
			PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT],
587
		]);
588
589
		$attach = mapi_message_createattach($outgoing);
590
		mapi_setprops($attach, [
591
			PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG,
592
			PR_ATTACHMENT_HIDDEN => true,
593
			PR_DISPLAY_NAME => $messageprops[PR_SUBJECT], ]);
594
595
		$sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE);
0 ignored issues
show
Bug introduced by
The function mapi_attach_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

595
		$sub = /** @scrutinizer ignore-call */ mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE);
Loading history...
596
597
		mapi_copyto($this->message, [], [], $sub);
598
		mapi_setprops($sub, [PR_MESSAGE_CLASS => 'IPM.Task']);
599
600
		mapi_savechanges($sub);
601
602
		mapi_savechanges($attach);
603
604
		mapi_savechanges($outgoing);
605
		mapi_message_submitmessage($outgoing);
606
607
		return true;
608
	}
609
610
	// Assignee functions (called by the assignee)
611
612
	/**
613
	 * Updates task version counter.
614
	 *
615
	 * Must be called before each update to increase counter.
616
	 */
617
	public function updateTaskRequest(): void {
618
		$messageprops = mapi_getprops($this->message, [$this->props['updatecount']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

618
		$messageprops = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['updatecount']]);
Loading history...
619
620
		if (isset($messageprops)) {
621
			++$messageprops[$this->props['updatecount']];
622
		}
623
		else {
624
			$messageprops[$this->props['updatecount']] = 1;
625
		}
626
627
		mapi_setprops($this->message, $messageprops);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

627
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, $messageprops);
Loading history...
628
	}
629
630
	/**
631
	 * Processes a task request.
632
	 *
633
	 * Message passed should be an IPM.TaskRequest message. The task request is then processed to create
634
	 * the task in the tasks folder if needed.
635
	 *
636
	 * @return bool
637
	 */
638
	public function processTaskRequest() {
639
		if (!$this->isTaskRequest()) {
640
			return false;
641
		}
642
		$messageProps = mapi_getprops($this->message, [PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

642
		$messageProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_PROCESSED, $this->props["taskupdates"], PR_MESSAGE_TO_ME]);
Loading history...
643
		if (isset($messageProps[PR_PROCESSED]) && $messageProps[PR_PROCESSED]) {
644
			return true;
645
		}
646
647
		// if task is updated in task folder then we don't need to process
648
		// old request.
649
		if ($this->isTaskRequestUpdated()) {
650
			return true;
651
		}
652
653
		$isReceivedItem = $this->isReceivedItem($messageProps);
654
655
		$props = [];
656
		$props[PR_PROCESSED] = true;
657
		$props[$this->props["taskstate"]] = $isReceivedItem ? tdsOWN : tdsACC;
658
		$props[$this->props["ownership"]] = $isReceivedItem ? olOwnTask : olDelegatedTask;
659
660
		mapi_setprops($this->message, $props);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

660
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, $props);
Loading history...
661
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

661
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
662
663
		// Don't create associated task in task folder if "taskupdates" is not true.
664
		if (!$isReceivedItem && !$messageProps[$this->props["taskupdates"]]) {
665
			return true;
666
		}
667
		// create an associated task in task folder while
668
		// reading/loading task request on client side.
669
		$task = $this->getAssociatedTask(true);
670
671
		$taskProps = mapi_getprops($task, [$this->props['taskmultrecips']]);
672
		$taskProps[$this->props["taskstate"]] = $isReceivedItem ? tdsOWN : tdsACC;
673
		$taskProps[$this->props["taskhistory"]] = thAssigned;
674
		$taskProps[$this->props["taskmode"]] = tdmtNothing;
675
		$taskProps[$this->props["taskaccepted"]] = false;
676
		$taskProps[$this->props["taskfcreator"]] = false;
677
		$taskProps[$this->props["ownership"]] = $isReceivedItem ? olOwnTask : olDelegatedTask;
678
		$taskProps[$this->props["task_acceptance_state"]] = olTaskNotDelegated;
679
		$taskProps[PR_ICON_INDEX] = ICON_TASK_ASSIGNEE;
680
681
		mapi_setprops($task, $taskProps);
682
		$this->setAssignorInRecipients($task);
683
684
		mapi_savechanges($task);
685
686
		return true;
687
	}
688
689
	/**
690
	 * Accepts a task request and sends the response.
691
	 *
692
	 * Message passed should be an IPM.Task (eg the task from getAssociatedTask())
693
	 *
694
	 * Copies the task to the user's task folder, sets it to accepted, and sends the acceptation
695
	 * message back to the organizer. The caller is responsible for removing the message.
696
	 *
697
	 * @return array|false PR_ENTRYID, PR_STORE_ENTRYID and PR_PARENT_ENTRYID of the task
698
	 */
699
	public function doAccept() {
700
		$prefix = _("Task Accepted:") . " ";
701
		$messageProps = mapi_getprops($this->message, [PR_MESSAGE_CLASS, $this->props['taskstate']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

701
		$messageProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_CLASS, $this->props['taskstate']]);
Loading history...
702
703
		if (!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
704
			// Can only accept assignee task
705
			return false;
706
		}
707
708
		$this->setLastUser();
709
		$this->updateTaskRequest();
710
711
		$props = [
712
			$this->props['taskhistory'] => thAccepted,
713
			$this->props['task_assigned_time'] => time(),
714
			$this->props['taskaccepted'] => true,
715
			$this->props['task_acceptance_state'] => olTaskNotDelegated, ];
716
717
		// Message is TaskRequest then update the associated task as well.
718
		if ($this->isTaskRequest($messageProps[PR_MESSAGE_CLASS])) {
719
			$task = $this->getAssociatedTask(false);
720
			if ($task) {
0 ignored issues
show
introduced by
$task is of type resource, thus it always evaluated to false.
Loading history...
721
				mapi_setprops($task, $props);
722
				mapi_savechanges($task);
723
			}
724
		}
725
726
		// Set as accepted
727
		mapi_setprops($this->message, $props);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

727
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, $props);
Loading history...
728
729
		// As we copy the all properties from received message we need to remove following
730
		// properties from accept response.
731
		mapi_deleteprops($this->message, [PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_deleteprops(). ( Ignorable by Annotation )

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

731
		mapi_deleteprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED]);
Loading history...
732
733
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

733
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
734
735
		$this->sendResponse(tdmtTaskAcc, $prefix);
736
737
		return $this->deleteReceivedTR();
738
	}
739
740
	/**
741
	 * Declines a task request and sends the response.
742
	 *
743
	 * Passed message must be a task request message, ie isTaskRequest() must return TRUE.
744
	 *
745
	 * Sends the decline message back to the organizer. The caller is responsible for removing the message.
746
	 *
747
	 * @return array|false TRUE on success, FALSE on failure
748
	 */
749
	public function doDecline() {
750
		$prefix = _("Task Declined:") . " ";
751
		$messageProps = mapi_getprops($this->message, [$this->props['taskstate']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

751
		$messageProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['taskstate']]);
Loading history...
752
753
		if (!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
754
			return false; // Can only decline assignee task
755
		}
756
757
		$this->setLastUser();
758
		$this->updateTaskRequest();
759
760
		// Set as declined
761
		mapi_setprops($this->message, [
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

761
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, [
Loading history...
762
			$this->props['taskhistory'] => thDeclined,
763
			$this->props['task_acceptance_state'] => olTaskDelegationDeclined,
764
		]);
765
		mapi_deleteprops($this->message, [PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_deleteprops(). ( Ignorable by Annotation )

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

765
		mapi_deleteprops(/** @scrutinizer ignore-type */ $this->message, [PR_MESSAGE_RECIP_ME, PR_MESSAGE_TO_ME, PR_MESSAGE_CC_ME, PR_PROCESSED]);
Loading history...
766
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

766
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
767
768
		$this->sendResponse(tdmtTaskDec, $prefix);
769
770
		// Delete the associated task when task request is declined by the assignee.
771
		$task = $this->getAssociatedTask(false);
772
		if ($task) {
0 ignored issues
show
introduced by
$task is of type resource, thus it always evaluated to false.
Loading history...
773
			$taskFolder = $this->getDefaultTasksFolder();
774
			$props = mapi_getprops($task, [PR_ENTRYID]);
775
			mapi_folder_deletemessages($taskFolder, [$props[PR_ENTRYID]]);
776
		}
777
778
		return $this->deleteReceivedTR();
779
	}
780
781
	/**
782
	 * Sends an update of the task if requested, and sends the Status-On-Completion report if complete and requested.
783
	 *
784
	 * If no updates were requested from the organizer, this function does nothing.
785
	 *
786
	 * @return bool TRUE if the update succeeded, FALSE otherwise
787
	 */
788
	public function doUpdate() {
789
		$messageProps = mapi_getprops($this->message, [$this->props['taskstate'], PR_SUBJECT]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

789
		$messageProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['taskstate'], PR_SUBJECT]);
Loading history...
790
791
		if (!isset($messageProps[$this->props['taskstate']]) || $messageProps[$this->props['taskstate']] != tdsOWN) {
792
			return false; // Can only update assignee task
793
		}
794
795
		$this->setLastUser();
796
		$this->updateTaskRequest();
797
798
		// Set as updated
799
		mapi_setprops($this->message, [$this->props['taskhistory'] => thUpdated]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

799
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['taskhistory'] => thUpdated]);
Loading history...
800
801
		mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

801
		mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
802
803
		$props = mapi_getprops($this->message, [$this->props['taskupdates'], $this->props['tasksoc'], $this->props['recurring'], $this->props['complete']]);
804
		if (!$props[$this->props['complete']] && $props[$this->props['taskupdates']] && !(isset($props[$this->props['recurring']]) && $props[$this->props['recurring']])) {
805
			$this->sendResponse(tdmtTaskUpd, _("Task Updated:") . " ");
806
		}
807
		elseif ($props[$this->props['complete']]) {
808
			$this->sendResponse(tdmtTaskUpd, _("Task Completed:") . " ");
809
		}
810
811
		return true;
812
	}
813
814
	/**
815
	 * Gets the store associated with the task.
816
	 *
817
	 * Normally this will just open the store that the processed message is in. However, if the message is opened
818
	 * by a delegate, this function opens the store that the message was delegated from.
819
	 */
820
	public function getTaskFolderStore() {
821
		$ownerentryid = false;
822
823
		$rcvdprops = mapi_getprops($this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

823
		$rcvdprops = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_RCVD_REPRESENTING_ENTRYID]);
Loading history...
824
		if (isset($rcvdprops[PR_RCVD_REPRESENTING_ENTRYID])) {
825
			$ownerentryid = $rcvdprops[PR_RCVD_REPRESENTING_ENTRYID];
826
		}
827
828
		if (!$ownerentryid) {
829
			$store = $this->store;
830
		}
831
		else {
832
			$ab = mapi_openaddressbook($this->session);
0 ignored issues
show
Bug introduced by
$this->session of type resource is incompatible with the type resource expected by parameter $session of mapi_openaddressbook(). ( Ignorable by Annotation )

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

832
			$ab = mapi_openaddressbook(/** @scrutinizer ignore-type */ $this->session);
Loading history...
833
			if (!$ab) {
0 ignored issues
show
introduced by
$ab is of type resource, thus it always evaluated to true.
Loading history...
834
				return false;
835
			}
836
837
			$mailuser = mapi_ab_openentry($ab, $ownerentryid);
838
			if (!$mailuser) {
0 ignored issues
show
introduced by
$mailuser is of type resource, thus it always evaluated to true.
Loading history...
839
				return false;
840
			}
841
842
			$mailuserprops = mapi_getprops($mailuser, [PR_EMAIL_ADDRESS]);
843
			if (!isset($mailuserprops[PR_EMAIL_ADDRESS])) {
844
				return false;
845
			}
846
847
			$storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
0 ignored issues
show
Bug introduced by
$this->store of type resource is incompatible with the type resource expected by parameter $store of mapi_msgstore_createentryid(). ( Ignorable by Annotation )

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

847
			$storeid = mapi_msgstore_createentryid(/** @scrutinizer ignore-type */ $this->store, $mailuserprops[PR_EMAIL_ADDRESS]);
Loading history...
848
849
			$store = mapi_openmsgstore($this->session, $storeid);
0 ignored issues
show
Bug introduced by
$this->session of type resource is incompatible with the type resource expected by parameter $ses of mapi_openmsgstore(). ( Ignorable by Annotation )

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

849
			$store = mapi_openmsgstore(/** @scrutinizer ignore-type */ $this->session, $storeid);
Loading history...
850
		}
851
852
		return $store;
853
	}
854
855
	/**
856
	 * Opens the default task folder for the current user, or the specified user if passed.
857
	 */
858
	public function getDefaultTasksFolder() {
859
		$store = $this->getTaskFolderStore();
860
861
		$inbox = mapi_msgstore_getreceivefolder($store);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $store of mapi_msgstore_getreceivefolder() 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

861
		$inbox = mapi_msgstore_getreceivefolder(/** @scrutinizer ignore-type */ $store);
Loading history...
862
		$inboxprops = mapi_getprops($inbox, [PR_IPM_TASK_ENTRYID]);
863
		if (!isset($inboxprops[PR_IPM_TASK_ENTRYID])) {
864
			return false;
865
		}
866
867
		return mapi_msgstore_openentry($store, $inboxprops[PR_IPM_TASK_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $store of mapi_msgstore_openentry() 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

867
		return mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $store, $inboxprops[PR_IPM_TASK_ENTRYID]);
Loading history...
868
	}
869
870
	/**
871
	 * Prepares the sent representing properties from given MAPI store.
872
	 *
873
	 * @param mixed $store MAPI store object
874
	 *
875
	 * @return array[][][][][]|false if store is not mail box owner entryid then return false else prepare the sent representing props and return it
876
	 *
877
	 * @psalm-return array<array<array<array<array<array<never, never>>>>>>|false
878
	 */
879
	public function getSentReprProps($store) {
880
		$storeprops = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]);
881
		if (!isset($storeprops[PR_MAILBOX_OWNER_ENTRYID])) {
882
			return false;
883
		}
884
885
		$ab = mapi_openaddressbook($this->session);
0 ignored issues
show
Bug introduced by
$this->session of type resource is incompatible with the type resource expected by parameter $session of mapi_openaddressbook(). ( Ignorable by Annotation )

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

885
		$ab = mapi_openaddressbook(/** @scrutinizer ignore-type */ $this->session);
Loading history...
886
		$mailuser = mapi_ab_openentry($ab, $storeprops[PR_MAILBOX_OWNER_ENTRYID]);
887
		$mailuserprops = mapi_getprops($mailuser, [PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_DISPLAY_NAME, PR_SEARCH_KEY, PR_ENTRYID]);
888
889
		$props = [];
890
		$props[PR_SENT_REPRESENTING_ADDRTYPE] = $mailuserprops[PR_ADDRTYPE];
891
		$props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $mailuserprops[PR_EMAIL_ADDRESS];
892
		$props[PR_SENT_REPRESENTING_NAME] = $mailuserprops[PR_DISPLAY_NAME];
893
		$props[PR_SENT_REPRESENTING_SEARCH_KEY] = $mailuserprops[PR_SEARCH_KEY];
894
		$props[PR_SENT_REPRESENTING_ENTRYID] = $mailuserprops[PR_ENTRYID];
895
896
		return $props;
897
	}
898
899
	/**
900
	 * Creates an outgoing message based on the passed message - will set delegate information
901
	 * and sent mail folder.
902
	 */
903
	public function createOutgoingMessage() {
904
		// Open our default store for this user (that's the only store we can submit in)
905
		$store = $this->getDefaultStore();
906
		$storeprops = mapi_getprops($store, [PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false; however, parameter $any of mapi_getprops() 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

906
		$storeprops = mapi_getprops(/** @scrutinizer ignore-type */ $store, [PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID]);
Loading history...
907
908
		$outbox = mapi_msgstore_openentry($store, $storeprops[PR_IPM_OUTBOX_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false; however, parameter $store of mapi_msgstore_openentry() 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

908
		$outbox = mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $store, $storeprops[PR_IPM_OUTBOX_ENTRYID]);
Loading history...
909
		if (!$outbox) {
0 ignored issues
show
introduced by
$outbox is of type resource, thus it always evaluated to true.
Loading history...
910
			return false;
911
		}
912
913
		$outgoing = mapi_folder_createmessage($outbox);
914
		if (!$outgoing) {
0 ignored issues
show
introduced by
$outgoing is of type resource, thus it always evaluated to true.
Loading history...
915
			return false;
916
		}
917
918
		// Set SENT_REPRESENTING in case we're sending as a delegate
919
		$ownerstore = $this->getTaskFolderStore();
920
		$sentreprprops = $this->getSentReprProps($ownerstore);
921
922
		// Check if getSentReprProps returned false (e.g., for public folders which are not mail-enabled)
923
		if ($sentreprprops !== false) {
0 ignored issues
show
introduced by
The condition $sentreprprops !== false is always false.
Loading history...
924
			mapi_setprops($outgoing, $sentreprprops);
925
		}
926
927
		mapi_setprops($outgoing, [PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID]]);
928
929
		return $outgoing;
930
	}
931
932
	/**
933
	 * Sends a response message (from assignee back to organizer).
934
	 *
935
	 * @param int   $type   Type of response (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd)
936
	 * @param mixed $prefix
937
	 *
938
	 * @return bool TRUE on success
939
	 */
940
	public function sendResponse($type, $prefix) {
941
		// Create a message in our outbox
942
		$outgoing = $this->createOutgoingMessage();
943
		$messageprops = mapi_getprops($this->message, [PR_CONVERSATION_TOPIC, PR_MESSAGE_CLASS, $this->props['complete']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

943
		$messageprops = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [PR_CONVERSATION_TOPIC, PR_MESSAGE_CLASS, $this->props['complete']]);
Loading history...
944
945
		$attach = mapi_message_createattach($outgoing);
946
		mapi_setprops($attach, [PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_CONVERSATION_TOPIC], PR_ATTACHMENT_HIDDEN => true]);
947
		$sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY);
0 ignored issues
show
Bug introduced by
The function mapi_attach_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

947
		$sub = /** @scrutinizer ignore-call */ mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY);
Loading history...
948
949
		$message = !$this->isTaskRequest() ? $this->message : $this->getAssociatedTask(false);
950
951
		$ignoreProps = [PR_ICON_INDEX, $this->props["categories"], PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY];
952
953
		mapi_copyto($message, [], $ignoreProps, $outgoing);
0 ignored issues
show
Bug introduced by
$message of type resource is incompatible with the type resource expected by parameter $src of mapi_copyto(). ( Ignorable by Annotation )

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

953
		mapi_copyto(/** @scrutinizer ignore-type */ $message, [], $ignoreProps, $outgoing);
Loading history...
954
		mapi_copyto($message, [], [], $sub);
955
956
		if (!$this->setRecipientsForResponse($outgoing, $type)) {
957
			return false;
958
		}
959
960
		$props = [];
961
962
		switch ($type) {
963
			case tdmtTaskAcc:
964
				$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Accept";
965
				mapi_setprops($sub, [PR_ICON_INDEX => ICON_TASK_ASSIGNER]);
966
				break;
967
968
			case tdmtTaskDec:
969
				$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Decline";
970
				mapi_setprops($sub, [PR_ICON_INDEX => ICON_TASK_DECLINE]);
971
				break;
972
973
			case tdmtTaskUpd:
974
				mapi_setprops($sub, [PR_ICON_INDEX => ICON_TASK_ASSIGNER]);
975
				if ($messageprops[$this->props['complete']]) {
976
					$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Complete";
977
				}
978
				else {
979
					$props[PR_MESSAGE_CLASS] = "IPM.TaskRequest.Update";
980
				}
981
982
				break;
983
		}
984
985
		mapi_savechanges($sub);
986
		mapi_savechanges($attach);
987
988
		$props[PR_SUBJECT] = $prefix . $messageprops[PR_CONVERSATION_TOPIC];
989
		$props[$this->props['taskmode']] = $type;
990
		$props[$this->props['task_assigned_time']] = time();
991
992
		mapi_setprops($outgoing, $props);
993
994
		// taskCommentsInfo contains some comments which added by assignee while
995
		// edit response before sending task response.
996
		if ($this->taskCommentsInfo != '') {
997
			$comments = $this->getTaskCommentsInfo();
998
			$stream = mapi_openproperty($outgoing, PR_BODY, IID_IStream, STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY);
999
			mapi_stream_setsize($stream, strlen($comments));
1000
			mapi_stream_write($stream, $comments);
1001
			mapi_stream_commit($stream);
1002
		}
1003
1004
		mapi_savechanges($outgoing);
1005
		mapi_message_submitmessage($outgoing);
1006
1007
		return true;
1008
	}
1009
1010
	public function getDefaultStore() {
1011
		$table = mapi_getmsgstorestable($this->session);
0 ignored issues
show
Bug introduced by
$this->session of type resource is incompatible with the type resource expected by parameter $session of mapi_getmsgstorestable(). ( Ignorable by Annotation )

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

1011
		$table = mapi_getmsgstorestable(/** @scrutinizer ignore-type */ $this->session);
Loading history...
1012
		$rows = mapi_table_queryallrows($table, [PR_DEFAULT_STORE, PR_ENTRYID]);
1013
1014
		foreach ($rows as $row) {
1015
			if ($row[PR_DEFAULT_STORE]) {
1016
				return mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
$this->session of type resource is incompatible with the type resource expected by parameter $ses of mapi_openmsgstore(). ( Ignorable by Annotation )

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

1016
				return mapi_openmsgstore(/** @scrutinizer ignore-type */ $this->session, $row[PR_ENTRYID]);
Loading history...
1017
			}
1018
		}
1019
1020
		return false;
1021
	}
1022
1023
	/**
1024
	 * Creates a new TaskGlobalObjId.
1025
	 *
1026
	 * Just 16 bytes of random data
1027
	 */
1028
	public function createTGOID(): string {
1029
		$goid = "";
1030
		for ($i = 0; $i < 16; ++$i) {
1031
			$goid .= chr(random_int(0, 255));
1032
		}
1033
1034
		return $goid;
1035
	}
1036
1037
	/**
1038
	 * Gets the embedded task of task request. Further used to
1039
	 * create/update associated task of assigner/assignee.
1040
	 *
1041
	 * @return bool|resource embedded task if found else false
1042
	 */
1043
	public function getEmbeddedTask() {
1044
		$task = false;
1045
		$goid = mapi_getprops($this->message, [$this->props["task_goid"]]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

1045
		$goid = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props["task_goid"]]);
Loading history...
1046
		$attachmentTable = mapi_message_getattachmenttable($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $msg of mapi_message_getattachmenttable(). ( Ignorable by Annotation )

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

1046
		$attachmentTable = mapi_message_getattachmenttable(/** @scrutinizer ignore-type */ $this->message);
Loading history...
1047
		$restriction = [RES_PROPERTY,
1048
			[RELOP => RELOP_EQ,
1049
				ULPROPTAG => PR_ATTACH_METHOD,
1050
				VALUE => ATTACH_EMBEDDED_MSG, ],
1051
		];
1052
		$rows = mapi_table_queryallrows($attachmentTable, [PR_ATTACH_NUM], $restriction);
1053
1054
		if (empty($rows)) {
1055
			return $task;
1056
		}
1057
1058
		foreach ($rows as $row) {
1059
			try {
1060
				$attach = mapi_message_openattach($this->message, $row[PR_ATTACH_NUM]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $msg of mapi_message_openattach(). ( Ignorable by Annotation )

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

1060
				$attach = mapi_message_openattach(/** @scrutinizer ignore-type */ $this->message, $row[PR_ATTACH_NUM]);
Loading history...
1061
				$task = mapi_attach_openobj($attach);
1062
			}
1063
			catch (MAPIException) {
1064
				continue;
1065
			}
1066
1067
			$taskGoid = mapi_getprops($task, [$this->props["task_goid"]]);
1068
			if ($goid[$this->props["task_goid"]] === $taskGoid[$this->props["task_goid"]]) {
1069
				mapi_setprops($attach, [PR_ATTACHMENT_HIDDEN => true]);
1070
				mapi_savechanges($attach);
1071
				mapi_savechanges($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

1071
				mapi_savechanges(/** @scrutinizer ignore-type */ $this->message);
Loading history...
1072
				break;
1073
			}
1074
		}
1075
1076
		return $task;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $task also could return the type resource which is incompatible with the documented return type boolean|resource.
Loading history...
1077
	}
1078
1079
	/**
1080
	 * Sets the user name who has last used this task. Update the
1081
	 * tasklastdelegate and task_assigned_time.
1082
	 */
1083
	public function setLastUser(): void {
1084
		$delegatestore = $this->getDefaultStore();
1085
		$taskstore = $this->getTaskFolderStore();
1086
1087
		$delegateprops = mapi_getprops($delegatestore, [PR_MAILBOX_OWNER_NAME]);
0 ignored issues
show
Bug introduced by
It seems like $delegatestore can also be of type false; however, parameter $any of mapi_getprops() 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

1087
		$delegateprops = mapi_getprops(/** @scrutinizer ignore-type */ $delegatestore, [PR_MAILBOX_OWNER_NAME]);
Loading history...
1088
		$taskprops = mapi_getprops($taskstore, [PR_MAILBOX_OWNER_NAME]);
0 ignored issues
show
Bug introduced by
It seems like $taskstore can also be of type false and resource; however, parameter $any of mapi_getprops() 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

1088
		$taskprops = mapi_getprops(/** @scrutinizer ignore-type */ $taskstore, [PR_MAILBOX_OWNER_NAME]);
Loading history...
1089
1090
		// The owner of the task
1091
		$username = $delegateprops[PR_MAILBOX_OWNER_NAME];
1092
		// This is me (the one calling the script)
1093
		$delegate = $taskprops[PR_MAILBOX_OWNER_NAME];
1094
1095
		if ($this->isTaskRequest()) {
1096
			$task = $this->getAssociatedTask(false);
1097
			mapi_setprops($task, [
0 ignored issues
show
Bug introduced by
$task of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

1097
			mapi_setprops(/** @scrutinizer ignore-type */ $task, [
Loading history...
1098
				$this->props["tasklastuser"] => $username,
1099
				$this->props["tasklastdelegate"] => $delegate,
1100
				$this->props['task_assigned_time'] => time(),
1101
			]);
1102
			mapi_savechanges($task);
0 ignored issues
show
Bug introduced by
$task of type resource is incompatible with the type resource expected by parameter $any of mapi_savechanges(). ( Ignorable by Annotation )

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

1102
			mapi_savechanges(/** @scrutinizer ignore-type */ $task);
Loading history...
1103
		}
1104
		mapi_setprops($this->message, [
1105
			$this->props["tasklastuser"] => $username,
1106
			$this->props["tasklastdelegate"] => $delegate,
1107
			$this->props['task_assigned_time'] => time(),
1108
		]);
1109
	}
1110
1111
	/**
1112
	 * Sets assignee as owner in the assignor's copy of task.
1113
	 * Assignee becomes the owner when a user/assignor assigns any task to someone.
1114
	 * There can be more than one assignee.
1115
	 */
1116
	public function setOwnerForAssignor(): void {
1117
		$recipTable = mapi_message_getrecipienttable($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $msg of mapi_message_getrecipienttable(). ( Ignorable by Annotation )

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

1117
		$recipTable = mapi_message_getrecipienttable(/** @scrutinizer ignore-type */ $this->message);
Loading history...
1118
		$recips = mapi_table_queryallrows($recipTable, [PR_DISPLAY_NAME]);
1119
1120
		if (!empty($recips)) {
1121
			$owner = [];
1122
			foreach ($recips as $value) {
1123
				$owner[] = $value[PR_DISPLAY_NAME];
1124
			}
1125
1126
			$props = [$this->props['owner'] => implode("; ", $owner)];
1127
			mapi_setprops($this->message, $props);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

1127
			mapi_setprops(/** @scrutinizer ignore-type */ $this->message, $props);
Loading history...
1128
		}
1129
	}
1130
1131
	/**
1132
	 * Sets assignor as recipients in assignee's copy of task.
1133
	 *
1134
	 * If assignor has requested task updates then the assignor is added as recipient type MAPI_CC.
1135
	 *
1136
	 * Also if assignor has request SOC then the assignor is also add as recipient type MAPI_BCC
1137
	 *
1138
	 * @param mixed $task assignee's copy of task
1139
	 */
1140
	public function setAssignorInRecipients($task): void {
1141
		$recipTable = mapi_message_getrecipienttable($task);
1142
1143
		// Delete all MAPI_TO recipients
1144
		$recips = mapi_table_queryallrows($recipTable, [PR_ROWID], [
1145
			RES_PROPERTY,
1146
			[
1147
				RELOP => RELOP_EQ,
1148
				ULPROPTAG => PR_RECIPIENT_TYPE,
1149
				VALUE => MAPI_TO,
1150
			],
1151
		]);
1152
		foreach ($recips as $recip) {
1153
			mapi_message_modifyrecipients($task, MODRECIP_REMOVE, [$recip]);
1154
		}
1155
1156
		$recips = [];
1157
		$taskReqProps = mapi_getprops($this->message, [
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

1157
		$taskReqProps = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [
Loading history...
1158
			PR_SENT_REPRESENTING_NAME,
1159
			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
1160
			PR_SENT_REPRESENTING_ENTRYID,
1161
			PR_SENT_REPRESENTING_ADDRTYPE,
1162
			PR_SENT_REPRESENTING_SEARCH_KEY,
1163
		]);
1164
		$associatedTaskProps = mapi_getprops($task, [
1165
			$this->props['taskupdates'],
1166
			$this->props['tasksoc'],
1167
			$this->props['taskmultrecips'],
1168
		]);
1169
1170
		// Build assignor info
1171
		$assignor = [
1172
			PR_ENTRYID => $taskReqProps[PR_SENT_REPRESENTING_ENTRYID],
1173
			PR_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
1174
			PR_EMAIL_ADDRESS => $taskReqProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
1175
			PR_RECIPIENT_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME],
1176
			PR_ADDRTYPE => empty($taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE],
1177
			PR_RECIPIENT_FLAGS => recipSendable,
1178
			PR_SEARCH_KEY => $taskReqProps[PR_SENT_REPRESENTING_SEARCH_KEY],
1179
		];
1180
1181
		// Assignor has requested task updates, so set him/her as MAPI_CC in recipienttable.
1182
		if ((isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['taskupdates']]) &&
1183
			!(isset($associatedTaskProps[$this->props['taskmultrecips']]) && $associatedTaskProps[$this->props['taskmultrecips']] == tmrReceived)) {
1184
			$assignor[PR_RECIPIENT_TYPE] = MAPI_CC;
1185
			$recips[] = $assignor;
1186
		}
1187
1188
		// Assignor wants to receive an email report when task is mark as 'Complete', so in recipients as MAPI_BCC
1189
		if ($associatedTaskProps[$this->props['tasksoc']]) {
1190
			$assignor[PR_RECIPIENT_TYPE] = MAPI_BCC;
1191
			$recips[] = $assignor;
1192
		}
1193
1194
		if (!empty($recips)) {
1195
			mapi_message_modifyrecipients($task, MODRECIP_ADD, $recips);
1196
		}
1197
	}
1198
1199
	/**
1200
	 * Deletes incoming task request from Inbox.
1201
	 *
1202
	 * @returns array|bool PR_ENTRYID, PR_STORE_ENTRYID and PR_PARENT_ENTRYID of the deleted task request
1203
	 *
1204
	 * @return array|false
1205
	 */
1206
	public function deleteReceivedTR() {
1207
		$store = $this->getTaskFolderStore();
1208
		$storeType = mapi_getprops($store, [PR_MDB_PROVIDER]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $any of mapi_getprops() 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

1208
		$storeType = mapi_getprops(/** @scrutinizer ignore-type */ $store, [PR_MDB_PROVIDER]);
Loading history...
1209
		if ($storeType[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID) {
1210
			$store = $this->getDefaultStore();
1211
		}
1212
		$inbox = mapi_msgstore_getreceivefolder($store);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $store of mapi_msgstore_getreceivefolder() 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

1212
		$inbox = mapi_msgstore_getreceivefolder(/** @scrutinizer ignore-type */ $store);
Loading history...
1213
1214
		$storeProps = mapi_getprops($store, [PR_IPM_WASTEBASKET_ENTRYID]);
1215
		$props = mapi_getprops($this->message, [$this->props['task_goid']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

1215
		$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['task_goid']]);
Loading history...
1216
		$goid = $props[$this->props['task_goid']];
1217
1218
		// Find the task by looking for the task_goid
1219
		$restriction = [
1220
			RES_PROPERTY,
1221
			[
1222
				RELOP => RELOP_EQ,
1223
				ULPROPTAG => $this->props['task_goid'],
1224
				VALUE => $goid,
1225
			],
1226
		];
1227
1228
		$contents = mapi_folder_getcontentstable($inbox);
1229
1230
		$rows = mapi_table_queryallrows($contents, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID], $restriction);
1231
1232
		if (!empty($rows)) {
1233
			// If there are multiple, just use the first
1234
			$entryid = $rows[0][PR_ENTRYID];
1235
			$wastebasket = mapi_msgstore_openentry($store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]);
0 ignored issues
show
Bug introduced by
It seems like $store can also be of type false and resource; however, parameter $store of mapi_msgstore_openentry() 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

1235
			$wastebasket = mapi_msgstore_openentry(/** @scrutinizer ignore-type */ $store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]);
Loading history...
1236
			mapi_folder_copymessages($inbox, [$entryid], $wastebasket, MESSAGE_MOVE);
1237
1238
			return [PR_ENTRYID => $entryid, PR_PARENT_ENTRYID => $rows[0][PR_PARENT_ENTRYID], PR_STORE_ENTRYID => $rows[0][PR_STORE_ENTRYID]];
1239
		}
1240
1241
		return false;
1242
	}
1243
1244
	/**
1245
	 * Sets recipients for the outgoing message according to type of the response.
1246
	 *
1247
	 * If it is a task update, then only recipient type MAPI_CC are taken from the task message.
1248
	 *
1249
	 * If it is accept/decline response, then PR_SENT_REPRESENTATING_XXXX are taken as recipient.
1250
	 *
1251
	 * @param mixed $outgoing     outgoing mapi message
1252
	 * @param int   $responseType response type (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd)
1253
	 */
1254
	public function setRecipientsForResponse($outgoing, $responseType): bool {
1255
		// Clear recipients from outgoing msg
1256
		$this->deleteAllRecipients($outgoing);
1257
1258
		// If it is a task update then get MAPI_CC recipients which are assignors who has asked for task update.
1259
		if ($responseType == tdmtTaskUpd) {
1260
			$props = mapi_getprops($this->message, [$this->props['complete']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

1260
			$props = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['complete']]);
Loading history...
1261
			$isComplete = $props[$this->props['complete']];
1262
1263
			$recipTable = mapi_message_getrecipienttable($this->message);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $msg of mapi_message_getrecipienttable(). ( Ignorable by Annotation )

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

1263
			$recipTable = mapi_message_getrecipienttable(/** @scrutinizer ignore-type */ $this->message);
Loading history...
1264
			$recips = mapi_table_queryallrows($recipTable, $this->recipProps, [
1265
				RES_PROPERTY,
1266
				[
1267
					RELOP => RELOP_EQ,
1268
					ULPROPTAG => PR_RECIPIENT_TYPE,
1269
					VALUE => ($isComplete ? MAPI_BCC : MAPI_CC),
1270
				],
1271
			]);
1272
1273
			// No recipients found, return error
1274
			if (empty($recips)) {
1275
				return false;
1276
			}
1277
1278
			foreach ($recips as $recip) {
1279
				$recip[PR_RECIPIENT_TYPE] = MAPI_TO;	// Change recipient type to MAPI_TO
1280
				mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, [$recip]);
1281
			}
1282
1283
			return true;
1284
		}
1285
1286
		$orgprops = mapi_getprops($this->message, [
1287
			PR_SENT_REPRESENTING_NAME,
1288
			PR_SENT_REPRESENTING_EMAIL_ADDRESS,
1289
			PR_SENT_REPRESENTING_ADDRTYPE,
1290
			PR_SENT_REPRESENTING_ENTRYID,
1291
			PR_SUBJECT,
1292
		]);
1293
1294
		$recip = [
1295
			PR_DISPLAY_NAME => $orgprops[PR_SENT_REPRESENTING_NAME],
1296
			PR_EMAIL_ADDRESS => $orgprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS],
1297
			PR_ADDRTYPE => $orgprops[PR_SENT_REPRESENTING_ADDRTYPE],
1298
			PR_ENTRYID => $orgprops[PR_SENT_REPRESENTING_ENTRYID],
1299
			PR_RECIPIENT_TYPE => MAPI_TO, ];
1300
1301
		mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, [$recip]);
1302
1303
		return true;
1304
	}
1305
1306
	/**
1307
	 * Deletes all recipients from given message object.
1308
	 *
1309
	 * @param mixed $message MAPI message from which recipients are to be removed
1310
	 */
1311
	public function deleteAllRecipients($message): void {
1312
		$recipTable = mapi_message_getrecipienttable($message);
1313
		$recipRows = mapi_table_queryallrows($recipTable, [PR_ROWID]);
1314
1315
		foreach ($recipRows as $recipient) {
1316
			mapi_message_modifyrecipients($message, MODRECIP_REMOVE, [$recipient]);
1317
		}
1318
	}
1319
1320
	/**
1321
	 * Marks the record to complete and send complete update
1322
	 * notification to assigner.
1323
	 *
1324
	 * @return bool TRUE if the update succeeded, FALSE otherwise
1325
	 */
1326
	public function sendCompleteUpdate() {
1327
		$messageprops = mapi_getprops($this->message, [$this->props['taskstate']]);
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_getprops(). ( Ignorable by Annotation )

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

1327
		$messageprops = mapi_getprops(/** @scrutinizer ignore-type */ $this->message, [$this->props['taskstate']]);
Loading history...
1328
1329
		if (!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) {
1330
			return false; // Can only decline assignee task
1331
		}
1332
1333
		mapi_setprops($this->message, [
0 ignored issues
show
Bug introduced by
$this->message of type resource is incompatible with the type resource expected by parameter $any of mapi_setprops(). ( Ignorable by Annotation )

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

1333
		mapi_setprops(/** @scrutinizer ignore-type */ $this->message, [
Loading history...
1334
			$this->props['complete'] => true,
1335
			$this->props['datecompleted'] => time(),
1336
			$this->props['status'] => 2,
1337
			$this->props['percent_complete'] => 1,
1338
		]);
1339
1340
		return $this->doUpdate();
1341
	}
1342
1343
	/**
1344
	 * Returns extra info about task request comments along with message body
1345
	 * which will be included in body while sending task request/response.
1346
	 *
1347
	 * @return string info about task request comments along with message body
1348
	 */
1349
	public function getTaskCommentsInfo() {
1350
		return $this->taskCommentsInfo;
1351
	}
1352
1353
	/**
1354
	 * Sets an extra info about task request comments along with message body
1355
	 * which will be included in body while sending task request/response.
1356
	 *
1357
	 * @param string $taskCommentsInfo info about task request comments along with message body
1358
	 */
1359
	public function setTaskCommentsInfo($taskCommentsInfo): void {
1360
		$this->taskCommentsInfo = $taskCommentsInfo;
1361
	}
1362
1363
	public function getSubProperties() {
1364
		$subProperties = [];
1365
		$subProperties["subject"] = PR_SUBJECT;
1366
		$subProperties["convtopic"] = PR_CONVERSATION_TOPIC;
1367
		$subProperties["complete"] = "PT_BOOLEAN:PSETID_Task:" . PidLidTaskComplete;
1368
		$subProperties["datecompleted"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDateCompleted;
1369
		$subProperties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126";
1370
		$subProperties["startdate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskStartDate;
1371
		$subProperties["duedate"] = "PT_SYSTIME:PSETID_Task:" . PidLidTaskDueDate;
1372
		$subProperties["status"] = "PT_LONG:PSETID_Task:" . PidLidTaskStatus;
1373
		$subProperties["percent_complete"] = "PT_DOUBLE:PSETID_Task:" . PidLidPercentComplete;
1374
		$subProperties["totalwork"] = "PT_LONG:PSETID_Task:0x8111";
1375
		$subProperties["actualwork"] = "PT_LONG:PSETID_Task:0x8110";
1376
		$subProperties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords";
1377
		$subProperties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539";
1378
		$subProperties["mileage"] = "PT_STRING8:PSETID_Common:0x8534";
1379
		$subProperties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535";
1380
1381
		return getPropIdsFromStrings($this->store, $subProperties);
1382
	}
1383
}
1384