1 | <?php |
||
2 | |||
3 | /** |
||
4 | * ResolveNames Module. |
||
5 | */ |
||
6 | class ResolveNamesModule extends Module { |
||
7 | /** |
||
8 | * Constructor. |
||
9 | * |
||
10 | * @param mixed $id |
||
11 | * @param mixed $data |
||
12 | */ |
||
13 | public function __construct($id, $data) { |
||
14 | parent::__construct($id, $data); |
||
15 | } |
||
16 | |||
17 | /** |
||
18 | * Executes all the actions in the $data variable. |
||
19 | */ |
||
20 | #[Override] |
||
21 | public function execute() { |
||
22 | foreach ($this->data as $actionType => $action) { |
||
23 | if (isset($actionType)) { |
||
24 | try { |
||
25 | match ($actionType) { |
||
26 | 'checknames' => $this->checkNames($action), |
||
0 ignored issues
–
show
|
|||
27 | default => $this->handleUnknownActionType($actionType), |
||
0 ignored issues
–
show
Are you sure the usage of
$this->handleUnknownActionType($actionType) targeting Module::handleUnknownActionType() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||
28 | }; |
||
29 | } |
||
30 | catch (MAPIException $e) { |
||
31 | $this->processException($e, $actionType); |
||
32 | } |
||
33 | } |
||
34 | } |
||
35 | } |
||
36 | |||
37 | /** |
||
38 | * Function which checks the names, sent by the client. This function is used |
||
39 | * when a user wants to sent an email and want to check the names filled in |
||
40 | * by the user in the to, cc and bcc field. This function uses the global |
||
41 | * user list to check if the names are correct. |
||
42 | * |
||
43 | * @param array $action the action data, sent by the client |
||
44 | */ |
||
45 | public function checkNames($action) { |
||
46 | if (isset($action['resolverequests'])) { |
||
47 | $data = []; |
||
48 | $excludeLocalContacts = $action['exclude_local_contacts'] ?? false; |
||
49 | $excludeGABGroups = $action['exclude_gab_groups'] ?? false; |
||
50 | $resolveRequest = $action['resolverequests']; |
||
51 | if (!is_array($resolveRequest)) { |
||
52 | $resolveRequest = [$resolveRequest]; |
||
53 | } |
||
54 | |||
55 | // open addressbook |
||
56 | // When local contacts need to be excluded we have pass true as the first argument |
||
57 | // so we will not have any Contact Providers set on the Addressbook resource. |
||
58 | $ab = $GLOBALS['mapisession']->getAddressbook($excludeLocalContacts); |
||
59 | |||
60 | $ab_dir = mapi_ab_openentry($ab); |
||
61 | $resolveResponse = []; |
||
62 | |||
63 | // check names |
||
64 | foreach ($resolveRequest as $query) { |
||
65 | if (is_array($query) && isset($query['id'])) { |
||
66 | $responseEntry = []; |
||
67 | $responseEntry['id'] = $query['id']; |
||
68 | |||
69 | if (!empty($query['display_name']) || !empty($query['email_address'])) { |
||
70 | $responseEntry['result'] = $this->searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups); |
||
71 | $resolveResponse[] = $responseEntry; |
||
72 | } |
||
73 | } |
||
74 | } |
||
75 | |||
76 | $data['resolveresponse'] = $resolveResponse; |
||
77 | |||
78 | $this->addActionData('checknames', $data); |
||
79 | $GLOBALS['bus']->addData($this->getResponseData()); |
||
80 | } |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * This function searches the addressbook specified for users and returns an array with data |
||
85 | * Please note that the returning array must be UTF8. |
||
86 | * |
||
87 | * @param resource $ab The addressbook |
||
88 | * @param resource $ab_dir The addressbook container |
||
89 | * @param string $query The search query, case is ignored |
||
90 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
91 | */ |
||
92 | public function searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups) { |
||
93 | // Prefer resolving the email_address. This allows the user |
||
94 | // to resolve recipients with a display name that matches a EX |
||
95 | // user with an alternative (external) email address. |
||
96 | $searchstr = empty($query['email_address']) ? $query['display_name'] : $query['email_address']; |
||
97 | // If the address_type is 'EX' then we are resolving something which must be found in |
||
98 | // the GAB as an exact match. So add the flag EMS_AB_ADDRESS_LOOKUP to ensure we will not |
||
99 | // get multiple results when multiple items have a partial match. |
||
100 | $flags = $query['address_type'] === 'EX' ? EMS_AB_ADDRESS_LOOKUP : 0; |
||
101 | |||
102 | try { |
||
103 | // First, try an addressbook lookup |
||
104 | $rows = mapi_ab_resolvename($ab, [[PR_DISPLAY_NAME => $searchstr]], $flags); |
||
105 | $this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows); |
||
106 | } |
||
107 | catch (MAPIException $e) { |
||
108 | if ($e->getCode() == MAPI_E_AMBIGUOUS_RECIP) { |
||
109 | $ab_entryid = mapi_ab_getdefaultdir($ab); |
||
110 | $ab_dir = mapi_ab_openentry($ab, $ab_entryid); |
||
111 | // Ambiguous, show possibilities: |
||
112 | $table = mapi_folder_getcontentstable($ab_dir, MAPI_DEFERRED_ERRORS); |
||
113 | $restriction = $this->getAmbigiousContactRestriction($searchstr, $excludeGABGroups, PR_ACCOUNT); |
||
114 | |||
115 | mapi_table_restrict($table, $restriction, TBL_BATCH); |
||
116 | mapi_table_sort($table, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH); |
||
117 | |||
118 | $rows = mapi_table_queryallrows($table, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_ENTRYID, PR_SEARCH_KEY, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]); |
||
119 | |||
120 | $rows = array_merge($rows, $this->getAmbigiousContactResolveResults($ab, $searchstr, $excludeGABGroups)); |
||
121 | } |
||
122 | elseif ($e->getCode() == MAPI_E_NOT_FOUND) { |
||
123 | $rows = []; |
||
124 | if ($query['address_type'] === 'SMTP') { |
||
125 | // If we still can't find anything, and we were searching for a SMTP user |
||
126 | // we can generate a oneoff entry which contains the information of the user. |
||
127 | if (!empty($query['email_address'])) { |
||
128 | $rows[] = [ |
||
129 | PR_ACCOUNT => $query['email_address'], PR_ADDRTYPE => 'SMTP', PR_EMAIL_ADDRESS => $query['email_address'], |
||
130 | PR_DISPLAY_NAME => $query['display_name'], PR_DISPLAY_TYPE_EX => DT_REMOTE_MAILUSER, PR_DISPLAY_TYPE => DT_MAILUSER, |
||
131 | PR_SMTP_ADDRESS => $query['email_address'], PR_OBJECT_TYPE => MAPI_MAILUSER, |
||
132 | PR_ENTRYID => mapi_createoneoff($query['display_name'], 'SMTP', $query['email_address']), |
||
133 | ]; |
||
134 | } |
||
135 | // Check also the user's contacts folders |
||
136 | else { |
||
137 | $this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows); |
||
138 | } |
||
139 | } |
||
140 | } |
||
141 | else { |
||
142 | // all other errors should be propagated to higher level exception handlers |
||
143 | throw $e; |
||
144 | } |
||
145 | } |
||
146 | |||
147 | $items = []; |
||
148 | if ($rows) { |
||
149 | foreach ($rows as $user_data) { |
||
150 | $item = []; |
||
151 | |||
152 | if (!isset($user_data[PR_ACCOUNT])) { |
||
153 | $abitem = mapi_ab_openentry($ab, $user_data[PR_ENTRYID]); |
||
154 | $user_data = mapi_getprops($abitem, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_DISPLAY_TYPE_EX, PR_ENTRYID, PR_SEARCH_KEY, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]); |
||
155 | } |
||
156 | |||
157 | if ($excludeGABGroups && $user_data[PR_OBJECT_TYPE] === MAPI_DISTLIST) { |
||
158 | // exclude groups from result |
||
159 | continue; |
||
160 | } |
||
161 | |||
162 | $item = []; |
||
163 | $item['object_type'] = $user_data[PR_OBJECT_TYPE] ?? MAPI_MAILUSER; |
||
164 | $item['entryid'] = isset($user_data[PR_ENTRYID]) ? bin2hex($user_data[PR_ENTRYID]) : ''; |
||
165 | $item['display_name'] = $user_data[PR_DISPLAY_NAME] ?? ''; |
||
166 | $item['display_type'] = $user_data[PR_DISPLAY_TYPE] ?? DT_MAILUSER; |
||
167 | |||
168 | // Test whether the GUID in the entryid is from the Contact Provider |
||
169 | if ($GLOBALS['entryid']->hasContactProviderGUID($item['entryid'])) { |
||
170 | // The properties for a Distribution List differs from the other objects |
||
171 | if ($item['object_type'] == MAPI_DISTLIST) { |
||
172 | $item['address_type'] = 'MAPIPDL'; |
||
173 | // The email_address is empty for DistList, using display name for resolving |
||
174 | $item['email_address'] = $item['display_name']; |
||
175 | $item['smtp_address'] ??= ''; |
||
176 | } |
||
177 | else { |
||
178 | $item['address_type'] = $user_data[PR_ADDRTYPE] ?? 'SMTP'; |
||
179 | if (isset($user_data['address_type']) && $user_data['address_type'] === 'EX') { |
||
180 | $item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? ''; |
||
181 | } |
||
182 | else { |
||
183 | // Fake being an EX account, since it's actually an SMTP addrtype the email address is in a different property. |
||
184 | $item['smtp_address'] = $user_data[PR_EMAIL_ADDRESS] ?? ''; |
||
185 | // Keep the old scenario happy. |
||
186 | $item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? ''; |
||
187 | } |
||
188 | } |
||
189 | // It can be considered a GAB entry |
||
190 | } |
||
191 | else { |
||
192 | $item['user_name'] = $user_data[PR_ACCOUNT] ?? $item['display_name']; |
||
193 | $item['display_type_ex'] = $user_data[PR_DISPLAY_TYPE_EX] ?? MAPI_MAILUSER; |
||
194 | $item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? ''; |
||
195 | $item['smtp_address'] = $user_data[PR_SMTP_ADDRESS] ?? $item['email_address']; |
||
196 | $item['address_type'] = $user_data[PR_ADDRTYPE] ?? 'SMTP'; |
||
197 | } |
||
198 | |||
199 | if (isset($user_data[PR_SEARCH_KEY])) { |
||
200 | $item['search_key'] = bin2hex($user_data[PR_SEARCH_KEY]); |
||
201 | } |
||
202 | else { |
||
203 | $emailAddress = $item['smtp_address'] ?? $item['email_address']; |
||
204 | $item['search_key'] = bin2hex(strtoupper($item['address_type'] . ':' . $emailAddress)) . '00'; |
||
205 | } |
||
206 | |||
207 | array_push($items, $item); |
||
208 | } |
||
209 | } |
||
210 | |||
211 | return $items; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Used to find multiple entries from the contact folders in the Addressbook when resolving |
||
216 | * returned an ambiguous result. It will find the Contact folders in the Addressbook and |
||
217 | * apply a restriction to extract the entries. |
||
218 | * |
||
219 | * @param resource $ab The addressbook |
||
220 | * @param string $query The search query, case is ignored |
||
221 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
222 | */ |
||
223 | public function getAmbigiousContactResolveResults($ab, $query, $excludeGABGroups) { |
||
224 | /* We need to look for the Contact folders at the bottom of the following tree. |
||
225 | * |
||
226 | * IAddrBook |
||
227 | * - Root Container |
||
228 | * - HIERARCHY TABLE |
||
229 | * - Contacts Folders (Contact Container) |
||
230 | * - HIERARCHY TABLE (Contact Container Hierarchy) |
||
231 | * - Contact folder 1 |
||
232 | * - Contact folder 2 |
||
233 | */ |
||
234 | |||
235 | $rows = []; |
||
236 | $contactFolderRestriction = $this->getAmbigiousContactRestriction($query, $excludeGABGroups, PR_EMAIL_ADDRESS); |
||
237 | // Open the AB Root Container by not supplying an entryid |
||
238 | $abRootContainer = mapi_ab_openentry($ab); |
||
239 | |||
240 | // Get the 'Contact Folders' |
||
241 | $hierarchyTable = mapi_folder_gethierarchytable($abRootContainer, MAPI_DEFERRED_ERRORS); |
||
242 | $abHierarchyRows = mapi_table_queryallrows($hierarchyTable, [PR_AB_PROVIDER_ID, PR_ENTRYID]); |
||
243 | |||
244 | // Look for the 'Contacts Folders' |
||
245 | for ($i = 0,$len = count($abHierarchyRows); $i < $len; ++$i) { |
||
246 | // Check if the folder matches the Contact Provider GUID |
||
247 | if ($abHierarchyRows[$i][PR_AB_PROVIDER_ID] == MUIDZCSAB) { |
||
248 | $abContactContainerEntryid = $abHierarchyRows[$i][PR_ENTRYID]; |
||
249 | break; |
||
250 | } |
||
251 | } |
||
252 | |||
253 | // Next go into the 'Contacts Folders' and look in the hierarchy table for the Contact folders. |
||
254 | if ($abContactContainerEntryid) { |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
255 | // Get the rows from hierarchy table of the 'Contacts Folders' |
||
256 | $abContactContainer = mapi_ab_openentry($ab, $abContactContainerEntryid); |
||
257 | $abContactContainerHierarchyTable = mapi_folder_gethierarchytable($abContactContainer, MAPI_DEFERRED_ERRORS); |
||
258 | $abContactContainerHierarchyRows = mapi_table_queryallrows($abContactContainerHierarchyTable, [PR_DISPLAY_NAME, PR_OBJECT_TYPE, PR_ENTRYID]); |
||
259 | |||
260 | // Loop through all the contact folders found under the 'Contacts Folders' hierarchy |
||
261 | for ($j = 0,$len = count($abContactContainerHierarchyRows); $j < $len; ++$j) { |
||
262 | // Open, get contents table, restrict, sort and then merge the result in the list of $rows |
||
263 | $abContactFolder = mapi_ab_openentry($ab, $abContactContainerHierarchyRows[$j][PR_ENTRYID]); |
||
264 | $abContactFolderTable = mapi_folder_getcontentstable($abContactFolder, MAPI_DEFERRED_ERRORS); |
||
265 | |||
266 | mapi_table_restrict($abContactFolderTable, $contactFolderRestriction, TBL_BATCH); |
||
267 | mapi_table_sort($abContactFolderTable, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH); |
||
268 | |||
269 | // Go go gadget, merge! |
||
270 | $rows = array_merge($rows, mapi_table_queryallrows($abContactFolderTable, [PR_ACCOUNT, PR_DISPLAY_NAME, PR_ENTRYID, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE])); |
||
271 | } |
||
272 | } |
||
273 | |||
274 | return $rows; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Setup the restriction used for resolving in the Contact folders or GAB. |
||
279 | * |
||
280 | * @param string $query The search query, case is ignored |
||
281 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
282 | * @param int $content the PROPTAG to search in |
||
283 | */ |
||
284 | public function getAmbigiousContactRestriction($query, $excludeGABGroups, $content) { |
||
285 | // only return users from who the displayName or the username starts with $name |
||
286 | // TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and $content. |
||
287 | $resAnd = [ |
||
288 | [RES_OR, |
||
289 | [ |
||
290 | [RES_CONTENT, |
||
291 | [ |
||
292 | FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE, |
||
293 | ULPROPTAG => PR_DISPLAY_NAME, |
||
294 | VALUE => $query, |
||
295 | ], |
||
296 | ], |
||
297 | [RES_CONTENT, |
||
298 | [ |
||
299 | FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE, |
||
300 | ULPROPTAG => $content, |
||
301 | VALUE => $query, |
||
302 | ], |
||
303 | ], |
||
304 | ], // RES_OR |
||
305 | ], |
||
306 | ]; |
||
307 | |||
308 | // create restrictions based on excludeGABGroups flag |
||
309 | if ($excludeGABGroups) { |
||
310 | array_push($resAnd, [ |
||
311 | RES_PROPERTY, |
||
312 | [ |
||
313 | RELOP => RELOP_EQ, |
||
314 | ULPROPTAG => PR_OBJECT_TYPE, |
||
315 | VALUE => MAPI_MAILUSER, |
||
316 | ], |
||
317 | ]); |
||
318 | } |
||
319 | else { |
||
320 | array_push($resAnd, [RES_OR, |
||
321 | [ |
||
322 | [RES_PROPERTY, |
||
323 | [ |
||
324 | RELOP => RELOP_EQ, |
||
325 | ULPROPTAG => PR_OBJECT_TYPE, |
||
326 | VALUE => MAPI_MAILUSER, |
||
327 | ], |
||
328 | ], |
||
329 | [RES_PROPERTY, |
||
330 | [ |
||
331 | RELOP => RELOP_EQ, |
||
332 | ULPROPTAG => PR_OBJECT_TYPE, |
||
333 | VALUE => MAPI_DISTLIST, |
||
334 | ], |
||
335 | ], |
||
336 | ], |
||
337 | ]); |
||
338 | } |
||
339 | |||
340 | return [RES_AND, $resAnd]; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Function does customization of exception based on module data. |
||
345 | * like, here it will generate display message based on actionType |
||
346 | * for particular exception. |
||
347 | * |
||
348 | * @param object $e Exception object |
||
349 | * @param string $actionType the action type, sent by the client |
||
350 | * @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 ![]() |
|||
351 | * @param string $parententryid parent entryid of the message |
||
352 | * @param string $entryid entryid of the message |
||
353 | * @param array $action the action data, sent by the client |
||
354 | */ |
||
355 | #[Override] |
||
356 | public function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) { |
||
357 | if (is_null($e->displayMessage)) { |
||
358 | switch ($actionType) { |
||
359 | case 'checknames': |
||
360 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
361 | $e->setDisplayMessage(_('You have insufficient privileges to perform this action.')); |
||
362 | } |
||
363 | else { |
||
364 | $e->setDisplayMessage(_('Could not resolve user.')); |
||
365 | } |
||
366 | break; |
||
367 | } |
||
368 | } |
||
369 | |||
370 | parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * This function searches the private contact folders for users and returns an array with data. |
||
375 | * Please note that the returning array must be UTF8. |
||
376 | * |
||
377 | * @param resource $ab The addressbook |
||
378 | * @param resource $ab_dir The addressbook container |
||
379 | * @param string $searchstr The search query, case is ignored |
||
380 | * @param array $rows Array of the found contacts |
||
381 | */ |
||
382 | public function searchContactsFolders($ab, $ab_dir, $searchstr, &$rows) { |
||
383 | $abhtable = mapi_folder_gethierarchytable($ab_dir, MAPI_DEFERRED_ERRORS | CONVENIENT_DEPTH); |
||
384 | $abcntfolders = mapi_table_queryallrows($abhtable, [PR_ENTRYID, PR_AB_PROVIDER_ID]); |
||
385 | $restriction = [ |
||
386 | RES_CONTENT, |
||
387 | [ |
||
388 | FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, |
||
389 | ULPROPTAG => PR_DISPLAY_NAME, |
||
390 | VALUE => [PR_DISPLAY_NAME => $searchstr], |
||
391 | ], |
||
392 | ]; |
||
393 | // restriction on hierarchy table for PR_AB_PROVIDER_ID |
||
394 | // seems not to work, just loop through |
||
395 | foreach ($abcntfolders as $abcntfolder) { |
||
396 | if ($abcntfolder[PR_AB_PROVIDER_ID] == ZARAFA_CONTACTS_GUID) { |
||
397 | $abfldentry = mapi_ab_openentry($ab, $abcntfolder[PR_ENTRYID]); |
||
398 | $abfldcontents = mapi_folder_getcontentstable($abfldentry); |
||
399 | mapi_table_restrict($abfldcontents, $restriction); |
||
400 | $r = mapi_table_queryallrows($abfldcontents, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_ENTRYID, |
||
401 | PR_SEARCH_KEY, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, |
||
402 | PR_OBJECT_TYPE, PR_DISPLAY_TYPE]); |
||
403 | if (is_array($r) && !empty($r)) { |
||
404 | $rows = array_merge($rows, $r); |
||
405 | } |
||
406 | } |
||
407 | } |
||
408 | } |
||
409 | } |
||
410 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.