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, "delete"); |
||||||
0 ignored issues
–
show
The call to
ItemModule::save() has too many arguments starting with 'delete' .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
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 | // Decode smime signed messages on this message |
||||||
463 | parse_smime($store, $message); |
||||||
464 | |||||||
465 | // Open embedded message if requested |
||||||
466 | $attachNum = !empty($action['attach_num']) ? $action['attach_num'] : false; |
||||||
467 | if ($attachNum) { |
||||||
468 | // get message props of sub message |
||||||
469 | $parentMessage = $message; |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
470 | $message = $GLOBALS['operations']->openMessage($store, $entryid, $attachNum, true); |
||||||
471 | |||||||
472 | if (empty($message)) { |
||||||
473 | return; |
||||||
474 | } |
||||||
475 | |||||||
476 | $data['item'] = $GLOBALS['operations']->getEmbeddedMessageProps($store, $message, $this->properties, $parentMessage, $attachNum); |
||||||
477 | } |
||||||
478 | else { |
||||||
479 | // get message props of the message |
||||||
480 | $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext); |
||||||
481 | $messageClass = $data['item']['props']['message_class'] ?? ''; |
||||||
482 | |||||||
483 | // Check for meeting request, do processing if necessary |
||||||
484 | if (stripos($messageClass, 'IPM.Schedule.Meeting') !== false) { |
||||||
485 | $req = new Meetingrequest($store, $message, $GLOBALS['mapisession']->getSession(), $this->directBookingMeetingRequest); |
||||||
486 | |||||||
487 | try { |
||||||
488 | if ($req->isMeetingRequestResponse($messageClass)) { |
||||||
489 | if ($req->isLocalOrganiser()) { |
||||||
490 | // We received a meeting request response, and we're the delegate/organiser |
||||||
491 | $req->processMeetingRequestResponse(); |
||||||
492 | } |
||||||
493 | } |
||||||
494 | elseif ($req->isMeetingRequest($messageClass)) { |
||||||
495 | if (!$req->isLocalOrganiser()) { |
||||||
496 | if ($req->isMeetingOutOfDate()) { |
||||||
497 | // we know that meeting is out of date so directly set this properties |
||||||
498 | $data['item']['props']['meetingtype'] = mtgOutOfDate; |
||||||
499 | $data['item']['props']['icon_index'] = 1033; |
||||||
500 | |||||||
501 | // send update to maillistmodule that meeting request is updated with out of date flag |
||||||
502 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID]); |
||||||
503 | $GLOBALS['bus']->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_SAVE, $messageProps); |
||||||
504 | } |
||||||
505 | else { |
||||||
506 | /* |
||||||
507 | * if meeting request is not out of date then process it for the first time |
||||||
508 | * which will create corresponding appointment in the user's calendar |
||||||
509 | */ |
||||||
510 | $calItemEntryid = $req->doAccept(true, false, false); |
||||||
511 | // we need to set timezone information for all-day events |
||||||
512 | if (isset($data['item']['props']['alldayevent']) && |
||||||
513 | $data['item']['props']['alldayevent'] == true && |
||||||
514 | !empty($action["timezone_iana"]) && |
||||||
515 | is_string($calItemEntryid)) { |
||||||
516 | try { |
||||||
517 | $tzdef = mapi_ianatz_to_tzdef($action['timezone_iana']); |
||||||
518 | $tzdefObj = $GLOBALS['entryid']->createTimezoneDefinitionObject($tzdef); |
||||||
519 | $tzEffRuleIdx = getEffectiveTzreg($tzdefObj['rules']); |
||||||
520 | if (!is_null($tzEffRuleIdx)) { |
||||||
521 | $localStart = $data['item']['props']['appointment_startdate'] + $tzdefObj['rules'][$tzEffRuleIdx]['bias'] * 60; |
||||||
522 | if (isDst($tzdefObj['rules'][$tzEffRuleIdx], $data['item']['props']['appointment_startdate'])) { |
||||||
523 | $localStart += $tzdefObj['rules'][$tzEffRuleIdx]['dstbias'] * 60; |
||||||
524 | } |
||||||
525 | $duration = $data['item']['props']['appointment_duedate'] - $data['item']['props']['appointment_startdate']; |
||||||
526 | |||||||
527 | $calItem = $GLOBALS['mapisession']->openMessage($calItemEntryid); |
||||||
528 | mapi_setprops($calItem, [ |
||||||
529 | $this->properties['appointment_startdate'] => $localStart, |
||||||
530 | $this->properties['appointment_duedate'] => $localStart + $duration, |
||||||
531 | $this->properties['tzdefstart'] => $tzdef, |
||||||
532 | $this->properties['tzdefend'] => $tzdef, |
||||||
533 | ]); |
||||||
534 | mapi_savechanges($calItem); |
||||||
535 | $data['item']['props']['appointment_startdate'] = $localStart; |
||||||
536 | $data['item']['props']['appointment_duedate'] = $localStart + $duration; |
||||||
537 | } |
||||||
538 | } |
||||||
539 | catch (Exception $e) { |
||||||
540 | error_log(sprintf("Error setting timezone to an all-day event: %s", $e)); |
||||||
541 | } |
||||||
542 | } |
||||||
543 | } |
||||||
544 | |||||||
545 | // Show user whether meeting request conflict with other appointment or not. |
||||||
546 | $meetingConflicts = $req->isMeetingConflicting(); |
||||||
547 | |||||||
548 | /** |
||||||
549 | * if $meetingConflicts is boolean and true then its a normal meeting. |
||||||
550 | * if $meetingConflicts is integer then it indicates no of instances of a recurring meeting which conflicts with Calendar. |
||||||
551 | */ |
||||||
552 | if ($meetingConflicts !== false) { |
||||||
553 | if ($meetingConflicts === true) { |
||||||
554 | $data['item']['props']['conflictinfo'] = _('Conflicts with another appointment.'); |
||||||
555 | } |
||||||
556 | else { |
||||||
557 | $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); |
||||||
558 | } |
||||||
559 | } |
||||||
560 | } |
||||||
561 | } |
||||||
562 | elseif ($req->isMeetingCancellation()) { |
||||||
563 | $req->processMeetingCancellation(); |
||||||
564 | } |
||||||
565 | |||||||
566 | if ($req->isInCalendar()) { |
||||||
567 | $calendarItemProps = $this->getCalendarItemProps($req); |
||||||
568 | if (!empty($calendarItemProps)) { |
||||||
569 | $data['item']['props'] = array_merge($data['item']['props'], $calendarItemProps); |
||||||
570 | } |
||||||
571 | } |
||||||
572 | else { |
||||||
573 | $data['item']['props']['appointment_not_found'] = true; |
||||||
574 | } |
||||||
575 | } |
||||||
576 | catch (MAPIException $e) { |
||||||
577 | // if quota is exceeded or or we don't have permission to write in calendar folder than ignore the exception. |
||||||
578 | if ($e->getCode() !== MAPI_E_STORE_FULL && $e->getCode() !== MAPI_E_NO_ACCESS) { |
||||||
579 | // re-throw the exception if it is not one of quota/calendar permission. |
||||||
580 | throw $e; |
||||||
581 | } |
||||||
582 | } |
||||||
583 | if (!empty($data['item']['props']['appointment_recurring']) && |
||||||
584 | empty($data['item']['props']['appointment_recurring_pattern'])) { |
||||||
585 | $recurr = new Recurrence($store, $message); |
||||||
586 | $data['item']['props']['appointment_recurring_pattern'] = $recurr->saveRecurrencePattern(); |
||||||
587 | } |
||||||
588 | } |
||||||
589 | elseif (stripos($messageClass, 'REPORT.IPM.NOTE.NDR') !== false) { |
||||||
590 | // check if this message is a NDR (mail)message, if so, generate a new body message |
||||||
591 | $data['item']['props']['isHTML'] = false; |
||||||
592 | $data['item']['props']['body'] = $this->getNDRbody($message); |
||||||
593 | } |
||||||
594 | } |
||||||
595 | |||||||
596 | $userEntryId = ''; |
||||||
597 | if (isset($data['item']['props']['sent_representing_entryid'])) { |
||||||
598 | $userEntryId = hex2bin($data['item']['props']['sent_representing_entryid']); |
||||||
599 | } |
||||||
600 | elseif (isset($data['item']['props']['sender_entryid'])) { |
||||||
601 | $userEntryId = hex2bin($data['item']['props']['sender_entryid']); |
||||||
602 | } |
||||||
603 | |||||||
604 | // get user image saved in LDAP. |
||||||
605 | if (!empty($userEntryId)) { |
||||||
606 | $data['item']['props']['user_image'] = $GLOBALS['operations']->getCompressedUserImage($userEntryId); |
||||||
607 | } |
||||||
608 | |||||||
609 | // Allowing to hook in just before the data sent away to be sent to the client |
||||||
610 | $GLOBALS['PluginManager']->triggerHook('server.module.itemmodule.open.after', [ |
||||||
611 | 'moduleObject' => &$this, |
||||||
612 | 'store' => $store, |
||||||
613 | 'entryid' => $entryid, |
||||||
614 | 'action' => $action, |
||||||
615 | 'message' => &$message, |
||||||
616 | 'data' => &$data, |
||||||
617 | ]); |
||||||
618 | |||||||
619 | // ugly workaround to show clip icon for the signed/encrypted emails |
||||||
620 | if (isset($data['item']['props']['smime'])) { |
||||||
621 | $data['item']['props']['hasattach'] = true; |
||||||
622 | unset($data['item']['props']['hide_attachments']); |
||||||
623 | } |
||||||
624 | |||||||
625 | $this->addActionData('item', $data); |
||||||
626 | $GLOBALS['bus']->addData($this->getResponseData()); |
||||||
627 | } |
||||||
628 | |||||||
629 | /** |
||||||
630 | * Function which saves an item. |
||||||
631 | * |
||||||
632 | * @param object $store MAPI Message Store Object |
||||||
633 | * @param string $parententryid parent entryid of the message |
||||||
634 | * @param array $action the action data, sent by the client |
||||||
635 | * @param mixed $entryid |
||||||
636 | */ |
||||||
637 | public function save($store, $parententryid, $entryid, $action) { |
||||||
638 | $result = false; |
||||||
639 | |||||||
640 | if (isset($action["props"])) { |
||||||
641 | if (!$store) { |
||||||
0 ignored issues
–
show
|
|||||||
642 | $store = $GLOBALS['mapisession']->getDefaultMessageStore(); |
||||||
643 | } |
||||||
644 | if (!$parententryid) { |
||||||
645 | if (isset($action['props']['message_class'])) { |
||||||
646 | $parententryid = $this->getDefaultFolderEntryID($store, $action['props']['message_class']); |
||||||
647 | } |
||||||
648 | else { |
||||||
649 | $parententryid = $this->getDefaultFolderEntryID($store, ''); |
||||||
650 | } |
||||||
651 | } |
||||||
652 | |||||||
653 | if ($store && $parententryid) { |
||||||
654 | $props = Conversion::mapXML2MAPI($this->properties, $action["props"]); |
||||||
655 | |||||||
656 | $messageProps = []; // props returned from saveMessage |
||||||
657 | |||||||
658 | // Save message |
||||||
659 | if (!empty($props)) { |
||||||
660 | $result = $GLOBALS["operations"]->saveMessage($store, $entryid, $parententryid, $props, $messageProps, [], !empty($action['attachments']) ? $action['attachments'] : []); |
||||||
661 | } |
||||||
662 | |||||||
663 | if ($result) { |
||||||
664 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_SAVE, $messageProps); |
||||||
665 | |||||||
666 | $this->addActionData("update", ["item" => Conversion::mapMAPI2XML($this->properties, $messageProps)]); |
||||||
667 | $GLOBALS["bus"]->addData($this->getResponseData()); |
||||||
668 | } |
||||||
669 | else { |
||||||
670 | $this->sendFeedback(false); |
||||||
671 | } |
||||||
672 | } |
||||||
673 | } |
||||||
674 | } |
||||||
675 | |||||||
676 | /** |
||||||
677 | * Function which deletes an item. |
||||||
678 | * |
||||||
679 | * @param object $store MAPI Message Store Object |
||||||
680 | * @param string $parententryid parent entryid of the message |
||||||
681 | * @param string $entryid entryid of the message |
||||||
682 | * @param array $action the action data, sent by the client |
||||||
683 | */ |
||||||
684 | public function delete($store, $parententryid, $entryid, $action) { |
||||||
685 | if (!$store || !$parententryid || !$entryid) { |
||||||
0 ignored issues
–
show
|
|||||||
686 | return; |
||||||
687 | } |
||||||
688 | $props = []; |
||||||
689 | $props[PR_PARENT_ENTRYID] = $parententryid; |
||||||
690 | $props[PR_ENTRYID] = $entryid; |
||||||
691 | |||||||
692 | $storeprops = mapi_getprops($store, [PR_ENTRYID]); |
||||||
693 | $props[PR_STORE_ENTRYID] = $storeprops[PR_ENTRYID]; |
||||||
694 | |||||||
695 | $soft = $action['message_action']['soft_delete'] ?? false; |
||||||
696 | $unread = $action['message_action']['non_read_notify'] ?? false; |
||||||
697 | $result = $GLOBALS["operations"]->deleteMessages($store, $parententryid, $entryid, $soft, $unread); |
||||||
698 | if ($result) { |
||||||
699 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $props); |
||||||
700 | $this->sendFeedback(true); |
||||||
701 | } |
||||||
702 | } |
||||||
703 | |||||||
704 | /** |
||||||
705 | * Function which returns the entryid of a default folder. |
||||||
706 | * |
||||||
707 | * @param object $store MAPI Message Store Object |
||||||
708 | * @param string $messageClass the class of the folder |
||||||
709 | * |
||||||
710 | * @return string entryid of a default folder, false if not found |
||||||
711 | */ |
||||||
712 | public function getDefaultFolderEntryID($store, $messageClass) { |
||||||
713 | $entryid = false; |
||||||
714 | |||||||
715 | if ($store) { |
||||||
0 ignored issues
–
show
|
|||||||
716 | $rootcontainer = mapi_msgstore_openentry($store); |
||||||
717 | $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]); |
||||||
718 | |||||||
719 | switch ($messageClass) { |
||||||
720 | case "IPM.Appointment": |
||||||
721 | if (isset($rootcontainerprops[PR_IPM_APPOINTMENT_ENTRYID])) { |
||||||
722 | $entryid = $rootcontainerprops[PR_IPM_APPOINTMENT_ENTRYID]; |
||||||
723 | } |
||||||
724 | break; |
||||||
725 | |||||||
726 | case "IPM.Contact": |
||||||
727 | case "IPM.DistList": |
||||||
728 | if (isset($rootcontainerprops[PR_IPM_CONTACT_ENTRYID])) { |
||||||
729 | $entryid = $rootcontainerprops[PR_IPM_CONTACT_ENTRYID]; |
||||||
730 | } |
||||||
731 | break; |
||||||
732 | |||||||
733 | case "IPM.StickyNote": |
||||||
734 | if (isset($rootcontainerprops[PR_IPM_NOTE_ENTRYID])) { |
||||||
735 | $entryid = $rootcontainerprops[PR_IPM_NOTE_ENTRYID]; |
||||||
736 | } |
||||||
737 | break; |
||||||
738 | |||||||
739 | case "IPM.Task": |
||||||
740 | if (isset($rootcontainerprops[PR_IPM_TASK_ENTRYID])) { |
||||||
741 | $entryid = $rootcontainerprops[PR_IPM_TASK_ENTRYID]; |
||||||
742 | } |
||||||
743 | break; |
||||||
744 | |||||||
745 | default: |
||||||
746 | if (isset($rootcontainerprops[PR_IPM_DRAFTS_ENTRYID])) { |
||||||
747 | $entryid = $rootcontainerprops[PR_IPM_DRAFTS_ENTRYID]; |
||||||
748 | } |
||||||
749 | break; |
||||||
750 | } |
||||||
751 | } |
||||||
752 | |||||||
753 | return $entryid; |
||||||
754 | } |
||||||
755 | |||||||
756 | /** |
||||||
757 | * Function which copies or moves one or more items. |
||||||
758 | * |
||||||
759 | * @param resource $store MAPI Message Store Object |
||||||
760 | * @param string $parententryid entryid of the folder |
||||||
761 | * @param mixed $entryids list of entryids which will be copied or moved (in binary format) |
||||||
762 | * @param array $action the action data, sent by the client |
||||||
763 | */ |
||||||
764 | public function copy($store, $parententryid, $entryids, $action) { |
||||||
765 | $result = false; |
||||||
0 ignored issues
–
show
|
|||||||
766 | |||||||
767 | if ($store && $parententryid && $entryids) { |
||||||
0 ignored issues
–
show
|
|||||||
768 | $dest_store = $store; |
||||||
769 | if (isset($action["message_action"]["destination_store_entryid"])) { |
||||||
770 | $dest_storeentryid = hex2bin($action["message_action"]["destination_store_entryid"]); |
||||||
771 | $dest_store = $GLOBALS["mapisession"]->openMessageStore($dest_storeentryid); |
||||||
772 | } |
||||||
773 | |||||||
774 | $dest_folderentryid = false; |
||||||
775 | if (isset($action["message_action"]["destination_parent_entryid"])) { |
||||||
776 | $dest_folderentryid = hex2bin($action["message_action"]["destination_parent_entryid"]); |
||||||
777 | } |
||||||
778 | |||||||
779 | $moveMessages = false; |
||||||
780 | if (isset($action["message_action"]["action_type"]) && $action["message_action"]["action_type"] == "move") { |
||||||
781 | $moveMessages = true; |
||||||
782 | } |
||||||
783 | |||||||
784 | // if item has some set of props that need to be saved into the newly copied/moved item |
||||||
785 | $copyProps = []; |
||||||
786 | if (isset($action["message_action"]["dropmodifications"])) { |
||||||
787 | $copyProps = Conversion::mapXML2MAPI($this->properties, $action["message_action"]["dropmodifications"]); |
||||||
788 | } |
||||||
789 | |||||||
790 | // if item has some changes made before choosing different calendar from create-in dropdown |
||||||
791 | if (isset($action["props"]) && !empty($action["props"])) { |
||||||
792 | $copyProps = Conversion::mapXML2MAPI($this->properties, $action["props"]); |
||||||
793 | } |
||||||
794 | |||||||
795 | $props = []; |
||||||
796 | $props[PR_PARENT_ENTRYID] = $parententryid; |
||||||
797 | $props[PR_ENTRYID] = $entryids; |
||||||
798 | |||||||
799 | $storeprops = mapi_getprops($store, [PR_ENTRYID]); |
||||||
800 | $props[PR_STORE_ENTRYID] = $storeprops[PR_ENTRYID]; |
||||||
801 | |||||||
802 | $skipCopyProperties = []; |
||||||
803 | if (isset($action["message_action"]["unset_Private"])) { |
||||||
804 | if ($moveMessages) { |
||||||
805 | array_push($skipCopyProperties, $this->properties["private"], $this->properties["sensitivity"]); |
||||||
806 | } |
||||||
807 | else { |
||||||
808 | array_push($this->skipCopyProperties, $this->properties["private"], $this->properties["sensitivity"]); |
||||||
809 | } |
||||||
810 | } |
||||||
811 | |||||||
812 | $result = $GLOBALS["operations"]->copyMessages($store, $parententryid, $dest_store, $dest_folderentryid, $entryids, $moveMessages ? $skipCopyProperties : $this->skipCopyProperties, $moveMessages, $copyProps); |
||||||
813 | |||||||
814 | if ($result) { |
||||||
815 | if ($moveMessages) { |
||||||
816 | $GLOBALS["bus"]->notify(bin2hex($parententryid), TABLE_DELETE, $props); |
||||||
817 | } |
||||||
818 | |||||||
819 | // Delete the PR_ENTRYID, the copied or moved message has a new entryid, |
||||||
820 | // and at this time we have no idea what that might be. So make sure |
||||||
821 | // we unset it, otherwise the notification handlers get weird ideas |
||||||
822 | // and could reset the PR_PARENT_ENTRYID to the old folder again. |
||||||
823 | unset($props[PR_ENTRYID]); |
||||||
824 | $props[PR_PARENT_ENTRYID] = $dest_folderentryid; |
||||||
825 | $props[PR_STORE_ENTRYID] = $dest_storeentryid; |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
826 | $GLOBALS["bus"]->notify(bin2hex($dest_folderentryid), TABLE_SAVE, $props); |
||||||
827 | } |
||||||
828 | |||||||
829 | $this->sendFeedback($result, []); |
||||||
830 | } |
||||||
831 | } |
||||||
832 | |||||||
833 | /** |
||||||
834 | * Function returns correspondent calendar item's properties attached |
||||||
835 | * with the meeting request/response/cancellation. |
||||||
836 | * |
||||||
837 | * @param Meetingrequest $meetingRequestObject the meeting request object |
||||||
838 | * using which function will fetch meeting request properties and return them |
||||||
839 | */ |
||||||
840 | public function getCalendarItemProps($meetingRequestObject) { |
||||||
841 | $calendarItem = $meetingRequestObject->getCorrespondentCalendarItem(); |
||||||
842 | $props = []; |
||||||
843 | if ($calendarItem !== false) { |
||||||
844 | $calendarItemProps = mapi_getprops($calendarItem, [PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $meetingRequestObject->proptags['updatecounter'], $meetingRequestObject->proptags['goid']]); |
||||||
845 | |||||||
846 | // Store calendar item's necessary properties in props array. |
||||||
847 | $props['appointment_store_entryid'] = bin2hex((string) $calendarItemProps[PR_STORE_ENTRYID]); |
||||||
848 | $props['appointment_parent_entryid'] = bin2hex((string) $calendarItemProps[PR_PARENT_ENTRYID]); |
||||||
849 | $props['appointment_entryid'] = bin2hex((string) $calendarItemProps[PR_ENTRYID]); |
||||||
850 | |||||||
851 | $props['appointment_updatecounter'] = $calendarItemProps[$meetingRequestObject->proptags['updatecounter']] ?? 0; |
||||||
852 | |||||||
853 | $messageProps = mapi_getprops($meetingRequestObject->message, [$meetingRequestObject->proptags['goid']]); |
||||||
854 | |||||||
855 | $basedate = $meetingRequestObject->getBasedateFromGlobalID($messageProps[$meetingRequestObject->proptags['goid']]); |
||||||
856 | |||||||
857 | if ($basedate !== false) { |
||||||
858 | $props['appointment_basedate'] = $basedate; |
||||||
859 | |||||||
860 | // if basedate is provided then it is exception, so get update counter of the exception |
||||||
861 | $exception = $meetingRequestObject->getExceptionItem($calendarItem, $basedate); |
||||||
862 | |||||||
863 | if ($exception !== false) { |
||||||
864 | // we are able to find the exception then get updatecounter |
||||||
865 | $exceptionProps = mapi_getprops($exception, [$meetingRequestObject->proptags['updatecounter']]); |
||||||
866 | $props['appointment_updatecounter'] = $exceptionProps[$meetingRequestObject->proptags['updatecounter']] ?? 0; |
||||||
867 | } |
||||||
868 | } |
||||||
869 | |||||||
870 | if ($meetingRequestObject->isMeetingRequestResponse()) { |
||||||
871 | $props['meeting_updated'] = $meetingRequestObject->isMeetingUpdated($basedate); |
||||||
872 | } |
||||||
873 | |||||||
874 | return $props; |
||||||
875 | } |
||||||
876 | |||||||
877 | return false; |
||||||
878 | } |
||||||
879 | |||||||
880 | /** |
||||||
881 | * Get a text body for a Non-Delivery report. |
||||||
882 | * |
||||||
883 | * This function reads the necessary properties from the passed message and constructs |
||||||
884 | * a user-readable NDR message from those properties |
||||||
885 | * |
||||||
886 | * @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 ![]() |
|||||||
887 | * |
||||||
888 | * @return string NDR body message as plaintext message |
||||||
889 | */ |
||||||
890 | public function getNDRbody($message) { |
||||||
891 | $message_props = mapi_getprops($message, [PR_ORIGINAL_SUBJECT, PR_ORIGINAL_SUBMIT_TIME, PR_BODY]); |
||||||
892 | $body = ''; |
||||||
893 | |||||||
894 | // use PR_BODY if it's there, otherwise create a recipient failed message |
||||||
895 | if (isset($message_props[PR_BODY]) || propIsError(PR_BODY, $message_props) == MAPI_E_NOT_ENOUGH_MEMORY) { |
||||||
896 | $body = mapi_openproperty($message, PR_BODY); |
||||||
897 | } |
||||||
898 | |||||||
899 | if (empty($body)) { |
||||||
900 | $body = _("Your message did not reach some or all of the intended recipients") . "\n\n"; |
||||||
901 | $body .= "\t" . _("Subject") . ": " . $message_props[PR_ORIGINAL_SUBJECT] . "\n"; |
||||||
902 | $body .= "\t" . _("Sent") . ": " . date(DATE_RFC2822, $message_props[PR_ORIGINAL_SUBMIT_TIME]) . "\n\n"; |
||||||
903 | $body .= _("The following recipient(s) could not be reached") . ":\n"; |
||||||
904 | |||||||
905 | $recipienttable = mapi_message_getrecipienttable($message); |
||||||
906 | $recipientrows = mapi_table_queryallrows($recipienttable, [PR_DISPLAY_NAME, PR_REPORT_TIME, PR_REPORT_TEXT]); |
||||||
907 | foreach ($recipientrows as $recipient) { |
||||||
908 | $body .= "\n\t" . $recipient[PR_DISPLAY_NAME] . " on " . date(DATE_RFC2822, $recipient[PR_REPORT_TIME]) . "\n"; |
||||||
909 | $body .= "\t\t" . $recipient[PR_REPORT_TEXT] . "\n"; |
||||||
910 | } |
||||||
911 | } |
||||||
912 | |||||||
913 | return $body; |
||||||
914 | } |
||||||
915 | |||||||
916 | /** |
||||||
917 | * Send a meeting cancellation. |
||||||
918 | * |
||||||
919 | * This function sends a meeting cancellation for the meeting references by the passed entryid. It |
||||||
920 | * will send the meeting cancellation and move the item itself to the waste basket. |
||||||
921 | * |
||||||
922 | * @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 ![]() |
|||||||
923 | * @param string $entryid entryid of the appointment for which the cancellation should be sent |
||||||
924 | * @param object $action data sent by client |
||||||
925 | * @param bool $directBookingMeetingRequest Indicates if a Meeting Request should use direct booking or not |
||||||
926 | */ |
||||||
927 | public function cancelInvitation($store, $entryid, $action, $directBookingMeetingRequest) { |
||||||
928 | $message = $GLOBALS['operations']->openMessage($store, $entryid); |
||||||
929 | |||||||
930 | // @TODO move this to meeting request class ? |
||||||
931 | $req = new Meetingrequest($store, $message, $GLOBALS['mapisession']->getSession(), $directBookingMeetingRequest); |
||||||
932 | |||||||
933 | // Update extra body information |
||||||
934 | if (isset($action['message_action']['meetingTimeInfo']) && !empty($action['message_action']['meetingTimeInfo'])) { |
||||||
935 | $req->setMeetingTimeInfo($action["message_action"]['meetingTimeInfo']); |
||||||
936 | unset($action["message_action"]['meetingTimeInfo']); |
||||||
937 | } |
||||||
938 | |||||||
939 | // get basedate from action data and pass to meeting request class |
||||||
940 | $basedate = !empty($action['basedate']) ? $action['basedate'] : false; |
||||||
941 | |||||||
942 | $req->doCancelInvitation($basedate); |
||||||
943 | |||||||
944 | if ($basedate !== false) { |
||||||
945 | // if basedate is specified then we have created exception in recurring meeting request |
||||||
946 | // so send notification of creation of exception |
||||||
947 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
948 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_SAVE, $messageProps); |
||||||
949 | } |
||||||
950 | else { |
||||||
951 | // for normal/recurring meetings send delete notification |
||||||
952 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
953 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), TABLE_DELETE, $messageProps); |
||||||
954 | } |
||||||
955 | } |
||||||
956 | |||||||
957 | /** |
||||||
958 | * Remove all appointments for a certain meeting request. |
||||||
959 | * |
||||||
960 | * This function searches the default calendar for all meeting requests for the specified |
||||||
961 | * meeting. All those appointments are then removed. |
||||||
962 | * |
||||||
963 | * @param mapistore $store Mapi store in which the meeting request and the calendar reside |
||||||
964 | * @param string $entryid Entryid of the meeting request or appointment for which all items should be deleted |
||||||
965 | * @param string $basedate if specified contains starttime of day of an occurrence |
||||||
966 | * @param bool $directBookingMeetingRequest Indicates if a Meeting Request should use direct booking or not |
||||||
967 | */ |
||||||
968 | public function removeFromCalendar($store, $entryid, $basedate, $directBookingMeetingRequest) { |
||||||
969 | $message = $GLOBALS["operations"]->openMessage($store, $entryid); |
||||||
970 | |||||||
971 | $req = new Meetingrequest($store, $message, $GLOBALS["mapisession"]->getSession(), $directBookingMeetingRequest); |
||||||
972 | |||||||
973 | $req->doRemoveFromCalendar($basedate); |
||||||
974 | |||||||
975 | // Notify the bus that the message has been deleted |
||||||
976 | $messageProps = mapi_getprops($message, [PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID]); |
||||||
977 | $GLOBALS["bus"]->notify(bin2hex((string) $messageProps[PR_PARENT_ENTRYID]), $basedate ? TABLE_SAVE : TABLE_DELETE, $messageProps); |
||||||
978 | } |
||||||
979 | } |
||||||
980 |