1 | <?php |
||
2 | |||
3 | require_once __DIR__ . '/../exceptions/class.SearchException.php'; |
||
4 | |||
5 | /** |
||
6 | * Module |
||
7 | * Superclass of every module. Many default functions are defined in this class. |
||
8 | */ |
||
9 | class Module { |
||
10 | /** |
||
11 | * @var string entryid, which will be registered by the bus object |
||
12 | */ |
||
13 | public $entryid; |
||
14 | |||
15 | /** |
||
16 | * @var array list of the results, which is send to the client |
||
17 | */ |
||
18 | public $responseData; |
||
19 | |||
20 | /** |
||
21 | * @var array list of all the errors occurred |
||
22 | */ |
||
23 | public $errors; |
||
24 | |||
25 | /** |
||
26 | * @var State The state object which refers to the statefile |
||
27 | */ |
||
28 | public $sessionState; |
||
29 | |||
30 | /** |
||
31 | * @var array data stored in session for this module |
||
32 | */ |
||
33 | public $sessionData; |
||
34 | |||
35 | /** |
||
36 | * @var array list of the properties necessary for the module |
||
37 | */ |
||
38 | public $properties; |
||
39 | |||
40 | /** |
||
41 | * @var list of the folder list properties |
||
42 | */ |
||
43 | public $list_properties; |
||
44 | |||
45 | /** |
||
46 | * Constructor. |
||
47 | * |
||
48 | * @param int $id unique id |
||
49 | * @param array $data list of all actions |
||
50 | */ |
||
51 | public function __construct(public $id, public $data) { |
||
52 | $this->errors = []; |
||
53 | $this->responseData = []; |
||
54 | $this->sessionState = false; |
||
0 ignored issues
–
show
|
|||
55 | $this->sessionData = false; |
||
0 ignored issues
–
show
It seems like
false of type false is incompatible with the declared type array of property $sessionData .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
56 | |||
57 | $this->createNotifiers(); |
||
58 | |||
59 | // Get the store from $data and set it to properties class. |
||
60 | // It is requires for multi server environment where namespace differs. |
||
61 | // e.g. 'categories' => -2062020578, 'categories' => -2062610402, |
||
62 | if (isset($GLOBALS['properties'])) { |
||
63 | $GLOBALS['properties']->setStore($this->getActionStore($this->getActionData($this->data))); |
||
64 | } |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * Creates the notifiers for this module, |
||
69 | * and register them to the Bus. |
||
70 | */ |
||
71 | public function createNotifiers() {} |
||
72 | |||
73 | /** |
||
74 | * Executes all the actions in the $data variable. |
||
75 | */ |
||
76 | public function execute() { |
||
77 | // you must implement this function for each module |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * This will call $handleException of updating the MAPIException based in the module data. |
||
82 | * When this is done, $sendFeedback will be called to send the message to the client. |
||
83 | * |
||
84 | * @param object $e exception object |
||
85 | * @param string $actionType the action type, sent by the client |
||
86 | * @param MAPIobject $store store object of the store |
||
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 ![]() |
|||
87 | * @param string $parententryid parent entryid of the message |
||
88 | * @param string $entryid entryid of the message/folder |
||
89 | * @param array $action the action data, sent by the client |
||
90 | */ |
||
91 | public function processException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) { |
||
92 | $this->handleException($e, $actionType, $store, $parententryid, $entryid, $action); |
||
93 | $this->sendFeedback(false, $this->errorDetailsFromException($e)); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Function does customization of MAPIException based on module data. |
||
98 | * like, here it will generate display message based on actionType |
||
99 | * for particular exception. |
||
100 | * |
||
101 | * @param object $e exception object |
||
102 | * @param string $actionType the action type, sent by the client |
||
103 | * @param MAPIobject $store store object of the message |
||
104 | * @param string $parententryid parent entryid of the message |
||
105 | * @param string $entryid entryid of the message/folder |
||
106 | * @param array $action the action data, sent by the client |
||
107 | */ |
||
108 | public function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) { |
||
109 | if (is_null($e->displayMessage)) { |
||
110 | switch ($actionType) { |
||
111 | case "save": |
||
112 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
113 | $e->setDisplayMessage(_("You have insufficient privileges to save this message.")); |
||
114 | } |
||
115 | else { |
||
116 | $e->setDisplayMessage(_("Could not save message.")); |
||
117 | } |
||
118 | $e->allowToShowDetailMessage = true; |
||
119 | break; |
||
120 | |||
121 | case "delete": |
||
122 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
123 | $e->setDisplayMessage(_("You have insufficient privileges to delete this message.")); |
||
124 | } |
||
125 | else { |
||
126 | $e->setDisplayMessage(_("Could not delete message.")); |
||
127 | } |
||
128 | break; |
||
129 | |||
130 | case "cancelMeetingRequest": |
||
131 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
132 | $e->setDisplayMessage(_("You have insufficient privileges to cancel this Meeting Request.")); |
||
133 | } |
||
134 | else { |
||
135 | $e->setDisplayMessage(_("Could not cancel Meeting Request.")); |
||
136 | } |
||
137 | break; |
||
138 | |||
139 | case "declineMeetingRequest": |
||
140 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
141 | $e->setDisplayMessage(_("You have insufficient privileges to decline this Meeting Request.")); |
||
142 | } |
||
143 | else { |
||
144 | $e->setDisplayMessage(_("Could not decline Meeting Request.")); |
||
145 | } |
||
146 | break; |
||
147 | |||
148 | case "acceptMeetingRequest": |
||
149 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
150 | $e->setDisplayMessage(_("You have insufficient privileges to accept this Meeting Request.")); |
||
151 | } |
||
152 | else { |
||
153 | $e->setDisplayMessage(_("Could not accept Meeting Request.")); |
||
154 | } |
||
155 | break; |
||
156 | |||
157 | case "cancelInvitation": |
||
158 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
159 | $e->setDisplayMessage(_("You have insufficient privileges to cancel Meeting Request invitation.")); |
||
160 | } |
||
161 | else { |
||
162 | $e->setDisplayMessage(_("Could not cancel Meeting Request invitations.")); |
||
163 | } |
||
164 | break; |
||
165 | |||
166 | case "updatesearch": |
||
167 | case "stopsearch": |
||
168 | case "search": |
||
169 | if ($e->getCode() == MAPI_E_NOT_INITIALIZED) { |
||
170 | $e->setDisplayMessage(_("You can not continue search operation on this folder.")); |
||
171 | } |
||
172 | else { |
||
173 | $e->setDisplayMessage(_("Error in search, please try again.")); |
||
174 | } |
||
175 | break; |
||
176 | |||
177 | case "expand": |
||
178 | $e->setDisplayMessage(_("Error in distribution list expansion.")); |
||
179 | break; |
||
180 | } |
||
181 | Log::Write( |
||
182 | LOGLEVEL_ERROR, |
||
183 | "Module::handleException():" . $actionType . ": " . $e->displayMessage |
||
184 | ); |
||
185 | } |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Get quota information of user store and check for over qouta restrictions, |
||
190 | * if any qouta (softquota/hardquota) limit is exceeded then it will simply |
||
191 | * return appropriate message string according to quota type(hardquota/softquota). |
||
192 | * |
||
193 | * @param mixed $store Store object of the store |
||
194 | * |
||
195 | * @return string short message according to quota type(hardquota/softquota) or a blank string |
||
196 | */ |
||
197 | public function getOverQuotaMessage($store) { |
||
198 | if ($store === false) { |
||
199 | $store = $GLOBALS['mapisession']->getDefaultMessageStore(); |
||
200 | } |
||
201 | |||
202 | $storeProps = mapi_getprops($store, [PR_QUOTA_WARNING_THRESHOLD, PR_QUOTA_SEND_THRESHOLD, PR_QUOTA_RECEIVE_THRESHOLD, PR_MESSAGE_SIZE_EXTENDED]); |
||
203 | |||
204 | $quotaDetails = [ |
||
205 | 'store_size' => round($storeProps[PR_MESSAGE_SIZE_EXTENDED] / 1024), |
||
206 | 'quota_warning' => $storeProps[PR_QUOTA_WARNING_THRESHOLD], |
||
207 | 'quota_soft' => $storeProps[PR_QUOTA_SEND_THRESHOLD], |
||
208 | 'quota_hard' => $storeProps[PR_QUOTA_RECEIVE_THRESHOLD], |
||
209 | ]; |
||
210 | |||
211 | if ($quotaDetails['quota_hard'] !== 0 && $quotaDetails['store_size'] > $quotaDetails['quota_hard']) { |
||
212 | return _('The message store has exceeded its hard quota limit.') . '<br/>' . |
||
213 | _('To reduce the amount of data in this message store, select some items that you no longer need, delete them and cleanup your Deleted Items folder.'); |
||
214 | } |
||
215 | |||
216 | // if hard quota limit doesn't restrict the operation then check for soft qouta limit |
||
217 | if ($quotaDetails['quota_soft'] !== 0 && $quotaDetails['store_size'] > $quotaDetails['quota_soft']) { |
||
218 | return _('The message store has exceeded its soft quota limit.') . '<br/> ' . |
||
219 | _('To reduce the amount of data in this message store, select some items that you no longer need, delete them and cleanup your Deleted Items folder.'); |
||
220 | } |
||
221 | |||
222 | return ''; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * sends a success or error message to client based on parameters passed. |
||
227 | * |
||
228 | * @param bool $success operation completed successfully or not |
||
229 | * @param array $data the data array that will be send to the client as a response to success/failure |
||
230 | * @param bool $addResponseDataToBus if true then data will be added to bus otherwise data |
||
231 | * will be stored in module and later requests can add it to bus |
||
232 | */ |
||
233 | public function sendFeedback($success = false, $data = [], $addResponseDataToBus = true) { |
||
234 | // Send success/error message to client |
||
235 | $this->addActionData($success === true ? "success" : "error", $data); |
||
236 | |||
237 | if ($addResponseDataToBus) { |
||
238 | $GLOBALS["bus"]->addData($this->getResponseData()); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Function will retrieve error details from exception object based on exception type. |
||
244 | * it should also send type of exception with the data. so client can know which type |
||
245 | * of exception is generated. |
||
246 | * |
||
247 | * @param object $exception the exception object which is generated |
||
248 | * |
||
249 | * @return array error data |
||
250 | */ |
||
251 | public function errorDetailsFromException($exception) { |
||
252 | if (!$exception->isHandled) { |
||
253 | if ($exception instanceof MAPIException) { |
||
254 | $exception->setHandled(); |
||
255 | |||
256 | return [ |
||
257 | "type" => ERROR_MAPI, |
||
258 | "info" => [ |
||
259 | "hresult" => $exception->getCode(), |
||
260 | "title" => $exception->getTitle(), |
||
261 | "hresult_name" => get_mapi_error_name($exception->getCode()), |
||
262 | "file" => $exception->getFileLine(), |
||
263 | "display_message" => $exception->getDisplayMessage(), |
||
264 | "details_message" => $exception->getDetailsMessage(), |
||
265 | ], |
||
266 | ]; |
||
267 | } |
||
268 | if ($exception instanceof ZarafaException) { |
||
269 | $exception->setHandled(); |
||
270 | |||
271 | return [ |
||
272 | "type" => ERROR_ZARAFA, |
||
273 | "info" => [ |
||
274 | "file" => $exception->getFileLine(), |
||
275 | "title" => $exception->getTitle(), |
||
276 | "display_message" => $exception->getDisplayMessage(), |
||
277 | "original_message" => $exception->getMessage(), |
||
278 | ], |
||
279 | ]; |
||
280 | } |
||
281 | } |
||
282 | |||
283 | return []; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Function which returns an entryid, which is used to register this module. It |
||
288 | * searches in the class variable $data for a ParentEntryID or an EntryID. |
||
289 | * |
||
290 | * @return string an entryid if found, false if entryid not found |
||
291 | */ |
||
292 | public function getEntryID() { |
||
293 | $entryid = false; |
||
294 | foreach ($this->data as $action) { |
||
295 | if (isset($action["parent_entryid"]) && !empty($action["parent_entryid"])) { |
||
296 | $entryid = $action["parent_entryid"]; |
||
297 | } |
||
298 | elseif (isset($action["entryid"]) && !empty($action["entryid"])) { |
||
299 | $entryid = $action["entryid"]; |
||
300 | } |
||
301 | } |
||
302 | |||
303 | return $entryid; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Returns all the errors, which occurred. |
||
308 | * |
||
309 | * @return array an array of all the errors, which occurred |
||
310 | */ |
||
311 | public function getErrors() { |
||
312 | return $this->errors; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Returns the response data. |
||
317 | * |
||
318 | * @return array An array of the response data. This data is send to the client. |
||
319 | */ |
||
320 | public function getData() { |
||
321 | return $this->responseData; |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * Sets the action data, which will be executed. |
||
326 | * |
||
327 | * @param array $data array of all the actions |
||
328 | */ |
||
329 | public function setData($data) { |
||
330 | $this->data = $data; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Function which returns MAPI Message Store Object. It |
||
335 | * searches in the variable $action for a storeid. |
||
336 | * |
||
337 | * @param array $action the XML data retrieved from the client |
||
338 | * |
||
339 | * @return object MAPI Message Store Object, false if storeid is not found in the $action variable |
||
340 | */ |
||
341 | public function getActionStore($action) { |
||
342 | $store = false; |
||
343 | |||
344 | try { |
||
345 | if (isset($action["store_entryid"]) && !empty($action["store_entryid"])) { |
||
346 | if (is_array($action["store_entryid"])) { |
||
347 | $store = []; |
||
348 | foreach ($action["store_entryid"] as $store_id) { |
||
349 | array_push($store, $GLOBALS["mapisession"]->openMessageStore(hex2bin((string) $store_id))); |
||
350 | } |
||
351 | } |
||
352 | elseif (ctype_xdigit((string) $action["store_entryid"])) { |
||
353 | $store = $GLOBALS["mapisession"]->openMessageStore(hex2bin((string) $action["store_entryid"])); |
||
354 | } |
||
355 | } |
||
356 | } |
||
357 | catch (Exception) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
358 | } |
||
359 | |||
360 | return $store; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Function which returns a parent entryid. It |
||
365 | * searches in the variable $action for a parententryid. |
||
366 | * |
||
367 | * @param array $action the XML data retrieved from the client |
||
368 | * |
||
369 | * @return object MAPI Message Store Object, false if parententryid is not found in the $action variable |
||
370 | */ |
||
371 | public function getActionParentEntryID($action) { |
||
372 | $parententryid = false; |
||
373 | |||
374 | if (isset($action["parent_entryid"]) && !empty($action["parent_entryid"])) { |
||
375 | $parententryid = hex2bin((string) $action["parent_entryid"]); |
||
376 | } |
||
377 | |||
378 | return $parententryid; |
||
0 ignored issues
–
show
|
|||
379 | } |
||
380 | |||
381 | /** |
||
382 | * Function which returns an entryid. It |
||
383 | * searches in the variable $action for an entryid. |
||
384 | * |
||
385 | * @param array $action the XML data retrieved from the client |
||
386 | * |
||
387 | * @return mixed MAPI Message Store Object, false if entryid is not found in the $action variable |
||
388 | */ |
||
389 | public function getActionEntryID($action) { |
||
390 | $entryid = false; |
||
391 | |||
392 | if (isset($action["entryid"]) && !empty($action["entryid"])) { |
||
393 | if (is_array($action["entryid"])) { |
||
394 | $entryid = []; |
||
395 | foreach ($action["entryid"] as $action_entryid) { |
||
396 | array_push($entryid, hex2bin((string) $action_entryid)); |
||
397 | } |
||
398 | } |
||
399 | else { |
||
400 | $entryid = hex2bin((string) $action["entryid"]); |
||
401 | } |
||
402 | } |
||
403 | |||
404 | return $entryid; |
||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Helper function which used to get the action data from request. |
||
409 | * |
||
410 | * @param array $data list of all actions |
||
411 | * |
||
412 | * @return array $action the json data retrieved from the client |
||
413 | */ |
||
414 | public function getActionData($data) { |
||
415 | $actionData = false; |
||
416 | foreach ($data as $actionType => $action) { |
||
417 | if (isset($actionType)) { |
||
418 | $actionData = $action; |
||
419 | } |
||
420 | } |
||
421 | |||
422 | return $actionData; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Function which adds action data to module, so later it can be retrieved to send. |
||
427 | * |
||
428 | * @param string $actionType type of action that response data corresponds |
||
429 | * @param mixed $data |
||
430 | */ |
||
431 | public function addActionData($actionType, $data) { |
||
432 | if (!isset($this->responseData[$actionType])) { |
||
433 | $this->responseData[$actionType] = $data; |
||
434 | } |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Function which returns response data that will be sent to client. If there isn't any data added |
||
439 | * to response data then it will return a blank array. |
||
440 | * |
||
441 | * @return object response data |
||
442 | */ |
||
443 | public function getResponseData() { |
||
444 | if (!empty($this->responseData)) { |
||
445 | return |
||
0 ignored issues
–
show
|
|||
446 | [ |
||
447 | $this->getModuleName() => [ |
||
448 | $this->id => $this->responseData, |
||
449 | ], |
||
450 | ]; |
||
451 | } |
||
452 | |||
453 | return []; |
||
0 ignored issues
–
show
|
|||
454 | } |
||
455 | |||
456 | /** |
||
457 | * Function which returns name of the module class. |
||
458 | * |
||
459 | * @return string module name |
||
460 | */ |
||
461 | public function getModuleName() { |
||
462 | return strtolower(static::class); |
||
463 | } |
||
464 | |||
465 | /** |
||
466 | * Function which will handle unknown action type for all modules. |
||
467 | * |
||
468 | * @param string $actionType action type |
||
469 | */ |
||
470 | public function handleUnknownActionType($actionType) { |
||
471 | $this->sendFeedback( |
||
472 | false, |
||
473 | [ |
||
474 | "type" => ERROR_ZARAFA, |
||
475 | "info" => [ |
||
476 | "display_message" => _("Could not process request data properly."), |
||
477 | "original_message" => sprintf(_("Unknown action type specified - %s"), $actionType), |
||
478 | ], |
||
479 | ] |
||
480 | ); |
||
481 | Log::Write( |
||
482 | LOGLEVEL_ERROR, |
||
483 | "Module::handleUnknownActionType(): ERROR_ZARAFA : " . _("Could not process request data properly.") |
||
484 | ); |
||
485 | } |
||
486 | |||
487 | /** |
||
488 | * Loads sessiondata of the module from state file on disk. |
||
489 | */ |
||
490 | public function loadSessionData() { |
||
491 | $this->sessionState = new State('module_sessiondata'); |
||
492 | $this->sessionState->open(); |
||
493 | $this->sessionData = $this->sessionState->read($this->getModuleName()); |
||
0 ignored issues
–
show
It seems like
$this->sessionState->read($this->getModuleName()) can also be of type string . However, the property $sessionData is declared as type array . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
494 | } |
||
495 | |||
496 | /** |
||
497 | * Saves sessiondata of the module to the state file on disk. |
||
498 | */ |
||
499 | public function saveSessionData() { |
||
500 | if ($this->sessionData !== false) { |
||
0 ignored issues
–
show
|
|||
501 | $this->sessionState->write($this->getModuleName(), $this->sessionData); |
||
502 | } |
||
503 | $this->sessionState->close(); |
||
504 | } |
||
505 | } |
||
506 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..