1 | <?php |
||||||
2 | |||||||
3 | /** |
||||||
4 | * ItemModule |
||||||
5 | * Module which opens, creates, saves and deletes an item. It |
||||||
6 | * extends the Module class. |
||||||
7 | */ |
||||||
8 | class ItemModule extends Module { |
||||||
9 | /** |
||||||
10 | * The setting whether Meeting Requests should be booked directly or not. |
||||||
11 | */ |
||||||
12 | public $directBookingMeetingRequest; |
||||||
13 | |||||||
14 | /** |
||||||
15 | * The array of properties which should not be copied during the copy() action. |
||||||
16 | */ |
||||||
17 | public $skipCopyProperties; |
||||||
18 | |||||||
19 | /** |
||||||
20 | * Indicates that we are supporting only plain text body in the message props. |
||||||
21 | */ |
||||||
22 | public $plaintext; |
||||||
23 | |||||||
24 | /** |
||||||
25 | * Constructor. |
||||||
26 | * |
||||||
27 | * @param int $id unique id |
||||||
28 | * @param array $data list of all actions |
||||||
29 | */ |
||||||
30 | public function __construct($id, $data) { |
||||||
31 | $this->directBookingMeetingRequest = ENABLE_DIRECT_BOOKING; |
||||||
32 | $this->skipCopyProperties = []; |
||||||
33 | $this->plaintext = false; |
||||||
34 | |||||||
35 | parent::__construct($id, $data); |
||||||
36 | } |
||||||
37 | |||||||
38 | /** |
||||||
39 | * Executes all the actions in the $data variable. |
||||||
40 | */ |
||||||
41 | #[Override] |
||||||
42 | public function execute() { |
||||||
43 | foreach ($this->data as $actionType => $action) { |
||||||
44 | if (!isset($actionType)) { |
||||||
45 | continue; |
||||||
46 | } |
||||||
47 | |||||||
48 | try { |
||||||
49 | $store = $this->getActionStore($action); |
||||||
50 | $parententryid = $this->getActionParentEntryID($action); |
||||||
51 | $entryid = $this->getActionEntryID($action); |
||||||
52 | |||||||
53 | switch ($actionType) { |
||||||
54 | case "open": |
||||||
55 | $this->open($store, $entryid, $action); |
||||||
56 | break; |
||||||
57 | |||||||
58 | case "save": |
||||||
59 | if (!$store || !$parententryid) { |
||||||
60 | /* |
||||||
61 | * if parententryid or storeentryid is not passed then we can take a guess that |
||||||
62 | * it would be a save operation but instead of depending on server to get default |
||||||
63 | * parent and store client should always send parententryid and storeentryid |
||||||
64 | * |
||||||
65 | * we can also assume that user has permission to right in his own store |
||||||
66 | */ |
||||||
67 | $this->save($store, $parententryid, $entryid, $action); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
68 | break; |
||||||
69 | } |
||||||
70 | /* |
||||||
71 | * The "message_action" object has been set, check the action_type field for |
||||||
72 | * the exact action which must be taken. |
||||||
73 | * Supported actions: |
||||||
74 | * - acceptmeetingrequest: attendee has accepted mr |
||||||
75 | * - declineMeetingRequest: attendee has declined mr |
||||||
76 | */ |
||||||
77 | if (!isset($action["message_action"], $action["message_action"]["action_type"])) { |
||||||
78 | $this->save($store, $parententryid, $entryid, $action); |
||||||
79 | break; |
||||||
80 | } |
||||||
81 | |||||||
82 | switch ($action["message_action"]["action_type"]) { |
||||||
83 | case "declineMeetingRequest": |
||||||
84 | case "acceptMeetingRequest": |
||||||
85 | $message = $GLOBALS["operations"]->openMessage($store, $entryid); |
||||||
86 | $basedate = ($action['basedate'] ?? false); |
||||||
87 | $delete = false; |
||||||
88 | |||||||
89 | if ($basedate) { |
||||||
90 | $recurrence = new Recurrence($store, $message); |
||||||
0 ignored issues
–
show
The type
Recurrence 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
91 | $exceptionatt = $recurrence->getExceptionAttachment($basedate); |
||||||
92 | if ($exceptionatt) { |
||||||
93 | // get properties of existing exception. |
||||||
94 | $exceptionattProps = mapi_getprops($exceptionatt, [PR_ATTACH_NUM]); |
||||||
95 | $attach_num = $exceptionattProps[PR_ATTACH_NUM]; |
||||||
96 | } |
||||||
97 | } |
||||||
98 | |||||||
99 | /** |
||||||
100 | * Get message class from original message. This can be changed to |
||||||
101 | * IPM.Appointment if the item is a Meeting Request in the maillist. |
||||||
102 | * After Accepting/Declining the message is moved and changed. |
||||||
103 | */ |
||||||
104 | $originalMessageProps = mapi_getprops($message, [PR_MESSAGE_CLASS]); |
||||||
105 | $req = new Meetingrequest($store, $message, $GLOBALS["mapisession"]->getSession(), $this->directBookingMeetingRequest); |
||||||
0 ignored issues
–
show
The type
Meetingrequest 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
106 | |||||||
107 | // Update extra body information |
||||||
108 | if (isset($action["message_action"]['meetingTimeInfo']) && !empty($action["message_action"]['meetingTimeInfo'])) { |
||||||
109 | $req->setMeetingTimeInfo($action["message_action"]['meetingTimeInfo']); |
||||||
110 | unset($action["message_action"]['meetingTimeInfo']); |
||||||
111 | } |
||||||
112 | |||||||
113 | // sendResponse flag if it is set then send the mail response to the organzer. |
||||||
114 | $sendResponse = true; |
||||||
115 | if (isset($action["message_action"]["sendResponse"]) && $action["message_action"]["sendResponse"] == false) { |
||||||
116 | $sendResponse = false; |
||||||
117 | } |
||||||
118 | |||||||
119 | // @FIXME: fix body |
||||||
120 | $body = false; |
||||||
121 | if (isset($action["props"]["isHTML"]) && $action["props"]["isHTML"] === true) { |
||||||
122 | $body = $action["props"]["html_body"] ?? false; |
||||||
123 | } |
||||||
124 | else { |
||||||
125 | $body = $action["props"]["body"] ?? false; |
||||||
126 | } |
||||||
127 | |||||||
128 | if ($action["message_action"]["action_type"] == "acceptMeetingRequest") { |
||||||
129 | $tentative = $action["message_action"]["responseType"] === olResponseTentative; |
||||||
130 | $newProposedStartTime = $action["message_action"]["proposed_starttime"] ?? false; |
||||||
131 | $newProposedEndTime = $action["message_action"]["proposed_endtime"] ?? false; |
||||||
132 | |||||||
133 | // We are accepting MR from preview-read-mail so set delete the actual mail flag. |
||||||
134 | $delete = $req->isMeetingRequest($originalMessageProps[PR_MESSAGE_CLASS]); |
||||||
135 | |||||||
136 | $req->doAccept($tentative, $sendResponse, $delete, $newProposedStartTime, $newProposedEndTime, $body, true, $store, $basedate); |
||||||
137 | } |
||||||
138 | else { |
||||||
139 | $delete = $req->doDecline($sendResponse, $basedate, $body); |
||||||
140 | } |
||||||
141 | |||||||
142 | /** |
||||||
143 | * Now if the item is the Meeting Request that was sent to the attendee |
||||||
144 | * it is removed when the user has clicked on Accept/Decline. If the |
||||||
145 | * item is the appointment in the calendar it will not be moved. To only |
||||||
146 | * notify the bus when the item is a Meeting Request we are going to |
||||||
147 | * check the PR_MESSAGE_CLASS and see if it is "IPM.Meeting*". |
||||||
148 | */ |
||||||
149 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
150 | |||||||
151 | // if opened appointment is exception then it will add |
||||||
152 | // the attach_num and basedate in messageProps. |
||||||
153 | if (isset($attach_num)) { |
||||||
154 | $messageProps[PR_ATTACH_NUM] = [$attach_num]; |
||||||
155 | $messageProps[$this->properties["basedate"]] = $basedate; |
||||||
156 | } |
||||||
157 | |||||||
158 | if ($delete) { |
||||||
159 | // send TABLE_DELETE event because the message has moved |
||||||
160 | $this->sendFeedback(true); |
||||||
161 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_DELETE, $messageProps); |
||||||
162 | } |
||||||
163 | else { |
||||||
164 | $this->addActionData("update", ["item" => Conversion::mapMAPI2XML($this->properties, $messageProps)]); |
||||||
165 | $GLOBALS["bus"]->addData($this->getResponseData()); |
||||||
166 | |||||||
167 | // send TABLE_SAVE event because an occurrence is deleted |
||||||
168 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_SAVE, $messageProps); |
||||||
169 | } |
||||||
170 | |||||||
171 | break; |
||||||
172 | |||||||
173 | case "acceptTaskRequest": |
||||||
174 | case "declineTaskRequest": |
||||||
175 | $message = $GLOBALS["operations"]->openMessage($store, $entryid); |
||||||
176 | |||||||
177 | if (isset($action["props"]) && !empty($action["props"])) { |
||||||
178 | $properties = $GLOBALS["properties"]->getTaskProperties(); |
||||||
179 | mapi_setprops($message, Conversion::mapXML2MAPI($properties, $action["props"])); |
||||||
180 | mapi_savechanges($message); |
||||||
181 | } |
||||||
182 | // The task may be a delegated task, do an update if needed (will fail for non-delegated tasks) |
||||||
183 | $tr = new TaskRequest($store, $message, $GLOBALS["mapisession"]->getSession()); |
||||||
0 ignored issues
–
show
The type
TaskRequest 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
184 | $isAccept = $action["message_action"]["action_type"] == "acceptTaskRequest"; |
||||||
185 | if (isset($action["message_action"]["task_comments_info"]) && !empty($action["message_action"]["task_comments_info"])) { |
||||||
186 | $tr->setTaskCommentsInfo($action["message_action"]["task_comments_info"]); |
||||||
187 | } |
||||||
188 | if ($isAccept) { |
||||||
189 | $result = $tr->doAccept(); |
||||||
190 | } |
||||||
191 | else { |
||||||
192 | $result = $tr->doDecline(); |
||||||
193 | } |
||||||
194 | |||||||
195 | $this->sendFeedback(true); |
||||||
196 | if ($result !== false) { |
||||||
197 | $GLOBALS["bus"]->notify(bin2hex((string) $result[PR_PARENT_ENTRYID]), TABLE_DELETE, $result); |
||||||
198 | } |
||||||
199 | |||||||
200 | $props = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
201 | if (!$tr->isTaskRequest()) { |
||||||
202 | unset($props[PR_MESSAGE_CLASS]); |
||||||
203 | $GLOBALS["bus"]->notify(bin2hex((string) $props[PR_PARENT_ENTRYID]), $isAccept ? TABLE_SAVE : TABLE_DELETE, $props); |
||||||
204 | } |
||||||
205 | break; |
||||||
206 | |||||||
207 | case "copy": |
||||||
208 | case "move": |
||||||
209 | $this->copy($store, $parententryid, $entryid, $action); |
||||||
0 ignored issues
–
show
$store of type object is incompatible with the type resource expected by parameter $store of ItemModule::copy() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() $parententryid of type object is incompatible with the type string expected by parameter $parententryid of ItemModule::copy() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
210 | break; |
||||||
211 | |||||||
212 | case "reply": |
||||||
213 | case "replyall": |
||||||
214 | case "forward": |
||||||
215 | default: |
||||||
216 | $this->save($store, $parententryid, $entryid, $action); |
||||||
217 | } |
||||||
218 | break; |
||||||
219 | |||||||
220 | case "delete": |
||||||
221 | $subActionType = false; |
||||||
222 | if (isset($action["message_action"], $action["message_action"]["action_type"])) { |
||||||
223 | $subActionType = $action["message_action"]["action_type"]; |
||||||
224 | } |
||||||
225 | |||||||
226 | /* |
||||||
227 | * The "message_action" object has been set, check the action_type field for |
||||||
228 | * the exact action which must be taken. |
||||||
229 | * Supported actions: |
||||||
230 | * - cancelInvitation: organizer cancels already scheduled meeting |
||||||
231 | * - removeFromCalendar: attendee receives meeting cancellation and wants to remove item from calendar |
||||||
232 | */ |
||||||
233 | switch ($subActionType) { |
||||||
234 | case "removeFromCalendar": |
||||||
235 | $basedate = (isset($action['basedate']) && !empty($action['basedate'])) ? $action['basedate'] : false; |
||||||
236 | |||||||
237 | $this->removeFromCalendar($store, $entryid, $basedate, $this->directBookingMeetingRequest); |
||||||
0 ignored issues
–
show
It seems like
$basedate can also be of type false ; however, parameter $basedate of ItemModule::removeFromCalendar() does only seem to accept string , 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
![]() |
|||||||
238 | $this->sendFeedback(true); |
||||||
239 | break; |
||||||
240 | |||||||
241 | case "cancelInvitation": |
||||||
242 | $this->cancelInvitation($store, $entryid, $action, $this->directBookingMeetingRequest); |
||||||
243 | $this->sendFeedback(true); |
||||||
244 | break; |
||||||
245 | |||||||
246 | case "declineMeeting": |
||||||
247 | // @FIXME can we somehow merge declineMeeting and declineMeetingRequest sub actions? |
||||||
248 | $message = $GLOBALS["operations"]->openMessage($store, $entryid); |
||||||
249 | $basedate = (isset($action['basedate']) && !empty($action['basedate'])) ? $action['basedate'] : false; |
||||||
250 | |||||||
251 | $req = new Meetingrequest($store, $message, $GLOBALS["mapisession"]->getSession(), $this->directBookingMeetingRequest); |
||||||
252 | |||||||
253 | // @FIXME: may be we can remove this body check any get it while declining meeting 'body' |
||||||
254 | $body = false; |
||||||
255 | if (isset($action["props"]["isHTML"]) && $action["props"]["isHTML"] === true) { |
||||||
256 | $body = $action["props"]["html_body"] ?? false; |
||||||
257 | } |
||||||
258 | else { |
||||||
259 | $body = $action["props"]["body"] ?? false; |
||||||
260 | } |
||||||
261 | $req->doDecline(true, $basedate, $body); |
||||||
262 | |||||||
263 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
264 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), $basedate ? TABLE_SAVE : TABLE_DELETE, $messageProps); |
||||||
265 | |||||||
266 | break; |
||||||
267 | |||||||
268 | case "snooze": |
||||||
269 | case "dismiss": |
||||||
270 | $this->delete($store, $parententryid, $entryid, $action); |
||||||
0 ignored issues
–
show
$parententryid of type object is incompatible with the type string expected by parameter $parententryid of ItemModule::delete() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
271 | break; |
||||||
272 | |||||||
273 | default: |
||||||
274 | // Deleting an occurrence means that we have to save the message to |
||||||
275 | // generate an exception. So when the basedate is provided, we actually |
||||||
276 | // perform a save rather then delete. |
||||||
277 | if (isset($action['basedate']) && !empty($action['basedate'])) { |
||||||
278 | $this->save($store, $parententryid, $entryid, $action); |
||||||
279 | } |
||||||
280 | else { |
||||||
281 | $this->delete($store, $parententryid, $entryid, $action); |
||||||
282 | } |
||||||
283 | break; |
||||||
284 | } |
||||||
285 | break; |
||||||
286 | |||||||
287 | default: |
||||||
288 | $this->handleUnknownActionType($actionType); |
||||||
289 | } |
||||||
290 | } |
||||||
291 | catch (MAPIException $e) { |
||||||
292 | $this->processException($e, $actionType, $store, $parententryid, $entryid, $action); |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Comprehensibility
Best Practice
introduced
by
|
|||||||
293 | } |
||||||
294 | } |
||||||
295 | } |
||||||
296 | |||||||
297 | /** |
||||||
298 | * Function does customization of exception based on module data. |
||||||
299 | * like, here it will generate display message based on actionType |
||||||
300 | * for particular exception. |
||||||
301 | * |
||||||
302 | * @param object $e Exception object |
||||||
303 | * @param string $actionType the action type, sent by the client |
||||||
304 | * @param MAPIobject $store store object of message |
||||||
0 ignored issues
–
show
The type
MAPIobject 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
305 | * @param string $parententryid parent entryid of the message |
||||||
306 | * @param string $entryid entryid of the message |
||||||
307 | * @param array $action the action data, sent by the client |
||||||
308 | */ |
||||||
309 | #[Override] |
||||||
310 | public function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) { |
||||||
311 | if (is_null($e->displayMessage)) { |
||||||
312 | switch ($actionType) { |
||||||
313 | case "open": |
||||||
314 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
315 | $e->setDisplayMessage(_("You have insufficient privileges to open this message.")); |
||||||
316 | } |
||||||
317 | elseif ($e->getCode() == MAPI_E_NOT_FOUND) { |
||||||
318 | $e->setDisplayMessage(_("Could not find message, either it has been moved or deleted or you don't have access to open this message.")); |
||||||
319 | // Show error in console instead of a pop-up |
||||||
320 | if (isset($action["message_action"]['suppress_exception']) && $action["message_action"]['suppress_exception'] === true) { |
||||||
321 | $e->setNotificationType('console'); |
||||||
322 | } |
||||||
323 | } |
||||||
324 | else { |
||||||
325 | $e->setDisplayMessage(_("Could not open message.")); |
||||||
326 | $e->allowToShowDetailsMessage = true; |
||||||
327 | } |
||||||
328 | break; |
||||||
329 | |||||||
330 | case "save": |
||||||
331 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
332 | if (!empty($action["message_action"]["action_type"])) { |
||||||
333 | switch ($action["message_action"]["action_type"]) { |
||||||
334 | case "declineMeetingRequest": |
||||||
335 | $e->setDisplayMessage(_("You have insufficient privileges to decline this Meeting Request") . "."); |
||||||
336 | break; |
||||||
337 | |||||||
338 | case "acceptMeetingRequest": |
||||||
339 | $e->setDisplayMessage(_("You have insufficient privileges to accept this Meeting Request") . "."); |
||||||
340 | break; |
||||||
341 | |||||||
342 | case "copy": |
||||||
343 | $e->setDisplayMessage(_("Could not copy message") . "."); |
||||||
344 | break; |
||||||
345 | |||||||
346 | case "move": |
||||||
347 | $e->setDisplayMessage(_("Could not move message") . "."); |
||||||
348 | break; |
||||||
349 | } |
||||||
350 | } |
||||||
351 | |||||||
352 | if (empty($e->displayMessage)) { |
||||||
353 | $e->setDisplayMessage(_("You have insufficient privileges to save items in this folder") . "."); |
||||||
354 | } |
||||||
355 | } |
||||||
356 | elseif ($e->getCode() == MAPI_E_STORE_FULL) { |
||||||
357 | $e->setDisplayMessage($this->getOverQuotaMessage($store)); |
||||||
358 | } |
||||||
359 | else { |
||||||
360 | $e->setDisplayMessage(_("Could not save message") . "."); |
||||||
361 | $e->allowToShowDetailsMessage = true; |
||||||
362 | } |
||||||
363 | break; |
||||||
364 | |||||||
365 | case 'delete': |
||||||
366 | switch ($e->getCode()) { |
||||||
367 | case MAPI_E_NO_ACCESS: |
||||||
368 | if (!empty($action['message_action']['action_type'])) { |
||||||
369 | switch ($action['message_action']['action_type']) { |
||||||
370 | case 'removeFromCalendar': |
||||||
371 | $e->setDisplayMessage(_('You have insufficient privileges to remove item from the calendar.')); |
||||||
372 | break; |
||||||
373 | } |
||||||
374 | } |
||||||
375 | break; |
||||||
376 | |||||||
377 | case MAPI_E_NOT_IN_QUEUE: |
||||||
378 | $e->setDisplayMessage(_('Message is no longer in the outgoing queue, typically because it has already been sent.')); |
||||||
379 | break; |
||||||
380 | |||||||
381 | case MAPI_E_UNABLE_TO_ABORT: |
||||||
382 | $e->setDisplayMessage(_('Message cannot be aborted')); |
||||||
383 | break; |
||||||
384 | } |
||||||
385 | if (empty($e->displayMessage)) { |
||||||
386 | $e->setDisplayMessage(_("You have insufficient privileges to delete items in this folder") . "."); |
||||||
387 | } |
||||||
388 | break; |
||||||
389 | |||||||
390 | case "attach_items": |
||||||
391 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
392 | $e->setDisplayMessage(_("You have insufficient privileges to attach item as an attachment.")); |
||||||
393 | } |
||||||
394 | else { |
||||||
395 | $e->setDisplayMessage(_("Could not attach item as an attachment.")); |
||||||
396 | } |
||||||
397 | break; |
||||||
398 | |||||||
399 | case "reclaimownership": |
||||||
400 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
401 | $e->setDisplayMessage(_("You have insufficient privileges to reclaim the ownership for the Task Request.")); |
||||||
402 | } |
||||||
403 | else { |
||||||
404 | $e->setDisplayMessage(_("Could not reclaim the ownership for the Task Request.")); |
||||||
405 | } |
||||||
406 | break; |
||||||
407 | |||||||
408 | case "acceptTaskRequest": |
||||||
409 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
410 | $e->setDisplayMessage(_("You have insufficient privileges to accept this Task Request.")); |
||||||
411 | } |
||||||
412 | else { |
||||||
413 | $e->setDisplayMessage(_("Could not accept Task Request.")); |
||||||
414 | } |
||||||
415 | break; |
||||||
416 | |||||||
417 | case "declineTaskRequest": |
||||||
418 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||||||
419 | $e->setDisplayMessage(_("You have insufficient privileges to decline this Task Request.")); |
||||||
420 | } |
||||||
421 | else { |
||||||
422 | $e->setDisplayMessage(_("Could not decline Task Request.")); |
||||||
423 | } |
||||||
424 | break; |
||||||
425 | } |
||||||
426 | Log::Write( |
||||||
427 | LOGLEVEL_ERROR, |
||||||
428 | "itemmodule::handleException():" . $actionType . ": " . $e->displayMessage |
||||||
429 | ); |
||||||
430 | } |
||||||
431 | |||||||
432 | parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action); |
||||||
433 | } |
||||||
434 | |||||||
435 | /** |
||||||
436 | * Function which opens an item. |
||||||
437 | * |
||||||
438 | * @param object $store MAPI Message Store Object |
||||||
439 | * @param string $entryid entryid of the message |
||||||
440 | * @param array $action the action data, sent by the client |
||||||
441 | */ |
||||||
442 | public function open($store, $entryid, $action) { |
||||||
443 | $data = []; |
||||||
444 | |||||||
445 | if ($entryid) { |
||||||
446 | if ($store) { |
||||||
0 ignored issues
–
show
|
|||||||
447 | $message = $GLOBALS['operations']->openMessage($store, $entryid); |
||||||
448 | } |
||||||
449 | else { |
||||||
450 | // store is not passed so we need to open the message first to get the store resource |
||||||
451 | $message = $GLOBALS['mapisession']->openMessage($entryid); |
||||||
452 | |||||||
453 | $messageStoreInfo = mapi_getprops($message, [PR_STORE_ENTRYID]); |
||||||
454 | $store = $GLOBALS['mapisession']->openMessageStore($messageStoreInfo[PR_STORE_ENTRYID]); |
||||||
455 | } |
||||||
456 | } |
||||||
457 | |||||||
458 | if (empty($message)) { |
||||||
459 | return; |
||||||
460 | } |
||||||
461 | |||||||
462 | // Detect whether S/MIME decoding or meeting request processing is required |
||||||
463 | $props = mapi_getprops($message, [PR_MESSAGE_CLASS]); |
||||||
464 | $messageClass = $props[PR_MESSAGE_CLASS] ?? ''; |
||||||
465 | $requiresSmime = stripos((string) $messageClass, 'SMIME') !== false; |
||||||
466 | $requiresMeeting = stripos((string) $messageClass, 'IPM.Schedule.Meeting') !== false; |
||||||
467 | |||||||
468 | // Decode S/MIME signed messages only when needed |
||||||
469 | if ($requiresSmime) { |
||||||
470 | parse_smime($store, $message); |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
471 | } |
||||||
472 | |||||||
473 | // Open embedded message if requested |
||||||
474 | $attachNum = !empty($action['attach_num']) ? $action['attach_num'] : false; |
||||||
475 | |||||||
476 | if ($attachNum) { |
||||||
477 | // get message props of sub message |
||||||
478 | $parentMessage = $message; |
||||||
479 | $message = $GLOBALS['operations']->openMessage($store, $entryid, $attachNum, true); |
||||||
480 | |||||||
481 | if (empty($message)) { |
||||||
482 | return; |
||||||
483 | } |
||||||
484 | |||||||
485 | $data['item'] = $GLOBALS['operations']->getEmbeddedMessageProps($store, $message, $this->properties, $parentMessage, $attachNum); |
||||||
486 | } |
||||||
487 | else { |
||||||
488 | // get message props of the message |
||||||
489 | $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext, true); |
||||||
490 | $messageClass = $data['item']['props']['message_class'] ?? $messageClass; |
||||||
491 | |||||||
492 | // Determine again if meeting processing is required after parsing |
||||||
493 | if (!$requiresMeeting) { |
||||||
494 | $requiresMeeting = stripos($messageClass, 'IPM.Schedule.Meeting') !== false; |
||||||
495 | } |
||||||
496 | |||||||
497 | // Check for meeting request, do processing if necessary |
||||||
498 | if ($requiresMeeting) { |
||||||
499 | $req = new Meetingrequest($store, $message, $GLOBALS['mapisession']->getSession(), $this->directBookingMeetingRequest); |
||||||
500 | |||||||
501 | try { |
||||||
502 | if ($req->isMeetingRequestResponse($messageClass)) { |
||||||
503 | if ($req->isLocalOrganiser()) { |
||||||
504 | // We received a meeting request response, and we're the delegate/organiser |
||||||
505 | $req->processMeetingRequestResponse(); |
||||||
506 | } |
||||||
507 | } |
||||||
508 | elseif ($req->isMeetingRequest($messageClass)) { |
||||||
509 | if (!$req->isLocalOrganiser()) { |
||||||
510 | if ($req->isMeetingOutOfDate()) { |
||||||
511 | // we know that meeting is out of date so directly set this properties |
||||||
512 | $data['item']['props']['meetingtype'] = mtgOutOfDate; |
||||||
513 | $data['item']['props']['icon_index'] = 1033; |
||||||
514 | |||||||
515 | // send update to maillistmodule that meeting request is updated with out of date flag |
||||||
516 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]); |
||||||
517 | $GLOBALS['bus']->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_SAVE, $messageProps); |
||||||
518 | } |
||||||
519 | else { |
||||||
520 | /* |
||||||
521 | * if meeting request is not out of date then process it for the first time |
||||||
522 | * which will create corresponding appointment in the user's calendar |
||||||
523 | */ |
||||||
524 | $calItemEntryid = $req->doAccept(true, false, false); |
||||||
525 | // we need to set timezone information for all-day events |
||||||
526 | if (isset($data['item']['props']['alldayevent']) && |
||||||
527 | $data['item']['props']['alldayevent'] == true && |
||||||
528 | !empty($action["timezone_iana"]) && |
||||||
529 | is_string($calItemEntryid)) { |
||||||
530 | try { |
||||||
531 | $tzdef = mapi_ianatz_to_tzdef($action['timezone_iana']); |
||||||
532 | $tzdefObj = $GLOBALS['entryid']->createTimezoneDefinitionObject($tzdef); |
||||||
533 | $tzEffRuleIdx = getEffectiveTzreg($tzdefObj['rules']); |
||||||
534 | if (!is_null($tzEffRuleIdx)) { |
||||||
535 | $localStart = $data['item']['props']['appointment_startdate'] + $tzdefObj['rules'][$tzEffRuleIdx]['bias'] * 60; |
||||||
536 | if (isDst($tzdefObj['rules'][$tzEffRuleIdx], $data['item']['props']['appointment_startdate'])) { |
||||||
537 | $localStart += $tzdefObj['rules'][$tzEffRuleIdx]['dstbias'] * 60; |
||||||
538 | } |
||||||
539 | $duration = $data['item']['props']['appointment_duedate'] - $data['item']['props']['appointment_startdate']; |
||||||
540 | |||||||
541 | $calItem = $GLOBALS['mapisession']->openMessage($calItemEntryid); |
||||||
542 | mapi_setprops($calItem, [ |
||||||
543 | $this->properties['appointment_startdate'] => $localStart, |
||||||
544 | $this->properties['appointment_duedate'] => $localStart + $duration, |
||||||
545 | $this->properties['tzdefstart'] => $tzdef, |
||||||
546 | $this->properties['tzdefend'] => $tzdef, |
||||||
547 | ]); |
||||||
548 | mapi_savechanges($calItem); |
||||||
549 | $data['item']['props']['appointment_startdate'] = $localStart; |
||||||
550 | $data['item']['props']['appointment_duedate'] = $localStart + $duration; |
||||||
551 | } |
||||||
552 | } |
||||||
553 | catch (Exception $e) { |
||||||
554 | error_log(sprintf("Error setting timezone to an all-day event: %s", $e)); |
||||||
555 | } |
||||||
556 | } |
||||||
557 | } |
||||||
558 | |||||||
559 | // Show user whether meeting request conflict with other appointment or not. |
||||||
560 | $meetingConflicts = $req->isMeetingConflicting(); |
||||||
561 | |||||||
562 | /** |
||||||
563 | * if $meetingConflicts is boolean and true then its a normal meeting. |
||||||
564 | * if $meetingConflicts is integer then it indicates no of instances of a recurring meeting which conflicts with Calendar. |
||||||
565 | */ |
||||||
566 | if ($meetingConflicts !== false) { |
||||||
567 | if ($meetingConflicts === true) { |
||||||
568 | $data['item']['props']['conflictinfo'] = _('Conflicts with another appointment.'); |
||||||
569 | } |
||||||
570 | else { |
||||||
571 | $data['item']['props']['conflictinfo'] = sprintf(ngettext('%s occurrence of this recurring appointment conflicts with other appointment.', '%s occurrences of this recurring appointment conflicts with other appointments.', $meetingConflicts), $meetingConflicts); |
||||||
572 | } |
||||||
573 | } |
||||||
574 | } |
||||||
575 | } |
||||||
576 | elseif ($req->isMeetingCancellation()) { |
||||||
577 | $req->processMeetingCancellation(); |
||||||
578 | } |
||||||
579 | |||||||
580 | if ($req->isInCalendar()) { |
||||||
581 | $calendarItemProps = $this->getCalendarItemProps($req); |
||||||
582 | if (!empty($calendarItemProps)) { |
||||||
583 | $data['item']['props'] = array_merge($data['item']['props'], $calendarItemProps); |
||||||
584 | } |
||||||
585 | } |
||||||
586 | else { |
||||||
587 | $data['item']['props']['appointment_not_found'] = true; |
||||||
588 | } |
||||||
589 | } |
||||||
590 | catch (MAPIException $e) { |
||||||
591 | // if quota is exceeded or or we don't have permission to write in calendar folder than ignore the exception. |
||||||
592 | if ($e->getCode() !== MAPI_E_STORE_FULL && $e->getCode() !== MAPI_E_NO_ACCESS) { |
||||||
593 | // re-throw the exception if it is not one of quota/calendar permission. |
||||||
594 | throw $e; |
||||||
595 | } |
||||||
596 | } |
||||||
597 | if (!empty($data['item']['props']['appointment_recurring']) && |
||||||
598 | empty($data['item']['props']['appointment_recurring_pattern'])) { |
||||||
599 | $recurr = new Recurrence($store, $message); |
||||||
600 | $data['item']['props']['appointment_recurring_pattern'] = $recurr->saveRecurrencePattern(); |
||||||
601 | } |
||||||
602 | } |
||||||
603 | elseif (stripos($messageClass, 'REPORT.IPM.NOTE.NDR') !== false) { |
||||||
604 | // check if this message is a NDR (mail)message, if so, generate a new body message |
||||||
605 | $data['item']['props']['isHTML'] = false; |
||||||
606 | $data['item']['props']['body'] = $this->getNDRbody($message); |
||||||
607 | } |
||||||
608 | } |
||||||
609 | |||||||
610 | $userEntryId = ''; |
||||||
611 | if (isset($data['item']['props']['sent_representing_entryid'])) { |
||||||
612 | $userEntryId = hex2bin($data['item']['props']['sent_representing_entryid']); |
||||||
613 | } |
||||||
614 | elseif (isset($data['item']['props']['sender_entryid'])) { |
||||||
615 | $userEntryId = hex2bin($data['item']['props']['sender_entryid']); |
||||||
616 | } |
||||||
617 | |||||||
618 | // get user image saved in LDAP. |
||||||
619 | if (!empty($userEntryId)) { |
||||||
620 | $data['item']['props']['user_image'] = $GLOBALS['operations']->getCompressedUserImage($userEntryId); |
||||||
621 | } |
||||||
622 | |||||||
623 | // Allowing to hook in just before the data sent away to be sent to the client |
||||||
624 | $GLOBALS['PluginManager']->triggerHook('server.module.itemmodule.open.after', [ |
||||||
625 | 'moduleObject' => &$this, |
||||||
626 | 'store' => $store, |
||||||
627 | 'entryid' => $entryid, |
||||||
628 | 'action' => $action, |
||||||
629 | 'message' => &$message, |
||||||
630 | 'data' => &$data, |
||||||
631 | ]); |
||||||
632 | |||||||
633 | // ugly workaround to show clip icon for the signed/encrypted emails |
||||||
634 | if (isset($data['item']['props']['smime'])) { |
||||||
635 | $data['item']['props']['hasattach'] = true; |
||||||
636 | unset($data['item']['props']['hide_attachments']); |
||||||
637 | } |
||||||
638 | |||||||
639 | $this->addActionData('item', $data); |
||||||
640 | $GLOBALS['bus']->addData($this->getResponseData()); |
||||||
641 | } |
||||||
642 | |||||||
643 | /** |
||||||
644 | * Function which saves an item. |
||||||
645 | * |
||||||
646 | * @param object $store MAPI Message Store Object |
||||||
647 | * @param string $parententryid parent entryid of the message |
||||||
648 | * @param array $action the action data, sent by the client |
||||||
649 | * @param mixed $entryid |
||||||
650 | */ |
||||||
651 | public function save($store, $parententryid, $entryid, $action) { |
||||||
652 | $result = false; |
||||||
653 | |||||||
654 | if (isset($action["props"])) { |
||||||
655 | if (!$store) { |
||||||
0 ignored issues
–
show
|
|||||||
656 | $store = $GLOBALS['mapisession']->getDefaultMessageStore(); |
||||||
657 | } |
||||||
658 | if (!$parententryid) { |
||||||
659 | if (isset($action['props']['message_class'])) { |
||||||
660 | $parententryid = $this->getDefaultFolderEntryID($store, $action['props']['message_class']); |
||||||
661 | } |
||||||
662 | else { |
||||||
663 | $parententryid = $this->getDefaultFolderEntryID($store, ''); |
||||||
664 | } |
||||||
665 | } |
||||||
666 | |||||||
667 | if ($store && $parententryid) { |
||||||
668 | $props = Conversion::mapXML2MAPI($this->properties, $action["props"]); |
||||||
669 | |||||||
670 | $messageProps = []; // props returned from saveMessage |
||||||
671 | |||||||
672 | // Save message |
||||||
673 | if (!empty($props)) { |
||||||
674 | $result = $GLOBALS["operations"]->saveMessage($store, $entryid, $parententryid, $props, $messageProps, [], !empty($action['attachments']) ? $action['attachments'] : []); |
||||||
675 | } |
||||||
676 | |||||||
677 | if ($result) { |
||||||
678 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_SAVE, $messageProps); |
||||||
679 | |||||||
680 | $this->addActionData("update", ["item" => Conversion::mapMAPI2XML($this->properties, $messageProps)]); |
||||||
681 | $GLOBALS["bus"]->addData($this->getResponseData()); |
||||||
682 | } |
||||||
683 | else { |
||||||
684 | $this->sendFeedback(false); |
||||||
685 | } |
||||||
686 | } |
||||||
687 | } |
||||||
688 | } |
||||||
689 | |||||||
690 | /** |
||||||
691 | * Function which deletes an item. |
||||||
692 | * |
||||||
693 | * @param object $store MAPI Message Store Object |
||||||
694 | * @param string $parententryid parent entryid of the message |
||||||
695 | * @param string $entryid entryid of the message |
||||||
696 | * @param array $action the action data, sent by the client |
||||||
697 | */ |
||||||
698 | public function delete($store, $parententryid, $entryid, $action) { |
||||||
699 | if (!$store || !$parententryid || !$entryid) { |
||||||
0 ignored issues
–
show
|
|||||||
700 | return; |
||||||
701 | } |
||||||
702 | $props = []; |
||||||
703 | $props[PR_PARENT_ENTRYID] = $parententryid; |
||||||
704 | $props[PR_ENTRYID] = $entryid; |
||||||
705 | |||||||
706 | $storeprops = mapi_getprops($store, [PR_ENTRYID]); |
||||||
707 | $props[PR_STORE_ENTRYID] = $storeprops[PR_ENTRYID]; |
||||||
708 | |||||||
709 | $soft = $action['message_action']['soft_delete'] ?? false; |
||||||
710 | $unread = $action['message_action']['non_read_notify'] ?? false; |
||||||
711 | $result = $GLOBALS["operations"]->deleteMessages($store, $parententryid, $entryid, $soft, $unread); |
||||||
712 | if ($result) { |
||||||
713 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $props); |
||||||
714 | $this->sendFeedback(true); |
||||||
715 | } |
||||||
716 | } |
||||||
717 | |||||||
718 | /** |
||||||
719 | * Function which returns the entryid of a default folder. |
||||||
720 | * |
||||||
721 | * @param object $store MAPI Message Store Object |
||||||
722 | * @param string $messageClass the class of the folder |
||||||
723 | * |
||||||
724 | * @return string entryid of a default folder, false if not found |
||||||
725 | */ |
||||||
726 | public function getDefaultFolderEntryID($store, $messageClass) { |
||||||
727 | $entryid = false; |
||||||
728 | |||||||
729 | if ($store) { |
||||||
0 ignored issues
–
show
|
|||||||
730 | $rootcontainer = mapi_msgstore_openentry($store); |
||||||
731 | $rootcontainerprops = mapi_getprops($rootcontainer, [PR_IPM_DRAFTS_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID]); |
||||||
732 | |||||||
733 | switch ($messageClass) { |
||||||
734 | case "IPM.Appointment": |
||||||
735 | if (isset($rootcontainerprops[PR_IPM_APPOINTMENT_ENTRYID])) { |
||||||
736 | $entryid = $rootcontainerprops[PR_IPM_APPOINTMENT_ENTRYID]; |
||||||
737 | } |
||||||
738 | break; |
||||||
739 | |||||||
740 | case "IPM.Contact": |
||||||
741 | case "IPM.DistList": |
||||||
742 | if (isset($rootcontainerprops[PR_IPM_CONTACT_ENTRYID])) { |
||||||
743 | $entryid = $rootcontainerprops[PR_IPM_CONTACT_ENTRYID]; |
||||||
744 | } |
||||||
745 | break; |
||||||
746 | |||||||
747 | case "IPM.StickyNote": |
||||||
748 | if (isset($rootcontainerprops[PR_IPM_NOTE_ENTRYID])) { |
||||||
749 | $entryid = $rootcontainerprops[PR_IPM_NOTE_ENTRYID]; |
||||||
750 | } |
||||||
751 | break; |
||||||
752 | |||||||
753 | case "IPM.Task": |
||||||
754 | if (isset($rootcontainerprops[PR_IPM_TASK_ENTRYID])) { |
||||||
755 | $entryid = $rootcontainerprops[PR_IPM_TASK_ENTRYID]; |
||||||
756 | } |
||||||
757 | break; |
||||||
758 | |||||||
759 | default: |
||||||
760 | if (isset($rootcontainerprops[PR_IPM_DRAFTS_ENTRYID])) { |
||||||
761 | $entryid = $rootcontainerprops[PR_IPM_DRAFTS_ENTRYID]; |
||||||
762 | } |
||||||
763 | break; |
||||||
764 | } |
||||||
765 | } |
||||||
766 | |||||||
767 | return $entryid; |
||||||
768 | } |
||||||
769 | |||||||
770 | /** |
||||||
771 | * Function which copies or moves one or more items. |
||||||
772 | * |
||||||
773 | * @param resource $store MAPI Message Store Object |
||||||
774 | * @param string $parententryid entryid of the folder |
||||||
775 | * @param mixed $entryids list of entryids which will be copied or moved (in binary format) |
||||||
776 | * @param array $action the action data, sent by the client |
||||||
777 | */ |
||||||
778 | public function copy($store, $parententryid, $entryids, $action) { |
||||||
779 | $result = false; |
||||||
0 ignored issues
–
show
|
|||||||
780 | |||||||
781 | if ($store && $parententryid && $entryids) { |
||||||
0 ignored issues
–
show
|
|||||||
782 | $dest_store = $store; |
||||||
783 | if (isset($action["message_action"]["destination_store_entryid"])) { |
||||||
784 | $dest_storeentryid = hex2bin($action["message_action"]["destination_store_entryid"]); |
||||||
785 | $dest_store = $GLOBALS["mapisession"]->openMessageStore($dest_storeentryid); |
||||||
786 | } |
||||||
787 | |||||||
788 | $dest_folderentryid = false; |
||||||
789 | if (isset($action["message_action"]["destination_parent_entryid"])) { |
||||||
790 | $dest_folderentryid = hex2bin($action["message_action"]["destination_parent_entryid"]); |
||||||
791 | } |
||||||
792 | |||||||
793 | $moveMessages = false; |
||||||
794 | if (isset($action["message_action"]["action_type"]) && $action["message_action"]["action_type"] == "move") { |
||||||
795 | $moveMessages = true; |
||||||
796 | } |
||||||
797 | |||||||
798 | // if item has some set of props that need to be saved into the newly copied/moved item |
||||||
799 | $copyProps = []; |
||||||
800 | if (isset($action["message_action"]["dropmodifications"])) { |
||||||
801 | $copyProps = Conversion::mapXML2MAPI($this->properties, $action["message_action"]["dropmodifications"]); |
||||||
802 | } |
||||||
803 | |||||||
804 | // if item has some changes made before choosing different calendar from create-in dropdown |
||||||
805 | if (isset($action["props"]) && !empty($action["props"])) { |
||||||
806 | $copyProps = Conversion::mapXML2MAPI($this->properties, $action["props"]); |
||||||
807 | } |
||||||
808 | |||||||
809 | $props = []; |
||||||
810 | $props[PR_PARENT_ENTRYID] = $parententryid; |
||||||
811 | $props[PR_ENTRYID] = $entryids; |
||||||
812 | |||||||
813 | $storeprops = mapi_getprops($store, [PR_ENTRYID]); |
||||||
814 | $props[PR_STORE_ENTRYID] = $storeprops[PR_ENTRYID]; |
||||||
815 | |||||||
816 | $skipCopyProperties = []; |
||||||
817 | if (isset($action["message_action"]["unset_Private"])) { |
||||||
818 | if ($moveMessages) { |
||||||
819 | array_push($skipCopyProperties, $this->properties["private"], $this->properties["sensitivity"]); |
||||||
820 | } |
||||||
821 | else { |
||||||
822 | array_push($this->skipCopyProperties, $this->properties["private"], $this->properties["sensitivity"]); |
||||||
823 | } |
||||||
824 | } |
||||||
825 | |||||||
826 | $result = $GLOBALS["operations"]->copyMessages($store, $parententryid, $dest_store, $dest_folderentryid, $entryids, $moveMessages ? $skipCopyProperties : $this->skipCopyProperties, $moveMessages, $copyProps); |
||||||
827 | |||||||
828 | if ($result) { |
||||||
829 | if ($moveMessages) { |
||||||
830 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $props); |
||||||
831 | } |
||||||
832 | |||||||
833 | // Delete the PR_ENTRYID, the copied or moved message has a new entryid, |
||||||
834 | // and at this time we have no idea what that might be. So make sure |
||||||
835 | // we unset it, otherwise the notification handlers get weird ideas |
||||||
836 | // and could reset the PR_PARENT_ENTRYID to the old folder again. |
||||||
837 | unset($props[PR_ENTRYID]); |
||||||
838 | $props[PR_PARENT_ENTRYID] = $dest_folderentryid; |
||||||
839 | $props[PR_STORE_ENTRYID] = $dest_storeentryid; |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
840 | $GLOBALS["bus"]->notify(bin2hex($dest_folderentryid), TABLE_SAVE, $props); |
||||||
841 | } |
||||||
842 | |||||||
843 | $this->sendFeedback($result, []); |
||||||
844 | } |
||||||
845 | } |
||||||
846 | |||||||
847 | /** |
||||||
848 | * Function returns correspondent calendar item's properties attached |
||||||
849 | * with the meeting request/response/cancellation. |
||||||
850 | * |
||||||
851 | * @param Meetingrequest $meetingRequestObject the meeting request object |
||||||
852 | * using which function will fetch meeting request properties and return them |
||||||
853 | */ |
||||||
854 | public function getCalendarItemProps($meetingRequestObject) { |
||||||
855 | $calendarItem = $meetingRequestObject->getCorrespondentCalendarItem(); |
||||||
856 | $props = []; |
||||||
857 | if ($calendarItem !== false) { |
||||||
858 | $calendarItemProps = mapi_getprops($calendarItem, [PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $meetingRequestObject->proptags['updatecounter'], $meetingRequestObject->proptags['goid']]); |
||||||
859 | |||||||
860 | // Store calendar item's necessary properties in props array. |
||||||
861 | $props['appointment_store_entryid'] = bin2hex((string) $calendarItemProps[PR_STORE_ENTRYID]); |
||||||
862 | $props['appointment_parent_entryid'] = bin2hex((string) $calendarItemProps[PR_PARENT_ENTRYID]); |
||||||
863 | $props['appointment_entryid'] = bin2hex((string) $calendarItemProps[PR_ENTRYID]); |
||||||
864 | |||||||
865 | $props['appointment_updatecounter'] = $calendarItemProps[$meetingRequestObject->proptags['updatecounter']] ?? 0; |
||||||
866 | |||||||
867 | $messageProps = mapi_getprops($meetingRequestObject->message, [$meetingRequestObject->proptags['goid']]); |
||||||
868 | |||||||
869 | $basedate = $meetingRequestObject->getBasedateFromGlobalID($messageProps[$meetingRequestObject->proptags['goid']]); |
||||||
870 | |||||||
871 | if ($basedate !== false) { |
||||||
872 | $props['appointment_basedate'] = $basedate; |
||||||
873 | |||||||
874 | // if basedate is provided then it is exception, so get update counter of the exception |
||||||
875 | $exception = $meetingRequestObject->getExceptionItem($calendarItem, $basedate); |
||||||
876 | |||||||
877 | if ($exception !== false) { |
||||||
878 | // we are able to find the exception then get updatecounter |
||||||
879 | $exceptionProps = mapi_getprops($exception, [$meetingRequestObject->proptags['updatecounter']]); |
||||||
880 | $props['appointment_updatecounter'] = $exceptionProps[$meetingRequestObject->proptags['updatecounter']] ?? 0; |
||||||
881 | } |
||||||
882 | } |
||||||
883 | |||||||
884 | if ($meetingRequestObject->isMeetingRequestResponse()) { |
||||||
885 | $props['meeting_updated'] = $meetingRequestObject->isMeetingUpdated($basedate); |
||||||
886 | } |
||||||
887 | |||||||
888 | return $props; |
||||||
889 | } |
||||||
890 | |||||||
891 | return false; |
||||||
892 | } |
||||||
893 | |||||||
894 | /** |
||||||
895 | * Get a text body for a Non-Delivery report. |
||||||
896 | * |
||||||
897 | * This function reads the necessary properties from the passed message and constructs |
||||||
898 | * a user-readable NDR message from those properties |
||||||
899 | * |
||||||
900 | * @param mapimessage $message The NDR message to read the information from |
||||||
0 ignored issues
–
show
The type
mapimessage 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
901 | * |
||||||
902 | * @return string NDR body message as plaintext message |
||||||
903 | */ |
||||||
904 | public function getNDRbody($message) { |
||||||
905 | $message_props = mapi_getprops($message, [PR_ORIGINAL_SUBJECT, PR_ORIGINAL_SUBMIT_TIME, PR_BODY]); |
||||||
906 | $body = ''; |
||||||
907 | |||||||
908 | // use PR_BODY if it's there, otherwise create a recipient failed message |
||||||
909 | if (isset($message_props[PR_BODY]) || propIsError(PR_BODY, $message_props) == MAPI_E_NOT_ENOUGH_MEMORY) { |
||||||
910 | $body = mapi_openproperty($message, PR_BODY); |
||||||
911 | } |
||||||
912 | |||||||
913 | if (empty($body)) { |
||||||
914 | $body = _("Your message did not reach some or all of the intended recipients") . "\n\n"; |
||||||
915 | $body .= "\t" . _("Subject") . ": " . $message_props[PR_ORIGINAL_SUBJECT] . "\n"; |
||||||
916 | $body .= "\t" . _("Sent") . ": " . date(DATE_RFC2822, $message_props[PR_ORIGINAL_SUBMIT_TIME]) . "\n\n"; |
||||||
917 | $body .= _("The following recipient(s) could not be reached") . ":\n"; |
||||||
918 | |||||||
919 | $recipienttable = mapi_message_getrecipienttable($message); |
||||||
920 | $recipientrows = mapi_table_queryallrows($recipienttable, [PR_DISPLAY_NAME, PR_REPORT_TIME, PR_REPORT_TEXT]); |
||||||
921 | foreach ($recipientrows as $recipient) { |
||||||
922 | $body .= "\n\t" . $recipient[PR_DISPLAY_NAME] . " on " . date(DATE_RFC2822, $recipient[PR_REPORT_TIME]) . "\n"; |
||||||
923 | $body .= "\t\t" . $recipient[PR_REPORT_TEXT] . "\n"; |
||||||
924 | } |
||||||
925 | } |
||||||
926 | |||||||
927 | return $body; |
||||||
928 | } |
||||||
929 | |||||||
930 | /** |
||||||
931 | * Send a meeting cancellation. |
||||||
932 | * |
||||||
933 | * This function sends a meeting cancellation for the meeting references by the passed entryid. It |
||||||
934 | * will send the meeting cancellation and move the item itself to the waste basket. |
||||||
935 | * |
||||||
936 | * @param mapistore $store The store in which the meeting request resides |
||||||
0 ignored issues
–
show
The type
mapistore 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||||
937 | * @param string $entryid entryid of the appointment for which the cancellation should be sent |
||||||
938 | * @param object $action data sent by client |
||||||
939 | * @param bool $directBookingMeetingRequest Indicates if a Meeting Request should use direct booking or not |
||||||
940 | */ |
||||||
941 | public function cancelInvitation($store, $entryid, $action, $directBookingMeetingRequest) { |
||||||
942 | $message = $GLOBALS['operations']->openMessage($store, $entryid); |
||||||
943 | |||||||
944 | // @TODO move this to meeting request class ? |
||||||
945 | $req = new Meetingrequest($store, $message, $GLOBALS['mapisession']->getSession(), $directBookingMeetingRequest); |
||||||
946 | |||||||
947 | // Update extra body information |
||||||
948 | if (isset($action['message_action']['meetingTimeInfo']) && !empty($action['message_action']['meetingTimeInfo'])) { |
||||||
949 | $req->setMeetingTimeInfo($action["message_action"]['meetingTimeInfo']); |
||||||
950 | unset($action["message_action"]['meetingTimeInfo']); |
||||||
951 | } |
||||||
952 | |||||||
953 | // get basedate from action data and pass to meeting request class |
||||||
954 | $basedate = !empty($action['basedate']) ? $action['basedate'] : false; |
||||||
955 | |||||||
956 | $req->doCancelInvitation($basedate); |
||||||
957 | |||||||
958 | if ($basedate !== false) { |
||||||
959 | // if basedate is specified then we have created exception in recurring meeting request |
||||||
960 | // so send notification of creation of exception |
||||||
961 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
962 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_SAVE, $messageProps); |
||||||
963 | } |
||||||
964 | else { |
||||||
965 | // for normal/recurring meetings send delete notification |
||||||
966 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
967 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_DELETE, $messageProps); |
||||||
968 | } |
||||||
969 | } |
||||||
970 | |||||||
971 | /** |
||||||
972 | * Remove all appointments for a certain meeting request. |
||||||
973 | * |
||||||
974 | * This function searches the default calendar for all meeting requests for the specified |
||||||
975 | * meeting. All those appointments are then removed. |
||||||
976 | * |
||||||
977 | * @param mapistore $store Mapi store in which the meeting request and the calendar reside |
||||||
978 | * @param string $entryid Entryid of the meeting request or appointment for which all items should be deleted |
||||||
979 | * @param string $basedate if specified contains starttime of day of an occurrence |
||||||
980 | * @param bool $directBookingMeetingRequest Indicates if a Meeting Request should use direct booking or not |
||||||
981 | */ |
||||||
982 | public function removeFromCalendar($store, $entryid, $basedate, $directBookingMeetingRequest) { |
||||||
983 | $message = $GLOBALS["operations"]->openMessage($store, $entryid); |
||||||
984 | |||||||
985 | $req = new Meetingrequest($store, $message, $GLOBALS["mapisession"]->getSession(), $directBookingMeetingRequest); |
||||||
986 | |||||||
987 | $req->doRemoveFromCalendar($basedate); |
||||||
988 | |||||||
989 | // Notify the bus that the message has been deleted |
||||||
990 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
991 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), $basedate ? TABLE_SAVE : TABLE_DELETE, $messageProps); |
||||||
992 | } |
||||||
993 | } |
||||||
994 |