Issues (752)

includes/modules/class.addressbookitemmodule.php (2 issues)

Severity
1
<?php
2
3
/**
4
 * Read Mail ItemModule.
5
 */
6
class AddressbookItemModule extends ItemModule {
7
	private $userDetailProperties;
8
	private $abObjectDetailProperties;
9
	private $groupDetailProperties;
10
11
	/**
12
	 * Constructor.
13
	 *
14
	 * @param int   $id   unique id
15
	 * @param array $data list of all actions
16
	 */
17
	public function __construct($id, $data) {
18
		$this->userDetailProperties = $GLOBALS["properties"]->getAddressBookItemMailuserProperties();
19
		$this->abObjectDetailProperties = $GLOBALS["properties"]->getAddressBookItemABObjectProperties();
20
		$this->groupDetailProperties = $GLOBALS["properties"]->getAddressBookItemDistlistProperties();
21
22
		parent::__construct($id, $data);
23
	}
24
25
	/**
26
	 * Function which opens an item.
27
	 *
28
	 * @param object $store   MAPI Message Store Object
29
	 * @param string $entryid entryid of the message
30
	 * @param array  $action  the action data, sent by the client
31
	 */
32
	#[Override]
33
	public function open($store, $entryid, $action) {
34
		if ($entryid) {
35
			$data = [];
36
37
			try {
38
				$abentry = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(false, true), $entryid);
39
			}
40
			catch (MAPIException $e) {
41
				// If the item not found in addressbook, it might be possible that
42
				// this particular item was added by setup-contact-provider mechanism.
43
				// Let us try to get that particular contact item in respective user store.
44
				// @Fixme: After implementation of KC-350 this extra handling can be removed.
45
				if ($e->getCode() == MAPI_E_NOT_FOUND || $e->getCode() == MAPI_E_INVALID_PARAMETER) {
46
					if ($GLOBALS['entryid']->hasNoMuid(bin2hex($entryid))) {
47
						$entryid = substr($entryid, 0, -1);
48
					}
49
					else {
50
						// unwrap ab entry id
51
						$entryid = substr($entryid, 28);
52
					}
53
54
					try {
55
						$contactItem = $GLOBALS['operations']->openMessage($GLOBALS['mapisession']->getDefaultMessageStore(), $entryid);
56
					}
57
					catch (MAPIException $me) {
58
						if (!empty($action['store_entryid'])) {
59
							$e->setHandled();
60
							$me->setHandled();
61
							$userStore = $GLOBALS['mapisession']->openMessageStore(hex2bin((string) $action['store_entryid']));
62
							$contactItem = $GLOBALS['operations']->openMessage($userStore, $entryid);
63
						}
64
					}
65
					finally {
66
						if (isset($contactItem) && $contactItem != false) {
67
							// Get necessary property from respective contact item
68
							$contactItemProps = mapi_getprops($contactItem, [PR_GIVEN_NAME, PR_DISPLAY_NAME, PR_TITLE, PR_COMPANY_NAME]);
69
70
							// Use the data retrieved from contact item to prepare response
71
							// as similar as it seems like an addressbook item.
72
							$data['props'] = [
73
								'object_type' => MAPI_MAILUSER,
74
								'given_name' => $contactItemProps[PR_GIVEN_NAME],
75
								'display_name' => $contactItemProps[PR_DISPLAY_NAME],
76
								'title' => $contactItemProps[PR_TITLE],
77
								'company_name' => $contactItemProps[PR_COMPANY_NAME],
78
							];
79
							$data['entryid'] = bin2hex($entryid);
80
81
							$this->addActionData("item", $data);
82
							$GLOBALS["bus"]->addData($this->getResponseData());
83
84
							return;
85
						}
86
					}
87
				}
88
			}
89
90
			if (mapi_last_hresult() == NOERROR && $abentry) {
91
				$objecttypeprop = mapi_getprops($abentry, [PR_OBJECT_TYPE]);
92
93
				if (isset($objecttypeprop[PR_OBJECT_TYPE])) {
94
					// Get the properties for a MAILUSER object and process those MAILUSER specific props that require some more actions
95
					if ($objecttypeprop[PR_OBJECT_TYPE] == MAPI_MAILUSER) {
96
						$messageprops = mapi_getprops($abentry, $this->userDetailProperties);
97
98
						$props = Conversion::mapMAPI2XML($this->userDetailProperties, $messageprops);
99
100
						if (isset($messageprops[PR_EMS_AB_THUMBNAIL_PHOTO])) {
101
							$props['props']['ems_ab_thumbnail_photo'] = $GLOBALS['operations']->compressedImage($messageprops[PR_EMS_AB_THUMBNAIL_PHOTO]);
102
						}
103
104
						// Get the properties of the manager
105
						$managerProps = $this->getManagerDetails($messageprops);
106
						if ($managerProps !== false) {
0 ignored issues
show
The condition $managerProps !== false is always true.
Loading history...
107
							$props['ems_ab_manager'] = [
108
								'item' => $managerProps,
109
							];
110
						}
111
112
						$homePhoneNumbers = $this->getHomePhoneNumbers($messageprops);
113
						if (!empty($homePhoneNumbers)) {
114
							// Add the list of home2_telephone_number_mv in the correct format to $props list to be send to the client
115
							$props['home2_telephone_numbers'] = [
116
								'item' => $homePhoneNumbers,
117
							];
118
						}
119
120
						$businessPhoneNumbers = $this->getBusinessPhoneNumbers($messageprops);
121
						if (!empty($businessPhoneNumbers)) {
122
							// Add the list of business2_telephone_number_mv in the correct format to $props list to be send to the client
123
							$props['business2_telephone_numbers'] = [
124
								'item' => $businessPhoneNumbers,
125
							];
126
						}
127
128
						// Get the properties of the "direct reports"
129
						$directReportsList = $this->getDirectReportsDetails($messageprops);
130
						if (!empty($directReportsList)) {
131
							// Add the list of proxy_addresses in the correct format to the $props list to be send to the client.
132
							$props['ems_ab_reports'] = [
133
								'item' => $directReportsList,
134
							];
135
						}
136
137
					// Get the properties for a DISTLIST object and process those DISTLIST specific props that require some more actions
138
					}
139
					else {
140
						$messageprops = mapi_getprops($abentry, $this->groupDetailProperties);
141
						$props = Conversion::mapMAPI2XML($this->groupDetailProperties, $messageprops);
142
143
						// Get the properties of the owner
144
						$ownerProps = $this->getOwnerDetails($messageprops);
145
						if ($ownerProps !== false) {
0 ignored issues
show
The condition $ownerProps !== false is always true.
Loading history...
146
							// We can use the same properties as we use for the manager in a MAILUSER
147
							$props['ems_ab_owner'] = [
148
								'item' => $ownerProps,
149
							];
150
						}
151
152
						// Get the list of members for this DISTLSIT
153
						$props['members'] = [
154
							'item' => $this->getMembersDetails($abentry),
155
						];
156
					}
157
158
					// Get the proxy addresses list, this property exists in both MAILUSER and DISTLIST
159
					$proxyAddresses = $this->getProxyAddressesDetails($messageprops);
160
					// Remove the MV-flagged property
161
					if (!empty($proxyAddresses)) {
162
						// Add the list of proxy_addresses in the correct format to the $props list to be send to the client.
163
						$props['ems_ab_proxy_addresses'] = [
164
							'item' => $proxyAddresses,
165
						];
166
					}
167
168
					// Get the properties of the group membership, this property exists in both MAILUSER and DISTLIST
169
					$memberOfList = $this->getMemberOfDetails($messageprops);
170
					if (!empty($memberOfList)) {
171
						// Add the list of proxy_addresses in the correct format to the $props list to be send to the client.
172
						$props['ems_ab_is_member_of_dl'] = [
173
							'item' => $memberOfList,
174
						];
175
					}
176
177
					// Remove the MV-flagged properties and also its single valued counterpart
178
					unset($props['props']['ems_ab_is_member_of_dl'], $props['props']['business2_telephone_number_mv'], $props['props']['business2_telephone_number'], $props['props']['home2_telephone_number_mv'], $props['props']['home2_telephone_number'], $props['props']['ems_ab_proxy_addresses'], $props['props']['ems_ab_reports_mv'], $props['props']['ems_ab_reports'], $props['props']['ems_ab_owner'], $props['props']['ems_ab_manager']);
179
180
					// Allowing to hook in and add more properties
181
					$GLOBALS['PluginManager']->triggerHook("server.module.addressbookitemmodule.open.props", [
182
						'moduleObject' => &$this,
183
						'abentry' => $abentry,
184
						'object_type' => $objecttypeprop[PR_OBJECT_TYPE],
185
						'messageprops' => $messageprops,
186
						'props' => &$props,
187
					]);
188
189
					$data["item"] = $props;
190
					$this->addActionData("item", $data);
191
				}
192
				else {
193
					// Handling error: not able to handle this type of object
194
					$data["error"] = [];
195
					$data["error"]["message"] = _("Could not handle this type of object.");
196
					$this->addActionData("error", $data);
197
				}
198
			}
199
			else {
200
				// Handle not being able to open the object
201
				$data["error"] = [];
202
				$data["error"]["hresult"] = mapi_last_hresult();
203
				$data["error"]["hresult_name"] = get_mapi_error_name(mapi_last_hresult());
204
				$data["error"]["message"] = _("Could not open this object.");
205
				$this->addActionData("error", $data);
206
			}
207
208
			$GLOBALS["bus"]->addData($this->getResponseData());
209
		}
210
	}
211
212
	/**
213
	 * Get Business Telephone numbers in the messageprops array when it is set in the
214
	 * PR_HOME2_TELEPHONE_NUMBER_MV. This property is poorly documented and in Outlook it checks
215
	 * the property with and without the MV flag. The one without a MV flag can contain only one
216
	 * entry and the one with MV flag can contain a list. It then merges both into one list.
217
	 * This function has the same behavior and sets the list in the $messageprops.
218
	 *
219
	 * @param $messageprops Array Details properties of an user entry
220
	 *
221
	 * @return array List of telephone numbers
222
	 */
223
	public function getHomePhoneNumbers($messageprops) {
224
		$list = [];
225
		if (isset($messageprops[$this->userDetailProperties['home2_telephone_number']])) {
226
			$list[] = $messageprops[$this->userDetailProperties['home2_telephone_number']];
227
		}
228
		if (isset($messageprops[$this->userDetailProperties['home2_telephone_number_mv']])) {
229
			$list = array_merge($list, $messageprops[$this->userDetailProperties['home2_telephone_number_mv']]);
230
		}
231
232
		$returnList = [];
233
		for ($i = 0, $len = count($list); $i < $len; ++$i) {
234
			array_push($returnList, ['number' => $list[$i]]);
235
		}
236
237
		return $returnList;
238
	}
239
240
	/**
241
	 * Get Business Telephone numbers in the messageprops array when it is set in the
242
	 * PR_BUSINESS2_TELEPHONE_NUMBER_MV. This property is poorly documented and in Outlook it checks
243
	 * the property with and without the MV flag. The one without a MV flag can contain only one
244
	 * entry and the one with MV flag can contain a list. It then merges both into one list.
245
	 * This function has the same behavior and sets the list in the $messageprops.
246
	 *
247
	 * @param $messageprops Array Details properties of an user entry
248
	 *
249
	 * @return array List of telephone numbers
250
	 */
251
	public function getBusinessPhoneNumbers($messageprops) {
252
		$list = [];
253
		if (isset($messageprops[$this->userDetailProperties['business2_telephone_number']])) {
254
			$list[] = $messageprops[$this->userDetailProperties['business2_telephone_number']];
255
		}
256
		if (isset($messageprops[$this->userDetailProperties['business2_telephone_number_mv']])) {
257
			$list = array_merge($list, $messageprops[$this->userDetailProperties['business2_telephone_number_mv']]);
258
		}
259
260
		$returnList = [];
261
		for ($i = 0, $len = count($list); $i < $len; ++$i) {
262
			array_push($returnList, ['number' => $list[$i]]);
263
		}
264
265
		return $returnList;
266
	}
267
268
	/**
269
	 * Get Proxy Addresses in the messageprops array when it is set in the
270
	 * PR_EMS_AB_PROXY_ADDRESSES. This property is poorly documented and in Outlook it checks
271
	 * the property with and without the MV flag. The one without a MV flag can contain only one
272
	 * entry and the one with MV flag can contain a list. It then merges both into one list.
273
	 * This function has the same behavior and sets the list in the $messageprops.
274
	 *
275
	 * @param $messageprops Array Details properties of an user entry
276
	 *
277
	 * @return array List of addresses
278
	 */
279
	public function getProxyAddressesDetails($messageprops) {
280
		$returnList = [];
281
		if (isset($messageprops[$this->userDetailProperties['ems_ab_proxy_addresses']]) &&
282
			is_array($messageprops[$this->userDetailProperties['ems_ab_proxy_addresses']])) {
283
			foreach ($messageprops[$this->userDetailProperties['ems_ab_proxy_addresses']] as $eapa) {
284
				$returnList[] = ['address' => $eapa];
285
			}
286
		}
287
288
		return $returnList;
289
	}
290
291
	/**
292
	 * Get the information of the manager from the GAB details of a MAPI_MAILUSER. Will use the
293
	 * entryid to get the properties. If no entryid if found false is returned.
294
	 *
295
	 * @param $messageprops Array Details properties of an user entry
296
	 *
297
	 * @return array|bool List of properties or false if no manager is set
298
	 */
299
	public function getManagerDetails($messageprops) {
300
		if (isset($messageprops[$this->userDetailProperties['ems_ab_manager']])) {
301
			$entryidMananager = $messageprops[$this->userDetailProperties['ems_ab_manager']];
302
303
			$managerEntry = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(), $entryidMananager);
304
			$managerProps = mapi_getprops($managerEntry, $this->abObjectDetailProperties);
305
306
			return Conversion::mapMAPI2XML($this->abObjectDetailProperties, $managerProps);
307
		}
308
309
		return false;
310
	}
311
312
	/**
313
	 * Get the list of users that have been set in the PR_EMS_AB_REPORTS property in the
314
	 * $messageprops array. This property is poorly documented and in Outlook it checks
315
	 * the property with and without the MV flag. The one without a MV flag can contain only one
316
	 * entry and the one with MV flag can contain a list. It then merges both into one list.
317
	 * This function has the same behavior and sets the list in the $messageprops.
318
	 *
319
	 * @param $messageprops Array Details properties of an user entry
320
	 *
321
	 * @return array|bool List of properties or false if no manager is set
322
	 */
323
	public function getDirectReportsDetails($messageprops) {
324
		/*
325
		 * Get the entryIds from the PR_EMS_AB_REPORTS property (with and without MV flag as a
326
		 * fallback) and put the entryIds in a list.
327
		 */
328
		$entryids = [];
329
		if (isset($messageprops[$this->userDetailProperties['ems_ab_reports']])) {
330
			$entryids[] = $messageprops[$this->userDetailProperties['ems_ab_reports']];
331
		}
332
		if (isset($messageprops[$this->userDetailProperties['ems_ab_reports_mv']])) {
333
			$entryids = array_merge($entryids, $messageprops[$this->userDetailProperties['ems_ab_reports_mv']]);
334
		}
335
336
		$result = [];
337
		// Convert the entryIds in an array of properties of the AB entryies
338
		for ($i = 0, $len = count($entryids); $i < $len; ++$i) {
339
			// Get the properties from the AB entry
340
			$entry = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(), $entryids[$i]);
341
			$props = mapi_getprops($entry, $this->abObjectDetailProperties);
342
			// Convert the properties for each entry and put it in an array
343
			$result[] = Conversion::mapMAPI2XML($this->abObjectDetailProperties, $props);
344
		}
345
346
		return $result;
347
	}
348
349
	/**
350
	 * Get the list of users that have been set in the PR_EMS_AB_MEMBER_OF_DL property in the
351
	 * $messageprops array. This property is poorly documented and in Outlook it checks
352
	 * the property with and without the MV flag. The one without a MV flag can contain only one
353
	 * entry and the one with MV flag can contain a list. It then merges both into one list.
354
	 * This function has the same behavior and sets the list in the $messageprops.
355
	 *
356
	 * @param $messageprops Array Details properties of an user entry
357
	 *
358
	 * @return array|bool List of properties or false if no manager is set
359
	 */
360
	public function getMemberOfDetails($messageprops) {
361
		$result = [];
362
		// Get the properties of the group membership
363
		if (isset($messageprops[$this->userDetailProperties['ems_ab_is_member_of_dl']])) {
364
			$entryids = $messageprops[$this->userDetailProperties['ems_ab_is_member_of_dl']];
365
			// Get the properties from every entryid in the memberOf list
366
			for ($i = 0, $len = count($entryids); $i < $len; ++$i) {
367
				// Get the properties from the AB entry
368
				$entry = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(), $entryids[$i]);
369
				$props = mapi_getprops($entry, $this->abObjectDetailProperties);
370
				// Convert the properties for each entry and put it in an array
371
				$result[] = Conversion::mapMAPI2XML($this->abObjectDetailProperties, $props);
372
			}
373
		}
374
375
		return $result;
376
	}
377
378
	/**
379
	 * Get the information of the owner from the GAB details of a MAPI_DISTLIST. Will use the
380
	 * entryid to get the properties. If no entryid if found false is returned.
381
	 *
382
	 * @param $messageprops Array Details properties of an distlist entry
383
	 *
384
	 * @return array|bool List of properties or false if no owner is set
385
	 */
386
	public function getOwnerDetails($messageprops) {
387
		if (isset($messageprops[$this->groupDetailProperties['ems_ab_owner']])) {
388
			$entryidOwner = $messageprops[$this->groupDetailProperties['ems_ab_owner']];
389
390
			$ownerEntry = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(), $entryidOwner);
391
			$ownerProps = mapi_getprops($ownerEntry, $this->abObjectDetailProperties);
392
393
			return Conversion::mapMAPI2XML($this->abObjectDetailProperties, $ownerProps);
394
		}
395
396
		return false;
397
	}
398
399
	/**
400
	 * Get the information of the members from the GAB details of a MAPI_DISTLIST. The
401
	 * information can be found in the contentstable of the AB entry opened by the user.
402
	 *
403
	 * @param $abentry Resource Reference to the user-opened AB entry
404
	 *
405
	 * @return array|bool List of members
406
	 */
407
	public function getMembersDetails($abentry) {
408
		$result = [];
409
410
		$table = mapi_folder_getcontentstable($abentry, MAPI_DEFERRED_ERRORS);
411
412
		/*
413
		 * To prevent loading a huge list that the browser cannot handle, it is possible to
414
		 * limit the maximum number of shown items. Note that when the table doesn't
415
		 * contain the requested number of rows, it will not give any errors and simply
416
		 * return what is available.
417
		 * When the limit is 0 or below, then no limit is applied and we use 0x7fffffff
418
		 * to indicate we want to have all rows from the table.
419
		 */
420
		$rows = mapi_table_queryrows($table, $this->abObjectDetailProperties, 0, (ABITEMDETAILS_MAX_NUM_DISTLIST_MEMBERS > 0) ? ABITEMDETAILS_MAX_NUM_DISTLIST_MEMBERS : 0x7FFFFFFF);
421
		for ($i = 0, $len = count($rows); $i < $len; ++$i) {
422
			$result[] = Conversion::mapMAPI2XML($this->abObjectDetailProperties, $rows[$i]);
423
		}
424
425
		return $result;
426
	}
427
}
428