1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
require_once BASE_PATH . 'server/includes/core/class.properties.php'; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* MAPI session handling. |
7
|
|
|
* |
8
|
|
|
* This class handles MAPI authentication and stores |
9
|
|
|
*/ |
10
|
|
|
class MAPISession { |
11
|
|
|
/** |
12
|
|
|
* @var resource This holds the MAPI Session |
13
|
|
|
*/ |
14
|
|
|
private $session; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var resource This can hold the addressbook resource |
18
|
|
|
*/ |
19
|
|
|
private $ab; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var array List with all the currently opened stores |
23
|
|
|
*/ |
24
|
|
|
private $stores; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var string The entryid (binary) of the default store |
28
|
|
|
*/ |
29
|
|
|
private $defaultstore; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var string The entryid (binary) of the public store |
33
|
|
|
*/ |
34
|
|
|
private $publicStore; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var array Information about the current session (username/email/password/etc) |
38
|
|
|
*/ |
39
|
|
|
private $session_info; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var array Mapping username -> entryid for other stores |
43
|
|
|
*/ |
44
|
|
|
private $userstores; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var array Cache for userentryid -> archive properties |
48
|
|
|
*/ |
49
|
|
|
private $archivePropsCache; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var int Makes sure retrieveUserData is called only once |
53
|
|
|
*/ |
54
|
|
|
private $userDataRetrieved; |
55
|
|
|
|
56
|
|
|
public function __construct() { |
57
|
|
|
$this->stores = []; |
58
|
|
|
$this->defaultstore = 0; |
59
|
|
|
$this->publicStore = 0; |
60
|
|
|
$this->session = false; |
|
|
|
|
61
|
|
|
$this->ab = false; |
|
|
|
|
62
|
|
|
$this->userstores = []; |
63
|
|
|
$this->archivePropsCache = []; |
64
|
|
|
$this->userDataRetrieved = false; |
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Logon to via php_mapi extension. |
69
|
|
|
* |
70
|
|
|
* Logs on to Gromox with the specified username and password. If the server is not specified, |
71
|
|
|
* it will logon to the local server. |
72
|
|
|
* |
73
|
|
|
* @param string $username the username of the user |
74
|
|
|
* @param string $password the password of the user |
75
|
|
|
* @param string $server the server address |
76
|
|
|
* @param string $sslcert_file the optional ssl certificate file |
77
|
|
|
* @param string $sslcert_pass the optional ssl certificate password |
78
|
|
|
* @param string $flags the optional logon flags |
79
|
|
|
* |
80
|
|
|
* @return int 0 on no error, otherwise a MAPI error code |
81
|
|
|
*/ |
82
|
|
|
public function logon($username = '', $password = '', $server = DEFAULT_SERVER, $sslcert_file = '', $sslcert_pass = '', $flags = 0) { |
83
|
|
|
$result = NOERROR; |
84
|
|
|
$username = (string) $username; |
85
|
|
|
$password = (string) $password; |
86
|
|
|
$flags |= 1; // Always disable notifications |
87
|
|
|
|
88
|
|
|
try { |
89
|
|
|
$webapp_version = 'WebApp-' . trim(file_get_contents(BASE_PATH . 'version')); |
90
|
|
|
$browser_version = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; |
91
|
|
|
$this->session = mapi_logon_zarafa( |
92
|
|
|
$username, |
93
|
|
|
$password, |
94
|
|
|
$server, |
95
|
|
|
$sslcert_file, |
96
|
|
|
$sslcert_pass, |
97
|
|
|
$flags, |
98
|
|
|
$webapp_version, |
99
|
|
|
$browser_version |
100
|
|
|
); |
101
|
|
|
if ($this->session !== false) { |
102
|
|
|
$this->session_info["username"] = $username; |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
catch (MAPIException $e) { |
106
|
|
|
$result = $e->getCode(); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
return $result; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Logons to gromox using the access token. |
114
|
|
|
* |
115
|
|
|
* @param mixed $email the username/email of the user |
116
|
|
|
* @param mixed $token the access token |
117
|
|
|
* |
118
|
|
|
* @return int 0 on no error, otherwise a MAPI error code |
119
|
|
|
*/ |
120
|
|
|
public function logon_token($email = null, $token = null) { |
121
|
|
|
$result = NOERROR; |
122
|
|
|
$email = (string) $email; |
123
|
|
|
$token = (string) $token; |
124
|
|
|
|
125
|
|
|
try { |
126
|
|
|
$this->session = mapi_logon_token($token); |
127
|
|
|
if ($this->session !== false) { |
128
|
|
|
$this->session_info["username"] = $email; |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
catch (MAPIException $e) { |
132
|
|
|
$result = $e->getCode(); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
return $result; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Get the user MAPI Object. |
140
|
|
|
* |
141
|
|
|
* @param string $userEntryid The user entryid which is going to open. default is false. |
142
|
|
|
* |
143
|
|
|
* @return object an user MAPI object |
144
|
|
|
*/ |
145
|
|
|
public function getUser($userEntryid = false) { |
146
|
|
|
if ($userEntryid === false) { |
147
|
|
|
// get user entryid |
148
|
|
|
$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_USER_ENTRYID]); |
149
|
|
|
$userEntryid = $store_props[PR_USER_ENTRYID]; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// open the user entry |
153
|
|
|
return mapi_ab_openentry($this->getAddressbook(true), $userEntryid); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Get logged-in user information. |
158
|
|
|
* |
159
|
|
|
* This function populates the 'session_info' property of this class with the following information: |
160
|
|
|
* - userentryid: the MAPI entryid of the current user |
161
|
|
|
* - fullname: the fullname of the current user |
162
|
|
|
* - emailaddress: the email address of the current user |
163
|
|
|
* |
164
|
|
|
* The function only populates the information once, subsequent calls will return without error and without |
165
|
|
|
* doing anything. |
166
|
|
|
* |
167
|
|
|
* @return array Array of information about the currently logged-on user |
168
|
|
|
*/ |
169
|
|
|
public function retrieveUserData() { |
170
|
|
|
if ($this->userDataRetrieved) { |
171
|
|
|
return; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
$result = NOERROR; |
175
|
|
|
|
176
|
|
|
try { |
177
|
|
|
$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_USER_ENTRYID]); |
178
|
|
|
$user = $this->getUser($store_props[PR_USER_ENTRYID]); |
179
|
|
|
|
180
|
|
|
// receive userdata |
181
|
|
|
$user_props = [ PR_ASSISTANT, PR_ASSISTANT_TELEPHONE_NUMBER, PR_BUSINESS2_TELEPHONE_NUMBER, PR_BUSINESS_TELEPHONE_NUMBER, |
182
|
|
|
PR_COMPANY_NAME, PR_COUNTRY, PR_DEPARTMENT_NAME, PR_DISPLAY_NAME, |
183
|
|
|
PR_EMAIL_ADDRESS, PR_EMS_AB_THUMBNAIL_PHOTO, PR_GIVEN_NAME, PR_HOME2_TELEPHONE_NUMBER, |
184
|
|
|
PR_STREET_ADDRESS, PR_HOME_TELEPHONE_NUMBER, PR_INITIALS, PR_LOCALITY, |
185
|
|
|
PR_MOBILE_TELEPHONE_NUMBER, PR_OFFICE_LOCATION, PR_PAGER_TELEPHONE_NUMBER, PR_POSTAL_CODE, |
186
|
|
|
PR_PRIMARY_FAX_NUMBER, PR_PRIMARY_TELEPHONE_NUMBER, PR_SEARCH_KEY, PR_SMTP_ADDRESS, |
187
|
|
|
PR_STATE_OR_PROVINCE, PR_SURNAME, PR_TITLE ]; |
188
|
|
|
|
189
|
|
|
$user_props = mapi_getprops($user, $user_props); |
190
|
|
|
|
191
|
|
|
if (is_array($user_props) && isset($user_props[PR_DISPLAY_NAME], $user_props[PR_SMTP_ADDRESS])) { |
192
|
|
|
$this->session_info["userentryid"] = $store_props[PR_USER_ENTRYID]; |
193
|
|
|
$this->session_info["fullname"] = $user_props[PR_DISPLAY_NAME]; |
194
|
|
|
$this->session_info["smtpaddress"] = $user_props[PR_SMTP_ADDRESS]; |
195
|
|
|
$this->session_info["emailaddress"] = $user_props[PR_EMAIL_ADDRESS]; |
196
|
|
|
$this->session_info["searchkey"] = $user_props[PR_SEARCH_KEY]; |
197
|
|
|
$this->session_info["userimage"] = isset($user_props[PR_EMS_AB_THUMBNAIL_PHOTO]) ? "data:image/jpeg;base64," . base64_encode($user_props[PR_EMS_AB_THUMBNAIL_PHOTO]) : ""; |
198
|
|
|
|
199
|
|
|
$this->session_info["given_name"] = isset($user_props[PR_GIVEN_NAME]) ? $user_props[PR_GIVEN_NAME] : ''; |
200
|
|
|
$this->session_info["initials"] = isset($user_props[PR_INITIALS]) ? $user_props[PR_INITIALS] : ''; |
201
|
|
|
$this->session_info["surname"] = isset($user_props[PR_SURNAME]) ? $user_props[PR_SURNAME] : ''; |
202
|
|
|
$this->session_info["street_address"] = isset($user_props[PR_STREET_ADDRESS]) ? $user_props[PR_STREET_ADDRESS] : ''; |
203
|
|
|
$this->session_info["locality"] = isset($user_props[PR_LOCALITY]) ? $user_props[PR_LOCALITY] : ''; |
204
|
|
|
$this->session_info["state_or_province"] = isset($user_props[PR_STATE_OR_PROVINCE]) ? $user_props[PR_STATE_OR_PROVINCE] : ''; |
205
|
|
|
$this->session_info["postal_code"] = isset($user_props[PR_POSTAL_CODE]) ? $user_props[PR_POSTAL_CODE] : ''; |
206
|
|
|
$this->session_info["country"] = isset($user_props[PR_COUNTRY]) ? $user_props[PR_COUNTRY] : ''; |
207
|
|
|
$this->session_info["title"] = isset($user_props[PR_TITLE]) ? $user_props[PR_TITLE] : ''; |
208
|
|
|
$this->session_info["company_name"] = isset($user_props[PR_COMPANY_NAME]) ? $user_props[PR_COMPANY_NAME] : ''; |
209
|
|
|
$this->session_info["department_name"] = isset($user_props[PR_DEPARTMENT_NAME]) ? $user_props[PR_DEPARTMENT_NAME] : ''; |
210
|
|
|
$this->session_info["office_location"] = isset($user_props[PR_OFFICE_LOCATION]) ? $user_props[PR_OFFICE_LOCATION] : ''; |
211
|
|
|
$this->session_info["assistant"] = isset($user_props[PR_ASSISTANT]) ? $user_props[PR_ASSISTANT] : ''; |
212
|
|
|
$this->session_info["assistant_telephone_number"] = isset($user_props[PR_ASSISTANT_TELEPHONE_NUMBER]) ? $user_props[PR_ASSISTANT_TELEPHONE_NUMBER] : ''; |
213
|
|
|
$this->session_info["office_telephone_number"] = isset($user_props[PR_PRIMARY_TELEPHONE_NUMBER]) ? $user_props[PR_PRIMARY_TELEPHONE_NUMBER] : ''; |
214
|
|
|
$this->session_info["business_telephone_number"] = isset($user_props[PR_BUSINESS_TELEPHONE_NUMBER]) ? $user_props[PR_BUSINESS_TELEPHONE_NUMBER] : ''; |
215
|
|
|
$this->session_info["business2_telephone_number"] = isset($user_props[PR_BUSINESS2_TELEPHONE_NUMBER]) ? $user_props[PR_BUSINESS2_TELEPHONE_NUMBER] : ''; |
216
|
|
|
$this->session_info["primary_fax_number"] = isset($user_props[PR_PRIMARY_FAX_NUMBER]) ? $user_props[PR_PRIMARY_FAX_NUMBER] : ''; |
217
|
|
|
$this->session_info["home_telephone_number"] = isset($user_props[PR_HOME_TELEPHONE_NUMBER]) ? $user_props[PR_HOME_TELEPHONE_NUMBER] : ''; |
218
|
|
|
$this->session_info["home2_telephone_number"] = isset($user_props[PR_HOME2_TELEPHONE_NUMBER]) ? $user_props[PR_HOME2_TELEPHONE_NUMBER] : ''; |
219
|
|
|
$this->session_info["mobile_telephone_number"] = isset($user_props[PR_MOBILE_TELEPHONE_NUMBER]) ? $user_props[PR_MOBILE_TELEPHONE_NUMBER] : ''; |
220
|
|
|
$this->session_info["pager_telephone_number"] = isset($user_props[PR_PAGER_TELEPHONE_NUMBER]) ? $user_props[PR_PAGER_TELEPHONE_NUMBER] : ''; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
$this->userDataRetrieved = true; |
|
|
|
|
224
|
|
|
} |
225
|
|
|
catch (MAPIException $e) { |
226
|
|
|
$result = $e->getCode(); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
return $result; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Get MAPI session object. |
234
|
|
|
* |
235
|
|
|
* @return mapisession Current MAPI session |
236
|
|
|
*/ |
237
|
|
|
public function getSession() { |
238
|
|
|
return $this->session; |
|
|
|
|
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Set MAPI session object. |
243
|
|
|
* |
244
|
|
|
* @param mapisession The MAPI session |
245
|
|
|
* @param mixed $session |
246
|
|
|
*/ |
247
|
|
|
public function setSession($session) { |
248
|
|
|
$this->session = $session; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Get MAPI addressbook object. |
253
|
|
|
* |
254
|
|
|
* @param bool $providerless When set to true it will return an addressbook resource |
255
|
|
|
* without any Contact Provider set on it, defaults to false |
256
|
|
|
* @param bool $loadSharedContactsProvider when set to true it denotes that shared folders are |
257
|
|
|
* required to be configured to load the contacts from |
258
|
|
|
* |
259
|
|
|
* @return mixed An addressbook object to be used with mapi_ab_* or an error code |
260
|
|
|
*/ |
261
|
|
|
public function getAddressbook($providerless = false, $loadSharedContactsProvider = false) { |
262
|
|
|
if ($providerless) { |
263
|
|
|
try { |
264
|
|
|
return mapi_openaddressbook($this->session); |
265
|
|
|
} |
266
|
|
|
catch (MAPIException $e) { |
267
|
|
|
return $e->getCode(); |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$result = NOERROR; |
272
|
|
|
|
273
|
|
|
if ($this->ab === false) { |
|
|
|
|
274
|
|
|
$this->setupContactProviderAddressbook($loadSharedContactsProvider); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
try { |
278
|
|
|
if ($this->ab === false) { |
|
|
|
|
279
|
|
|
$this->ab = mapi_openaddressbook($this->session); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
if ($this->ab !== false) { |
|
|
|
|
283
|
|
|
$result = $this->ab; |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
catch (MAPIException $e) { |
287
|
|
|
$result = $e->getCode(); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
return $result; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Get logon status |
295
|
|
|
* NOTE: This function only exists for backward compatibility with older plugins. |
296
|
|
|
* Currently the preferred way to check if a user is logged in, is using |
297
|
|
|
* the isAuthenticated() method of WebAppAuthentication. |
298
|
|
|
* |
299
|
|
|
* @return bool true on logged on, false on not logged on |
300
|
|
|
*/ |
301
|
|
|
public function isLoggedOn() { |
302
|
|
|
trigger_error("isLoggedOn is deprecated, use WebAppAuthentication::isAuthenticated()", E_USER_NOTICE); |
303
|
|
|
|
304
|
|
|
return WebAppAuthentication::isAuthenticated(); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Get current session id. |
309
|
|
|
* |
310
|
|
|
* @deprecated 2.2.0 This function only exists for backward compatibility with |
311
|
|
|
* older plugins that want to send the session id as a GET parameter with |
312
|
|
|
* requests that they make to grommunio.php. The script grommunio.php does not |
313
|
|
|
* expect this parameter anymore, but plugins that are not updated might |
314
|
|
|
* still call this function. |
315
|
|
|
* |
316
|
|
|
* @return string Always empty |
317
|
|
|
*/ |
318
|
|
|
public function getSessionID() { |
319
|
|
|
return ''; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Get current user entryid. |
324
|
|
|
* |
325
|
|
|
* @return string Current user's entryid |
326
|
|
|
*/ |
327
|
|
|
public function getUserEntryID() { |
328
|
|
|
$this->retrieveUserData(); |
329
|
|
|
return $this->session_info["userentryid"] ?? ''; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Get current username. |
334
|
|
|
* |
335
|
|
|
* @return string Current user's username (equal to username passed in logon() ) |
336
|
|
|
*/ |
337
|
|
|
public function getUserName() { |
338
|
|
|
$encryptionStore = EncryptionStore::getInstance(); |
339
|
|
|
return $encryptionStore->get('username') ? $encryptionStore->get('username') : ''; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Get current user's full name. |
344
|
|
|
* |
345
|
|
|
* @return string User's full name |
346
|
|
|
*/ |
347
|
|
|
public function getFullName() { |
348
|
|
|
$this->retrieveUserData(); |
349
|
|
|
return array_key_exists("fullname", $this->session_info) ? $this->session_info["fullname"] : false; |
|
|
|
|
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Get current user's smtp address. |
354
|
|
|
* |
355
|
|
|
* @return string User's smtp address |
356
|
|
|
*/ |
357
|
|
|
public function getSMTPAddress() { |
358
|
|
|
$this->retrieveUserData(); |
359
|
|
|
return array_key_exists("smtpaddress", $this->session_info) ? $this->session_info["smtpaddress"] : false; |
|
|
|
|
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Get current user's email address. |
364
|
|
|
* |
365
|
|
|
* @return string User's email address |
366
|
|
|
*/ |
367
|
|
|
public function getEmailAddress() { |
368
|
|
|
$this->retrieveUserData(); |
369
|
|
|
return array_key_exists("emailaddress", $this->session_info) ? $this->session_info["emailaddress"] : false; |
|
|
|
|
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Get current user's image from the LDAP server. |
374
|
|
|
* |
375
|
|
|
* @return string A base64 encoded string (data url) |
376
|
|
|
*/ |
377
|
|
|
public function getUserImage() { |
378
|
|
|
$this->retrieveUserData(); |
379
|
|
|
return array_key_exists("userimage", $this->session_info) ? $this->session_info["userimage"] : false; |
|
|
|
|
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
public function setUserImage($user_image) { |
383
|
|
|
if ($this->userDataRetrieved && is_array($this->session_info)) { |
384
|
|
|
$this->session_info["userimage"] = $user_image; |
385
|
|
|
} |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
public function getGivenName() { |
389
|
|
|
$this->retrieveUserData(); |
390
|
|
|
return array_key_exists("given_name", $this->session_info) ? $this->session_info["given_name"] : false; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
public function getInitials() { |
394
|
|
|
$this->retrieveUserData(); |
395
|
|
|
return array_key_exists("initials", $this->session_info) ? $this->session_info["initials"] : false; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
public function getSurname() { |
399
|
|
|
$this->retrieveUserData(); |
400
|
|
|
return array_key_exists("surname", $this->session_info) ? $this->session_info["surname"] : false; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
public function getStreetAddress() { |
404
|
|
|
$this->retrieveUserData(); |
405
|
|
|
return array_key_exists("street_address", $this->session_info) ? $this->session_info["street_address"] : false; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
public function getLocality() { |
409
|
|
|
$this->retrieveUserData(); |
410
|
|
|
return array_key_exists("locality", $this->session_info) ? $this->session_info["locality"] : false; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
public function getStateOrProvince() { |
414
|
|
|
$this->retrieveUserData(); |
415
|
|
|
return array_key_exists("state_or_province", $this->session_info) ? $this->session_info["state_or_province"] : false; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
public function getPostalCode() { |
419
|
|
|
$this->retrieveUserData(); |
420
|
|
|
return array_key_exists("postal_code", $this->session_info) ? $this->session_info["postal_code"] : false; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
public function getCountry() { |
424
|
|
|
$this->retrieveUserData(); |
425
|
|
|
return array_key_exists("country", $this->session_info) ? $this->session_info["country"] : false; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
public function getTitle() { |
429
|
|
|
$this->retrieveUserData(); |
430
|
|
|
return array_key_exists("title", $this->session_info) ? $this->session_info["title"] : false; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
public function getCompanyName() { |
434
|
|
|
$this->retrieveUserData(); |
435
|
|
|
return array_key_exists("company_name", $this->session_info) ? $this->session_info["company_name"] : false; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
public function getDepartmentName() { |
439
|
|
|
$this->retrieveUserData(); |
440
|
|
|
return array_key_exists("department_name", $this->session_info) ? $this->session_info["department_name"] : false; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
public function getOfficeLocation() { |
444
|
|
|
$this->retrieveUserData(); |
445
|
|
|
return array_key_exists("office_location", $this->session_info) ? $this->session_info["office_location"] : false; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
public function getAssistant() { |
449
|
|
|
$this->retrieveUserData(); |
450
|
|
|
return array_key_exists("assistant", $this->session_info) ? $this->session_info["assistant"] : false; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
public function getAssistantTelephoneNumber() { |
454
|
|
|
$this->retrieveUserData(); |
455
|
|
|
return array_key_exists("assistant_telephone_number", $this->session_info) ? $this->session_info["assistant_telephone_number"] : false; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
public function getOfficeTelephoneNumber() { |
459
|
|
|
$this->retrieveUserData(); |
460
|
|
|
return array_key_exists("office_telephone_number", $this->session_info) ? $this->session_info["office_telephone_number"] : false; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
public function getBusinessTelephoneNumber() { |
464
|
|
|
$this->retrieveUserData(); |
465
|
|
|
return array_key_exists("business_telephone_number", $this->session_info) ? $this->session_info["business_telephone_number"] : false; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
public function getBusiness2TelephoneNumber() { |
469
|
|
|
$this->retrieveUserData(); |
470
|
|
|
return array_key_exists("business2_telephone_number", $this->session_info) ? $this->session_info["business2_telephone_number"] : false; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
public function getPrimaryFaxNumber() { |
474
|
|
|
$this->retrieveUserData(); |
475
|
|
|
return array_key_exists("primary_fax_number", $this->session_info) ? $this->session_info["primary_fax_number"] : false; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
public function getHomeTelephoneNumber() { |
479
|
|
|
$this->retrieveUserData(); |
480
|
|
|
return array_key_exists("home_telephone_number", $this->session_info) ? $this->session_info["home_telephone_number"] : false; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
public function getHome2TelephoneNumber() { |
484
|
|
|
$this->retrieveUserData(); |
485
|
|
|
return array_key_exists("home2_telephone_number", $this->session_info) ? $this->session_info["home2_telephone_number"] : false; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
public function getMobileTelephoneNumber() { |
489
|
|
|
$this->retrieveUserData(); |
490
|
|
|
return array_key_exists("mobile_telephone_number", $this->session_info) ? $this->session_info["mobile_telephone_number"] : false; |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
public function getPagerTelephoneNumber() { |
494
|
|
|
$this->retrieveUserData(); |
495
|
|
|
return array_key_exists("pager_telephone_number", $this->session_info) ? $this->session_info["pager_telephone_number"] : false; |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Get currently disabled features for the user. |
500
|
|
|
* |
501
|
|
|
* @return array an disabled features list |
502
|
|
|
*/ |
503
|
|
|
public function getDisabledFeatures() { |
504
|
|
|
$userProps = mapi_getprops($this->getUser(), [PR_EC_DISABLED_FEATURES]); |
505
|
|
|
return isset($userProps[PR_EC_DISABLED_FEATURES]) ? $userProps[PR_EC_DISABLED_FEATURES] : []; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Checks whether the user is enabled for grommunio-web. |
510
|
|
|
* |
511
|
|
|
* @return bool |
512
|
|
|
*/ |
513
|
|
|
public function isGwebEnabled() { |
514
|
|
|
$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_EC_ENABLED_FEATURES_L]); |
515
|
|
|
return $store_props[PR_EC_ENABLED_FEATURES_L] & UP_WEB; |
|
|
|
|
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* @return bool true if webapp is disabled feature else return false |
520
|
|
|
*/ |
521
|
|
|
public function isWebappDisableAsFeature() { |
522
|
|
|
return !$this->isGwebEnabled() || array_search('webapp', $this->getDisabledFeatures()) !== false; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Magic method to get properties from the session_info. When a method of this class if called |
527
|
|
|
* and there is no method of this name defined this function will be called |
528
|
|
|
* It creates getter methods for the properties stored in $session_info using the following syntax: |
529
|
|
|
* getSomeUserProperty() will look return a property called some_user_property if it exists and |
530
|
|
|
* throw an exception otherwise. |
531
|
|
|
* |
532
|
|
|
* @param string $methodName The name of the method that was called |
533
|
|
|
* @param array $arguments The arguments that were passed in the call |
534
|
|
|
* |
535
|
|
|
* @return string The requested property if it exists |
536
|
|
|
* |
537
|
|
|
* @throws Exception |
538
|
|
|
*/ |
539
|
|
|
public function __call($methodName, $arguments) { |
540
|
|
|
if (!preg_match('/^get(.+)$/', $methodName, $matches)) { |
541
|
|
|
// We don't know this function, so let's throw an error |
542
|
|
|
throw new Exception('Method ' . $methodName . ' does not exist'); |
543
|
|
|
} |
544
|
|
|
$this->retrieveUserData(); |
545
|
|
|
$propertyName = strtolower(preg_replace('/([^A-Z])([A-Z])/', '$1_$2', $matches[1])); |
546
|
|
|
if (!array_key_exists($propertyName, $this->session_info)) { |
547
|
|
|
// We don't know this function, so let's throw an error |
548
|
|
|
throw new Exception('Method ' . $methodName . ' does not exist ' . $propertyName); |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
return $this->session_info[$propertyName]; |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* Returns a hash with information about the user that is logged in. |
556
|
|
|
* |
557
|
|
|
* @return array |
558
|
|
|
*/ |
559
|
|
|
public function getUserInfo() { |
560
|
|
|
return [ |
561
|
|
|
'username' => $this->getUserName(), |
562
|
|
|
'fullname' => $this->getFullName(), |
563
|
|
|
'entryid' => bin2hex($this->getUserEntryid()), |
564
|
|
|
'email_address' => $this->getEmailAddress(), |
565
|
|
|
'smtp_address' => $this->getSMTPAddress(), |
566
|
|
|
'search_key' => bin2hex($this->getSearchKey()), |
567
|
|
|
'user_image' => $this->getUserImage(), |
568
|
|
|
'given_name' => $this->getGivenName(), |
569
|
|
|
'initials' => $this->getInitials(), |
570
|
|
|
'surname' => $this->getSurname(), |
571
|
|
|
'street_address' => $this->getStreetAddress(), |
572
|
|
|
'locality' => $this->getLocality(), |
573
|
|
|
'state_or_province' => $this->getStateOrProvince(), |
574
|
|
|
'postal_code' => $this->getPostalCode(), |
575
|
|
|
'country' => $this->getCountry(), |
576
|
|
|
'title' => $this->getTitle(), |
577
|
|
|
'company_name' => $this->getCompanyName(), |
578
|
|
|
'department_name' => $this->getDepartmentName(), |
579
|
|
|
'office_location' => $this->getOfficeLocation(), |
580
|
|
|
'assistant' => $this->getAssistant(), |
581
|
|
|
'assistant_telephone_number' => $this->getAssistantTelephoneNumber(), |
582
|
|
|
'office_telephone_number' => $this->getOfficeTelephoneNumber(), |
583
|
|
|
'business_telephone_number' => $this->getBusinessTelephoneNumber(), |
584
|
|
|
'business2_telephone_number' => $this->getBusiness2TelephoneNumber(), |
585
|
|
|
'primary_fax_number' => $this->getPrimaryFaxNumber(), |
586
|
|
|
'home_telephone_number' => $this->getHomeTelephoneNumber(), |
587
|
|
|
'home2_telephone_number' => $this->getHome2TelephoneNumber(), |
588
|
|
|
'mobile_telephone_number' => $this->getMobileTelephoneNumber(), |
589
|
|
|
'pager_telephone_number' => $this->getPagerTelephoneNumber(), |
590
|
|
|
]; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
/** |
594
|
|
|
* Get current user's search key. |
595
|
|
|
* |
596
|
|
|
* @return string Current user's searchkey |
597
|
|
|
*/ |
598
|
|
|
public function getSearchKey() { |
599
|
|
|
$this->retrieveUserData(); |
600
|
|
|
return $this->session_info["searchkey"] ?? ''; |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* Get the message stores from the message store table from your session. Standard stores |
605
|
|
|
* like the default store and the public store are made them easily accessible through the |
606
|
|
|
* defaultstore and publicStore properties. |
607
|
|
|
*/ |
608
|
|
|
public function loadMessageStoresFromSession() { |
609
|
|
|
$storestables = mapi_getmsgstorestable($this->session); |
610
|
|
|
$rows = mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]); |
611
|
|
|
foreach ($rows as $row) { |
612
|
|
|
if (!$row[PR_ENTRYID]) { |
613
|
|
|
continue; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) { |
617
|
|
|
$this->defaultstore = $row[PR_ENTRYID]; |
618
|
|
|
} |
619
|
|
|
elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { |
620
|
|
|
$this->publicStore = $row[PR_ENTRYID]; |
621
|
|
|
} |
622
|
|
|
elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) { |
623
|
|
|
$eidObj = $GLOBALS["entryid"]->createMsgStoreEntryIdObj($row[PR_ENTRYID]); |
624
|
|
|
if (isset($eidObj['MailboxDN'])) { |
625
|
|
|
$this->openMessageStore($row[PR_ENTRYID], strtolower($eidObj['MailboxDN'])); |
626
|
|
|
} |
627
|
|
|
} |
628
|
|
|
} |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
/** |
632
|
|
|
* Get the current user's default message store. |
633
|
|
|
* |
634
|
|
|
* The store is opened only once, subsequent calls will return the previous store object |
635
|
|
|
* |
636
|
|
|
* @param bool reopen force re-open |
|
|
|
|
637
|
|
|
* @param mixed $reopen |
638
|
|
|
* |
639
|
|
|
* @return mapistore User's default message store object |
|
|
|
|
640
|
|
|
*/ |
641
|
|
|
public function getDefaultMessageStore($reopen = false) { |
642
|
|
|
// Return cached default store if we have one |
643
|
|
|
if (!$reopen && isset($this->defaultstore, $this->stores[$this->defaultstore])) { |
644
|
|
|
return $this->stores[$this->defaultstore]; |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
$this->loadMessageStoresFromSession(); |
648
|
|
|
|
649
|
|
|
return $this->openMessageStore($this->defaultstore, 'Default store'); |
650
|
|
|
} |
651
|
|
|
|
652
|
|
|
/** |
653
|
|
|
* The default messagestore entryid. |
654
|
|
|
* |
655
|
|
|
* @return string the entryid of the default messagestore |
656
|
|
|
*/ |
657
|
|
|
public function getDefaultMessageStoreEntryId() { |
658
|
|
|
if (!isset($this->defaultstore)) { |
659
|
|
|
$this->loadMessageStoresFromSession(); |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
return bin2hex($this->defaultstore); |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* Get single store and it's archive store as well if we are openig full store. |
667
|
|
|
* |
668
|
|
|
* @param object $store the store of the user |
669
|
|
|
* @param array $storeOptions contains folder_type of which folder to open |
670
|
|
|
* It is mapped to username, If folder_type is 'all' (i.e. Open Entire Inbox) |
671
|
|
|
* then we will open full store and it's archived stores. |
672
|
|
|
* @param string $username The username |
673
|
|
|
* |
674
|
|
|
* @return array storeArray The array of stores containing user's store and archived stores |
675
|
|
|
*/ |
676
|
|
|
public function getSingleMessageStores($store, $storeOptions, $username) { |
677
|
|
|
$storeArray = [$store]; |
678
|
|
|
$archivedStores = []; |
679
|
|
|
|
680
|
|
|
// Get archived stores for user if there's any |
681
|
|
|
if (!empty($username)) { |
682
|
|
|
// Check whether we should open the whole store or just single folders |
683
|
|
|
if (is_array($storeOptions) && isset($storeOptions[$username], $storeOptions[$username]['all'])) { |
684
|
|
|
$archivedStores = $this->getArchivedStores($this->resolveStrictUserName($username)); |
685
|
|
|
} |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
foreach ($archivedStores as $archivedStore) { |
689
|
|
|
$storeArray[] = $archivedStore; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
return $storeArray; |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
/** |
696
|
|
|
* Get the public message store. |
697
|
|
|
* |
698
|
|
|
* The store is opened only once, subsequent calls will return the previous store object |
699
|
|
|
* |
700
|
|
|
* @return mapistore Public message store object |
701
|
|
|
*/ |
702
|
|
|
public function getPublicMessageStore() { |
703
|
|
|
// Return cached public store if we have one |
704
|
|
|
if (isset($this->publicStore, $this->stores[$this->publicStore])) { |
705
|
|
|
return $this->stores[$this->publicStore]; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
$this->loadMessageStoresFromSession(); |
709
|
|
|
|
710
|
|
|
return $this->openMessageStore($this->publicStore, 'Public store'); |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
/** |
714
|
|
|
* Get all message stores currently open in the session. |
715
|
|
|
* |
716
|
|
|
* @return array Associative array with entryid -> mapistore of all open stores (private, public, delegate) |
717
|
|
|
*/ |
718
|
|
|
public function getAllMessageStores() { |
719
|
|
|
$this->getDefaultMessageStore(); |
720
|
|
|
$this->getPublicMessageStore(); |
721
|
|
|
$this->getArchivedStores($this->getUserEntryID()); |
722
|
|
|
// The cache now contains all the stores in our profile. Next, add the stores |
723
|
|
|
// for other users. |
724
|
|
|
$this->getOtherUserStore(); |
725
|
|
|
|
726
|
|
|
// Just return all the stores in our cache, even if we have some error in mapi |
727
|
|
|
return $this->stores; |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
/** |
731
|
|
|
* Open the message store with entryid $entryid. |
732
|
|
|
* |
733
|
|
|
* @param string $entryid string representation of the binary entryid of the store |
734
|
|
|
* @param string $name The name of the store. Will be logged when opening fails. |
735
|
|
|
* |
736
|
|
|
* @return mapistore The opened store on success, false otherwise |
737
|
|
|
*/ |
738
|
|
|
public function openMessageStore($entryid, $name = '') { |
739
|
|
|
// Check the cache before opening |
740
|
|
|
foreach ($this->stores as $storeEntryId => $storeObj) { |
741
|
|
|
if ($GLOBALS["entryid"]->compareEntryIds(bin2hex($entryid), bin2hex($storeEntryId))) { |
742
|
|
|
return $storeObj; |
743
|
|
|
} |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
try { |
747
|
|
|
$store = mapi_openmsgstore($this->session, $entryid); |
748
|
|
|
$store_props = mapi_getprops($store, [PR_ENTRYID]); |
749
|
|
|
$entryid = $store_props[PR_ENTRYID]; |
750
|
|
|
|
751
|
|
|
// Cache the store for later use |
752
|
|
|
$this->stores[$entryid] = $store; |
753
|
|
|
$this->userstores[$name] = $entryid; |
754
|
|
|
} |
755
|
|
|
catch (MAPIException $e) { |
756
|
|
|
error_log('Failed to open store. ' . $this->session_info["username"] . |
757
|
|
|
' requested ' . bin2hex($entryid) . ($name ? " ({$name})" : '')); |
758
|
|
|
|
759
|
|
|
return $e->getCode(); |
|
|
|
|
760
|
|
|
} |
761
|
|
|
catch (Exception $e) { |
762
|
|
|
// mapi_openmsgstore seems to throw another exception than MAPIException |
763
|
|
|
// sometimes, so we add a safety net. |
764
|
|
|
error_log('Failed to open store. ' . $this->session_info["username"] . |
765
|
|
|
' requested ' . bin2hex($entryid) . ($name ? " ({$name})" : '')); |
766
|
|
|
|
767
|
|
|
return $e->getCode(); |
|
|
|
|
768
|
|
|
} |
769
|
|
|
|
770
|
|
|
return $store; |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
/** |
774
|
|
|
* Searches for the PR_EC_ARCHIVE_SERVERS property of the user of the passed entryid in the |
775
|
|
|
* Addressbook. It will get all his archive store objects and add those to the $this->stores |
776
|
|
|
* list. It will return an array with the list of archive stores where the key is the |
777
|
|
|
* entryid of the store and the value the store resource. |
778
|
|
|
* |
779
|
|
|
* @param string $userEntryid Binary entryid of the user |
780
|
|
|
* |
781
|
|
|
* @return MAPIStore[] List of store resources with the key being the entryid of the store |
782
|
|
|
*/ |
783
|
|
|
public function getArchivedStores($userEntryid) { |
784
|
|
|
if (!isset($this->archivePropsCache[$userEntryid])) { |
785
|
|
|
$this->archivePropsCache[$userEntryid] = $this->getArchiveProps($userEntryid); |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
$userData = $this->archivePropsCache[$userEntryid]; |
789
|
|
|
|
790
|
|
|
$archiveStores = []; |
791
|
|
|
if (isset($userData[PR_EC_ARCHIVE_SERVERS]) && count($userData[PR_EC_ARCHIVE_SERVERS]) > 0) { |
792
|
|
|
// Get the store of the user, need this for the call to mapi_msgstore_getarchiveentryid() |
793
|
|
|
$userStoreEntryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $userData[PR_ACCOUNT]); |
794
|
|
|
$userStore = mapi_openmsgstore($GLOBALS['mapisession']->getSession(), $userStoreEntryid); |
795
|
|
|
|
796
|
|
|
for ($i = 0; $i < count($userData[PR_EC_ARCHIVE_SERVERS]); ++$i) { |
|
|
|
|
797
|
|
|
try { |
798
|
|
|
// Check if the store exists. It can be that the store archiving has been enabled, but no |
799
|
|
|
// archived store has been created an none can be found in the PR_EC_ARCHIVE_SERVERS property. |
800
|
|
|
$archiveStoreEntryid = mapi_msgstore_getarchiveentryid($userStore, $userData[PR_ACCOUNT], $userData[PR_EC_ARCHIVE_SERVERS][$i]); |
801
|
|
|
$archiveStores[$archiveStoreEntryid] = mapi_openmsgstore($GLOBALS['mapisession']->getSession(), $archiveStoreEntryid); |
802
|
|
|
// Add the archive store to the list |
803
|
|
|
$this->stores[$archiveStoreEntryid] = $archiveStores[$archiveStoreEntryid]; |
804
|
|
|
} |
805
|
|
|
catch (MAPIException $e) { |
806
|
|
|
$e->setHandled(); |
807
|
|
|
if ($e->getCode() == MAPI_E_UNKNOWN_ENTRYID) { |
808
|
|
|
dump('Failed to load archive store as entryid is not valid' . $e->getDisplayMessage()); |
809
|
|
|
} |
810
|
|
|
elseif ($e->getCode() == MAPI_E_NOT_FOUND) { |
811
|
|
|
// The corresponding store couldn't be found, print an error to the log. |
812
|
|
|
dump('Corresponding archive store couldn\'t be found' . $e->getDisplayMessage()); |
813
|
|
|
} |
814
|
|
|
else { |
815
|
|
|
dump('Failed to load archive store' . $e->getDisplayMessage()); |
816
|
|
|
} |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
return $archiveStores; |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
/** |
825
|
|
|
* @param string $userEntryid binary entryid of the user |
826
|
|
|
* |
827
|
|
|
* @return array address Archive Properties of the user |
828
|
|
|
*/ |
829
|
|
|
private function getArchiveProps($userEntryid) { |
830
|
|
|
$ab = $this->getAddressbook(); |
831
|
|
|
$abitem = mapi_ab_openentry($ab, $userEntryid); |
832
|
|
|
|
833
|
|
|
return mapi_getprops($abitem, [PR_ACCOUNT, PR_EC_ARCHIVE_SERVERS]); |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
/** |
837
|
|
|
* Get all the available shared stores. |
838
|
|
|
* |
839
|
|
|
* The store is opened only once, subsequent calls will return the previous store object |
840
|
|
|
*/ |
841
|
|
|
public function getOtherUserStore() { |
842
|
|
|
$otherusers = $this->retrieveOtherUsersFromSettings(); |
843
|
|
|
$otherUsersStores = []; |
844
|
|
|
|
845
|
|
|
foreach ($otherusers as $username => $folder) { |
846
|
|
|
if (isset($this->userstores[$username])) { |
847
|
|
|
continue; |
848
|
|
|
} |
849
|
|
|
$storeOk = true; |
850
|
|
|
|
851
|
|
|
if (is_array($folder) && !empty($folder)) { |
852
|
|
|
try { |
853
|
|
|
$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username); |
854
|
|
|
|
855
|
|
|
$sharedStore = $this->openMessageStore($user_entryid, $username); |
856
|
|
|
if ($sharedStore === false && $sharedStore === ecLoginPerm && |
857
|
|
|
$sharedStore === MAPI_E_CALL_FAILED && $sharedStore === MAPI_E_NOT_FOUND) { |
858
|
|
|
$storeOk = false; |
859
|
|
|
} |
860
|
|
|
|
861
|
|
|
// Check if an entire store will be loaded, if so load the archive store as well |
862
|
|
|
if ($storeOk && isset($folder['all']) && $folder['all']['folder_type'] == 'all') { |
863
|
|
|
$this->getArchivedStores($this->resolveStrictUserName($username)); |
864
|
|
|
} |
865
|
|
|
} |
866
|
|
|
catch (MAPIException $e) { |
867
|
|
|
if ($e->getCode() == MAPI_E_NOT_FOUND) { |
868
|
|
|
// The user or the corresponding store couldn't be found, |
869
|
|
|
// print an error to the log, and remove the user from the settings. |
870
|
|
|
dump('Failed to load store for user ' . $username . ', user was not found. Removing it from settings.'); |
871
|
|
|
$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true); |
872
|
|
|
} |
873
|
|
|
else { |
874
|
|
|
// That is odd, something else went wrong. Lets not be hasty and preserve |
875
|
|
|
// the user in the settings, but do print something to the log to indicate |
876
|
|
|
// something happened... |
877
|
|
|
dump('Failed to load store for user ' . $username . '. ' . $e->getDisplayMessage()); |
878
|
|
|
} |
879
|
|
|
} |
880
|
|
|
finally { |
881
|
|
|
if (!$storeOk && ($sharedStore == ecLoginPerm || $sharedStore == MAPI_E_NOT_FOUND)) { |
|
|
|
|
882
|
|
|
// The user or the corresponding store couldn't be opened |
883
|
|
|
// (e.g. the user was deleted or permissions revoked), |
884
|
|
|
// print an error to the log, and remove the user from the settings. |
885
|
|
|
dump(sprintf("The user %s failed to load store of the user %s. Removing it from settings.", $this->session_info["username"], $username)); |
886
|
|
|
$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true); |
887
|
|
|
} |
888
|
|
|
} |
889
|
|
|
} |
890
|
|
|
} |
891
|
|
|
|
892
|
|
|
foreach ($this->userstores as $entryid) { |
893
|
|
|
$otherUsersStores[$entryid] = $this->stores[$entryid]; |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
return $otherUsersStores; |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
/** |
900
|
|
|
* Resolve the username strictly by opening that user's store and returning the |
901
|
|
|
* PR_MAILBOX_OWNER_ENTRYID. This can be used for resolving an username without the risk of |
902
|
|
|
* ambiguity since mapi_ab_resolve() does not strictly resolve on the username. |
903
|
|
|
* |
904
|
|
|
* @param string $username The username |
905
|
|
|
* |
906
|
|
|
* @return Binary|int Entryid of the user on success otherwise the hresult error code |
|
|
|
|
907
|
|
|
*/ |
908
|
|
|
public function resolveStrictUserName($username) { |
909
|
|
|
$storeEntryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username); |
910
|
|
|
$store = $this->openMessageStore($storeEntryid, $username); |
911
|
|
|
$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]); |
912
|
|
|
|
913
|
|
|
return $storeProps[PR_MAILBOX_OWNER_ENTRYID]; |
914
|
|
|
} |
915
|
|
|
|
916
|
|
|
/** |
917
|
|
|
* Get other users from settings. |
918
|
|
|
* |
919
|
|
|
* @return array Array of usernames of delegate stores |
920
|
|
|
*/ |
921
|
|
|
public function retrieveOtherUsersFromSettings() { |
922
|
|
|
$other_users = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []); |
923
|
|
|
$result = []; |
924
|
|
|
foreach ($other_users as $username => $folders) { |
925
|
|
|
// No folders are being shared, the store has probably been closed by the user, |
926
|
|
|
// but the username is still lingering in the settings... |
927
|
|
|
if (!isset($folders) || empty($folders)) { |
928
|
|
|
continue; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
$username = strtolower(hex2bin($username)); |
932
|
|
|
if (!isset($result[$username])) { |
933
|
|
|
$result[$username] = []; |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
foreach ($folders as $folder) { |
937
|
|
|
if (is_array($folder)) { |
938
|
|
|
$result[$username][$folder["folder_type"]] = []; |
939
|
|
|
$result[$username][$folder["folder_type"]]["folder_type"] = $folder["folder_type"]; |
940
|
|
|
$result[$username][$folder["folder_type"]]["show_subfolders"] = $folder["show_subfolders"]; |
941
|
|
|
} |
942
|
|
|
} |
943
|
|
|
} |
944
|
|
|
|
945
|
|
|
return $result; |
946
|
|
|
} |
947
|
|
|
|
948
|
|
|
/** |
949
|
|
|
* Add the store of another user to the list of other user stores. |
950
|
|
|
* |
951
|
|
|
* @param string $username The username whose store should be added to the list of other users' stores |
952
|
|
|
* |
953
|
|
|
* @return mapistore The store of the user or false on error; |
954
|
|
|
*/ |
955
|
|
|
public function addUserStore($username) { |
956
|
|
|
$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username); |
957
|
|
|
|
958
|
|
|
if ($user_entryid) { |
959
|
|
|
// mapi_msgstore_createentryid and mapi_getprops(PR_ENTRYID) have different |
960
|
|
|
// values for shared stores, so save the one from mapi_getprops(PR_ENTRYID) |
961
|
|
|
// $this->userstores[$username] = $user_entryid; |
962
|
|
|
|
963
|
|
|
return $this->openMessageStore($user_entryid, $username); |
964
|
|
|
} |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
/** |
968
|
|
|
* Remove the store of another user from the list of other user stores. |
969
|
|
|
* |
970
|
|
|
* @param string $username The username whose store should be deleted from the list of other users' stores |
971
|
|
|
* |
972
|
|
|
* @return string The entryid of the store which was removed |
973
|
|
|
*/ |
974
|
|
|
public function removeUserStore($username) { |
975
|
|
|
// Remove the reference to the store if we had one |
976
|
|
|
if (isset($this->userstores[$username])) { |
977
|
|
|
$entryid = $this->userstores[$username]; |
978
|
|
|
unset($this->userstores[$username], $this->stores[$entryid]); |
979
|
|
|
|
980
|
|
|
return $entryid; |
981
|
|
|
} |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
/** |
985
|
|
|
* Get the store entryid of the specified user. |
986
|
|
|
* |
987
|
|
|
* The store must have been previously added via addUserStores. |
988
|
|
|
* |
989
|
|
|
* @param string $username The username whose store is being looked up |
990
|
|
|
* |
991
|
|
|
* @return string The entryid of the store of the user |
992
|
|
|
*/ |
993
|
|
|
public function getStoreEntryIdOfUser($username) { |
994
|
|
|
return $this->userstores[$username]; |
995
|
|
|
} |
996
|
|
|
|
997
|
|
|
/** |
998
|
|
|
* Get the username of the user store. |
999
|
|
|
* |
1000
|
|
|
* @param string $username the loginname of whom we want to full name |
1001
|
|
|
* |
1002
|
|
|
* @return string the display name of the user |
1003
|
|
|
*/ |
1004
|
|
|
public function getDisplayNameofUser($username) { |
1005
|
|
|
$user_entryid = $this->getStoreEntryIdOfUser($username); |
1006
|
|
|
$store = $this->openMessageStore($user_entryid, $username); |
1007
|
|
|
$props = mapi_getprops($store, [PR_DISPLAY_NAME]); |
1008
|
|
|
|
1009
|
|
|
return str_replace('Inbox - ', '', $props[PR_DISPLAY_NAME]); |
1010
|
|
|
} |
1011
|
|
|
|
1012
|
|
|
/** |
1013
|
|
|
* Get the username of the owner of the specified store. |
1014
|
|
|
* |
1015
|
|
|
* The store must have been previously added via addUserStores. |
1016
|
|
|
* |
1017
|
|
|
* @param string $entryid EntryID of the store |
1018
|
|
|
* |
1019
|
|
|
* @return string Username of the specified store or false if it is not found |
1020
|
|
|
*/ |
1021
|
|
|
public function getUserNameOfStore($entryid) { |
1022
|
|
|
foreach ($this->userstores as $username => $storeentryid) { |
1023
|
|
|
if ($GLOBALS["entryid"]->compareEntryIds(bin2hex($storeentryid), bin2hex($entryid))) { |
1024
|
|
|
return $username; |
1025
|
|
|
} |
1026
|
|
|
} |
1027
|
|
|
|
1028
|
|
|
return false; |
|
|
|
|
1029
|
|
|
} |
1030
|
|
|
|
1031
|
|
|
/** |
1032
|
|
|
* Open a MAPI message using session object. |
1033
|
|
|
* The function is used to open message when we dont' know |
1034
|
|
|
* the specific store and we want to open message using entryid. |
1035
|
|
|
* |
1036
|
|
|
* @param string $entryid entryid of the message |
1037
|
|
|
* |
1038
|
|
|
* @return object MAPI Message |
1039
|
|
|
*/ |
1040
|
|
|
public function openMessage($entryid) { |
1041
|
|
|
return mapi_openentry($this->session, $entryid); |
1042
|
|
|
} |
1043
|
|
|
|
1044
|
|
|
/** |
1045
|
|
|
* Setup the contact provider for the addressbook. It asks getContactFoldersForABContactProvider |
1046
|
|
|
* for the entryids and display names for the contact folders in the user's store. |
1047
|
|
|
* |
1048
|
|
|
* @param bool $loadSharedContactsProvider when set to true it denotes that shared folders are |
1049
|
|
|
* required to be configured to load the contacts from |
1050
|
|
|
*/ |
1051
|
|
|
public function setupContactProviderAddressbook($loadSharedContactsProvider) { |
1052
|
|
|
$profsect = mapi_openprofilesection($GLOBALS['mapisession']->getSession(), pbGlobalProfileSectionGuid); |
1053
|
|
|
if ($profsect) { |
1054
|
|
|
// Get information about all contact folders from own store, shared stores and public store |
1055
|
|
|
$defaultStore = $this->getDefaultMessageStore(); |
1056
|
|
|
$contactFolders = $this->getContactFoldersForABContactProvider($defaultStore); |
1057
|
|
|
|
1058
|
|
|
// include shared contact folders in addressbook if shared contact folders are enabled |
1059
|
|
|
if (ENABLE_SHARED_CONTACT_FOLDERS && $loadSharedContactsProvider) { |
1060
|
|
|
if (empty($this->userstores)) { |
1061
|
|
|
$this->getOtherUserStore(); |
1062
|
|
|
} |
1063
|
|
|
|
1064
|
|
|
$sharedSetting = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []); |
1065
|
|
|
// Find available contact folders from all user stores, one by one. |
1066
|
|
|
foreach ($this->userstores as $username => $storeEntryID) { |
1067
|
|
|
$userContactFolders = []; |
1068
|
|
|
$sharedUserSetting = []; |
1069
|
|
|
$openedUserStore = $this->openMessageStore($storeEntryID, $username); |
1070
|
|
|
|
1071
|
|
|
// Get settings of respective shared folder of given user |
1072
|
|
|
if (array_key_exists(strtolower(bin2hex($username)), $sharedSetting)) { |
1073
|
|
|
$sharedUserSetting = $sharedSetting[strtolower(bin2hex($username))]; |
1074
|
|
|
} |
1075
|
|
|
|
1076
|
|
|
// Only add opened shared folders into addressbook contacts provider. |
1077
|
|
|
// If entire inbox is opened then add each and every contact folders of that particular user. |
1078
|
|
|
if (isset($sharedUserSetting['all'])) { |
1079
|
|
|
$userContactFolders = $this->getContactFoldersForABContactProvider($openedUserStore); |
1080
|
|
|
} |
1081
|
|
|
elseif (isset($sharedUserSetting['contact'])) { |
1082
|
|
|
// Add respective default contact folder which is opened. |
1083
|
|
|
// Get entryid of default contact folder from root. |
1084
|
|
|
$root = mapi_msgstore_openentry($openedUserStore); |
1085
|
|
|
$rootProps = mapi_getprops($root, [PR_IPM_CONTACT_ENTRYID]); |
1086
|
|
|
|
1087
|
|
|
// Just add the default contact folder only. |
1088
|
|
|
$defaultContactFolder = [ |
1089
|
|
|
PR_STORE_ENTRYID => $storeEntryID, |
1090
|
|
|
PR_ENTRYID => $rootProps[PR_IPM_CONTACT_ENTRYID], |
1091
|
|
|
PR_DISPLAY_NAME => _("Contacts"), |
1092
|
|
|
]; |
1093
|
|
|
array_push($userContactFolders, $defaultContactFolder); |
1094
|
|
|
|
1095
|
|
|
// Go for sub folders only if configured in settings |
1096
|
|
|
if ($sharedUserSetting['contact']['show_subfolders'] == true) { |
1097
|
|
|
$subContactFolders = $this->getContactFolders($openedUserStore, $rootProps[PR_IPM_CONTACT_ENTRYID], true); |
|
|
|
|
1098
|
|
|
if (is_array($subContactFolders)) { |
1099
|
|
|
$userContactFolders = array_merge($userContactFolders, $subContactFolders); |
1100
|
|
|
} |
1101
|
|
|
} |
1102
|
|
|
} |
1103
|
|
|
|
1104
|
|
|
// Postfix display name of every contact folder with respective owner name |
1105
|
|
|
// it is mandatory to keep display-name different |
1106
|
|
|
$userStoreProps = mapi_getprops($openedUserStore, [PR_MAILBOX_OWNER_NAME]); |
1107
|
|
|
for ($i = 0,$len = count($userContactFolders); $i < $len; ++$i) { |
1108
|
|
|
$userContactFolders[$i][PR_DISPLAY_NAME] = $userContactFolders[$i][PR_DISPLAY_NAME] . " - " . $userStoreProps[PR_MAILBOX_OWNER_NAME]; |
1109
|
|
|
} |
1110
|
|
|
|
1111
|
|
|
$contactFolders = array_merge($contactFolders, $userContactFolders); |
1112
|
|
|
} |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
|
|
// Include public contact folders in addressbook if public folders and public contacts folders are enabled |
1116
|
|
|
if (ENABLE_PUBLIC_CONTACT_FOLDERS && ENABLE_PUBLIC_FOLDERS) { |
1117
|
|
|
$publicStore = $this->getPublicMessageStore(); |
1118
|
|
|
if ($publicStore !== false) { |
|
|
|
|
1119
|
|
|
$contactFolders = array_merge($contactFolders, $this->getContactFoldersForABContactProvider($publicStore)); |
1120
|
|
|
} |
1121
|
|
|
} |
1122
|
|
|
// TODO: The shared stores are not opened as there still is a bug that does not allow resolving from shared contact folders |
1123
|
|
|
|
1124
|
|
|
// These lists will be used to put set in the profile section |
1125
|
|
|
$contact_store_entryids = []; |
1126
|
|
|
$contact_folder_entryids = []; |
1127
|
|
|
$contact_folder_names = []; |
1128
|
|
|
|
1129
|
|
|
// Create the lists of store entryids, folder entryids and folder names to be added |
1130
|
|
|
// to the profile section |
1131
|
|
|
for ($i = 0, $len = count($contactFolders); $i < $len; ++$i) { |
1132
|
|
|
$contact_store_entryids[] = $contactFolders[$i][PR_STORE_ENTRYID]; |
1133
|
|
|
$contact_folder_entryids[] = $contactFolders[$i][PR_ENTRYID]; |
1134
|
|
|
$contact_folder_names[] = $contactFolders[$i][PR_DISPLAY_NAME]; |
1135
|
|
|
} |
1136
|
|
|
|
1137
|
|
|
if (!empty($contact_store_entryids)) { |
1138
|
|
|
// add the defaults contacts folder in the addressbook hierarchy under 'Contacts Folders' |
1139
|
|
|
mapi_setprops($profsect, [PR_ZC_CONTACT_STORE_ENTRYIDS => $contact_store_entryids, |
1140
|
|
|
PR_ZC_CONTACT_FOLDER_ENTRYIDS => $contact_folder_entryids, |
1141
|
|
|
PR_ZC_CONTACT_FOLDER_NAMES => $contact_folder_names, ]); |
1142
|
|
|
} |
1143
|
|
|
} |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
/** |
1147
|
|
|
* Get the store entryid, folder entryid and display name of the contact folders in the |
1148
|
|
|
* user's store. It returns an array prepared by getContactFolders. |
1149
|
|
|
* |
1150
|
|
|
* @param mapiStore $store The mapi store to look for folders in |
|
|
|
|
1151
|
|
|
* |
1152
|
|
|
* @return array Contact folder information |
1153
|
|
|
*/ |
1154
|
|
|
public function getContactFoldersForABContactProvider($store) { |
1155
|
|
|
$storeProps = mapi_getprops($store, [PR_ENTRYID, PR_MDB_PROVIDER, PR_IPM_SUBTREE_ENTRYID, PR_IPM_PUBLIC_FOLDERS_ENTRYID]); |
1156
|
|
|
$contactFolders = []; |
|
|
|
|
1157
|
|
|
|
1158
|
|
|
try { |
1159
|
|
|
// Only searches one level deep, otherwise deleted contact folders will also be included. |
1160
|
|
|
$contactFolders = $this->getContactFolders($store, $storeProps[PR_IPM_SUBTREE_ENTRYID], false); |
|
|
|
|
1161
|
|
|
} |
1162
|
|
|
catch (Exception $e) { |
1163
|
|
|
return $contactFolders; |
1164
|
|
|
} |
1165
|
|
|
|
1166
|
|
|
// Need to search all the contact-subfolders within first level contact folders. |
1167
|
|
|
$firstLevelHierarchyNodes = $contactFolders; |
1168
|
|
|
foreach ($firstLevelHierarchyNodes as $firstLevelNode) { |
1169
|
|
|
// To search for multiple levels CONVENIENT_DEPTH needs to be passed as well. |
1170
|
|
|
$contactFolders = array_merge($contactFolders, $this->getContactFolders($store, $firstLevelNode[PR_ENTRYID], true)); |
|
|
|
|
1171
|
|
|
} |
1172
|
|
|
|
1173
|
|
|
return $contactFolders; |
1174
|
|
|
} |
1175
|
|
|
|
1176
|
|
|
/** |
1177
|
|
|
* Get the store entryid, folder entryid and display name of the contact folders from within given folder, in the |
1178
|
|
|
* user's store. It provides an array where each item contains the information of a folder |
1179
|
|
|
* formatted like this: |
1180
|
|
|
* Array( |
1181
|
|
|
* PR_STORE_ENTRYID => '1234567890ABCDEF', |
1182
|
|
|
* PR_ENTRYID => '1234567890ABCDEF', |
1183
|
|
|
* PR_DISPLAY_NAME => 'Contact folder' |
1184
|
|
|
* ). |
1185
|
|
|
* |
1186
|
|
|
* @param mapiStore $store The mapi store of the user |
1187
|
|
|
* @param string $folderEntryid EntryID of the folder to look for contact folders in |
1188
|
|
|
* @param int $depthSearch flag to search into all the folder levels |
1189
|
|
|
* |
1190
|
|
|
* @return array an array in which founded contact-folders will be pushed |
1191
|
|
|
*/ |
1192
|
|
|
public function getContactFolders($store, $folderEntryid, $depthSearch) { |
1193
|
|
|
$restriction = [RES_CONTENT, |
1194
|
|
|
[ |
1195
|
|
|
// Fuzzylevel PF_PREFIX also allows IPF.Contact.Custom folders to be included. |
1196
|
|
|
// Otherwise FL_FULLSTRING would only allow IPF.Contact folders. |
1197
|
|
|
FUZZYLEVEL => FL_PREFIX, |
1198
|
|
|
ULPROPTAG => PR_CONTAINER_CLASS, |
1199
|
|
|
VALUE => [ |
1200
|
|
|
PR_CONTAINER_CLASS => "IPF.Contact", |
1201
|
|
|
], |
1202
|
|
|
], |
1203
|
|
|
]; |
1204
|
|
|
|
1205
|
|
|
// Set necessary flag(s) to search considering all the sub folders or not |
1206
|
|
|
$depthFlag = MAPI_DEFERRED_ERRORS; |
1207
|
|
|
if ($depthSearch) { |
1208
|
|
|
$depthFlag |= CONVENIENT_DEPTH; |
1209
|
|
|
} |
1210
|
|
|
|
1211
|
|
|
$hierarchyFolder = mapi_msgstore_openentry($store, $folderEntryid); |
1212
|
|
|
|
1213
|
|
|
// Filter-out contact folders only |
1214
|
|
|
$contactFolderTable = mapi_folder_gethierarchytable($hierarchyFolder, $depthFlag); |
1215
|
|
|
mapi_table_restrict($contactFolderTable, $restriction, TBL_BATCH); |
1216
|
|
|
|
1217
|
|
|
return mapi_table_queryallrows($contactFolderTable, [PR_STORE_ENTRYID, PR_ENTRYID, PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_DEPTH]); |
1218
|
|
|
} |
1219
|
|
|
|
1220
|
|
|
/** |
1221
|
|
|
* Obtains server version from the PR_EC_SERVER_VERSION property. |
1222
|
|
|
*/ |
1223
|
|
|
public function getServerVersion() { |
1224
|
|
|
$props = mapi_getprops($this->getDefaultMessageStore(), [PR_EC_SERVER_VERSION]); |
1225
|
|
|
if (propIsError(PR_EC_SERVER_VERSION, $props) === MAPI_E_NOT_FOUND) { |
1226
|
|
|
return ''; |
1227
|
|
|
} |
1228
|
|
|
|
1229
|
|
|
return $props[PR_EC_SERVER_VERSION]; |
1230
|
|
|
} |
1231
|
|
|
|
1232
|
|
|
/** |
1233
|
|
|
* Checks if the entryid is of the public store. |
1234
|
|
|
* |
1235
|
|
|
* @param string $entryid |
1236
|
|
|
* |
1237
|
|
|
* @return bool true if public store entryid false otherwise |
1238
|
|
|
*/ |
1239
|
|
|
public function isPublicStore($entryid) { |
1240
|
|
|
return $GLOBALS["entryid"]->compareEntryIds(bin2hex($this->publicStore), $entryid); |
1241
|
|
|
} |
1242
|
|
|
} |
1243
|
|
|
|
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..