Passed
Push — master ( 8dcc68...34e8da )
by
unknown
23:44 queued 20:40
created

Grommunio::isGSyncEnabled()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nop 0
dl 0
loc 21
rs 8.8333
c 0
b 0
f 0
nc 7
1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 *
7
 * This is a backend for grommunio. It is an implementation of IBackend and also
8
 * implements ISearchProvider to search in the grommunio system. The backend
9
 * implements IStateMachine as well to save the devices' information in the
10
 * user's store and extends InterProcessData to access Redis.
11
 */
12
13
// include PHP-MAPI classes
14
include_once('mapi/mapi.util.php');
15
include_once('mapi/mapidefs.php');
16
include_once('mapi/mapitags.php');
17
include_once('mapi/mapicode.php');
18
include_once('mapi/mapiguid.php');
19
20
21
//setlocale to UTF-8 in order to support properties containing Unicode characters
22
setlocale(LC_CTYPE, "en_US.UTF-8");
23
24
class Grommunio extends InterProcessData implements IBackend, ISearchProvider, IStateMachine  {
25
    private $mainUser;
26
    private $session;
27
    private $defaultstore;
28
    private $store;
29
    private $storeName;
30
    private $storeCache;
31
    private $notifications;
32
    private $changesSink;
33
    private $changesSinkFolders;
34
    private $changesSinkHierarchyHash;
35
    private $changesSinkStores;
36
    private $wastebasket;
37
    private $addressbook;
38
    private $folderStatCache;
39
    private $impersonateUser;
40
    private $stateFolder;
41
    private $userDeviceData;
42
43
    // KC config parameter for PR_EC_ENABLED_FEATURES / PR_EC_DISABLED_FEATURES
44
    const MOBILE_ENABLED = 'mobile';
45
46
    const MAXAMBIGUOUSRECIPIENTS = 9999;
47
    const FREEBUSYENUMBLOCKS = 50;
48
    const MAXFREEBUSYSLOTS = 32767; // max length of 32k for the MergedFreeBusy element is allowed
49
    const HALFHOURSECONDS = 1800;
50
51
    /**
52
     * Constructor of the grommunio Backend
53
     *
54
     * @access public
55
     */
56
    public function __construct() {
57
        $this->session = false;
58
        $this->store = false;
59
        $this->storeName = false;
60
        $this->storeCache = array();
61
        $this->notifications = false;
62
        $this->changesSink = false;
63
        $this->changesSinkFolders = array();
64
        $this->changesSinkStores = array();
65
        $this->changesSinkHierarchyHash = false;
66
        $this->wastebasket = false;
67
        $this->session = false;
68
        $this->folderStatCache = array();
69
        $this->impersonateUser = false;
70
        $this->stateFolder = null;
71
72
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio using PHP-MAPI version: %s - PHP version: %s", phpversion("mapi"), phpversion()));
73
74
        # Interprocessdata
75
        $this->allocate = 0;
76
        $this->type = "grommunio-sync:userdevices";
77
        $this->userDeviceData = "grommunio-sync:statefoldercache";
78
        parent::__construct();
79
    }
80
81
    /**
82
     * Indicates which StateMachine should be used
83
     *
84
     * @access public
85
     * @return boolean      Grommunio uses own state machine
86
     */
87
    public function GetStateMachine() {
88
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Grommunio which is incompatible with the documented return type boolean.
Loading history...
89
    }
90
91
    /**
92
     * Returns the Grommunio as it implements the ISearchProvider interface
93
     * This could be overwritten by the global configuration
94
     *
95
     * @access public
96
     * @return object       Implementation of ISearchProvider
97
     */
98
    public function GetSearchProvider() {
99
        return $this;
100
    }
101
102
    /**
103
     * Indicates which AS version is supported by the backend.
104
     *
105
     * @access public
106
     * @return string       AS version constant
107
     */
108
    public function GetSupportedASVersion() {
109
        return ZPush::ASV_141;
110
    }
111
112
    /**
113
     * Authenticates the user with the configured grommunio server
114
     *
115
     * @param string        $username
116
     * @param string        $domain
117
     * @param string        $password
118
     *
119
     * @access public
120
     * @return boolean
121
     * @throws AuthenticationRequiredException
122
     */
123
    public function Logon($user, $domain, $pass) {
124
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Trying to authenticate user '%s'..", $user));
125
126
        $this->mainUser = strtolower($user);
127
        // TODO the impersonated user should be passed directly to IBackend->Logon() - ZP-1351
128
        if (Request::GetImpersonatedUser()) {
129
            $this->impersonateUser = strtolower(Request::GetImpersonatedUser());
0 ignored issues
show
Bug introduced by
It seems like Request::GetImpersonatedUser() can also be of type false; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
            $this->impersonateUser = strtolower(/** @scrutinizer ignore-type */ Request::GetImpersonatedUser());
Loading history...
130
        }
131
132
        // check if we are impersonating someone
133
        // $defaultUser will be used for $this->defaultStore
134
        if ($this->impersonateUser !== false) {
135
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonation active - authenticating: '%s' - impersonating '%s'", $this->mainUser, $this->impersonateUser));
0 ignored issues
show
Bug introduced by
$this->impersonateUser of type true is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonation active - authenticating: '%s' - impersonating '%s'", $this->mainUser, /** @scrutinizer ignore-type */ $this->impersonateUser));
Loading history...
136
            $defaultUser = $this->impersonateUser;
137
        }
138
        else {
139
            $defaultUser = $this->mainUser;
140
        }
141
142
        $deviceId = Request::GetDeviceID();
143
144
        try {
145
            // check if notifications are available in php-mapi
146
            if(function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) {
147
                // send grommunio-sync version and user agent to ZCP - ZP-589
148
                if (Utils::CheckMapiExtVersion('7.2.0')) {
149
                    $zpush_version = 'Grommunio-Sync_' . @constant('GROMMUNIOSYNC_VERSION');
150
                    $user_agent = ($deviceId) ? ZPush::GetDeviceManager()->GetUserAgent() : "unknown";
151
                    $this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent);
1 ignored issue
show
Bug introduced by
The function mapi_logon_zarafa was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

151
                    $this->session = @/** @scrutinizer ignore-call */ mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent);
Loading history...
152
                }
153
                else {
154
                    $this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0);
155
                }
156
                $this->notifications = true;
157
            }
158
            // old fashioned session
159
            else {
160
                $this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER);
161
                $this->notifications = false;
162
            }
163
164
            if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
            if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
165
                ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->Logon(): login failed with error code: 0x%X", mapi_last_hresult()));
166
                if (mapi_last_hresult() == MAPI_E_NETWORK_ERROR)
167
                    throw new ServiceUnavailableException("Error connecting to KC (login)");
168
            }
169
        }
170
        catch (MAPIException $ex) {
171
            throw new AuthenticationRequiredException($ex->getDisplayMessage());
172
        }
173
174
        if(!$this->session) {
175
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): logon failed for user '%s'", $this->mainUser));
176
            $this->defaultstore = false;
177
            return false;
178
        }
179
180
        // Get/open default store
181
        $this->defaultstore = $this->openMessageStore($this->mainUser);
182
183
        // To impersonate, we overwrite the defaultstore. We still need to open it before we can do that.
184
        if ($this->impersonateUser) {
185
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonating user '%s'", $defaultUser));
186
            $this->defaultstore = $this->openMessageStore($defaultUser);
0 ignored issues
show
Bug introduced by
It seems like $defaultUser can also be of type true; however, parameter $user of Grommunio::openMessageStore() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
            $this->defaultstore = $this->openMessageStore(/** @scrutinizer ignore-type */ $defaultUser);
Loading history...
187
        }
188
189
        if (mapi_last_hresult() == MAPI_E_FAILONEPROVIDER)
190
            throw new ServiceUnavailableException("Error connecting to KC (open store)");
191
192
        if($this->defaultstore === false)
193
            throw new AuthenticationRequiredException(sprintf("Grommunio->Logon(): User '%s' has no default store", $defaultUser));
194
195
        $this->store = $this->defaultstore;
196
        $this->storeName = $defaultUser;
197
198
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): User '%s' is authenticated%s", $this->mainUser, ($this->impersonateUser ? " impersonating '".$this->impersonateUser."'" : '')));
0 ignored issues
show
Bug introduced by
Are you sure $this->impersonateUser of type true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

198
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): User '%s' is authenticated%s", $this->mainUser, ($this->impersonateUser ? " impersonating '"./** @scrutinizer ignore-type */ $this->impersonateUser."'" : '')));
Loading history...
199
200
        $this->isZPushEnabled();
201
202
        // check if this is a Zarafa 7 store with unicode support
203
        MAPIUtils::IsUnicodeStore($this->store);
0 ignored issues
show
Bug introduced by
$this->store of type true is incompatible with the type MAPIStore expected by parameter $store of MAPIUtils::IsUnicodeStore(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

203
        MAPIUtils::IsUnicodeStore(/** @scrutinizer ignore-type */ $this->store);
Loading history...
204
205
        // open the state folder
206
        $this->getStateFolder($deviceId);
0 ignored issues
show
Bug introduced by
It seems like $deviceId can also be of type false; however, parameter $devid of Grommunio::getStateFolder() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
        $this->getStateFolder(/** @scrutinizer ignore-type */ $deviceId);
Loading history...
207
        return true;
208
    }
209
210
    /**
211
     * Setup the backend to work on a specific store or checks ACLs there.
212
     * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be
213
     * performed on this store (switch operations store).
214
     * If the ACL check is enabled, this operation should just indicate the ACL status on
215
     * the submitted store, without changing the store for operations.
216
     * For the ACL status, the currently logged on user MUST have access rights on
217
     *  - the entire store - admin access if no folderid is sent, or
218
     *  - on a specific folderid in the store (secretary/full access rights)
219
     *
220
     * The ACLcheck MUST fail if a folder of the authenticated user is checked!
221
     *
222
     * @param string        $store              target store, could contain a "domain\user" value
223
     * @param boolean       $checkACLonly       if set to true, Setup() should just check ACLs
224
     * @param string        $folderid           if set, only ACLs on this folderid are relevant
225
     *
226
     * @access public
227
     * @return boolean
228
     */
229
    public function Setup($store, $checkACLonly = false, $folderid = false) {
230
        list($user, $domain) = Utils::SplitDomainUser($store);
231
232
        if (!isset($this->mainUser))
233
            return false;
234
235
        $mainUser = $this->mainUser;
236
        // when impersonating we need to check against the impersonated user
237
        if ($this->impersonateUser) {
238
            $mainUser = $this->impersonateUser;
239
        }
240
241
        if ($user === false)
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
242
            $user = $mainUser;
243
244
        // This is a special case. A user will get his entire folder structure by the foldersync by default.
245
        // The ACL check is executed when an additional folder is going to be sent to the mobile.
246
        // Configured that way the user could receive the same folderid twice, with two different names.
247
        if ($mainUser == $user && $checkACLonly && $folderid && !$this->impersonateUser) {
248
            ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->Setup(): Checking ACLs for folder of the users defaultstore. Fail is forced to avoid folder duplications on mobile.");
249
            return false;
250
        }
251
252
        // get the users store
253
        $userstore = $this->openMessageStore($user);
254
255
        // only proceed if a store was found, else return false
256
        if ($userstore) {
257
            // only check permissions
258
            if ($checkACLonly == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
259
                // check for admin rights
260
                if (!$folderid) {
261
                    if ($user != $this->mainUser) {
262
                        if ($this->impersonateUser) {
263
                            $storeProps = mapi_getprops($userstore, array(PR_IPM_SUBTREE_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

263
                            $storeProps = /** @scrutinizer ignore-call */ mapi_getprops($userstore, array(PR_IPM_SUBTREE_ENTRYID));
Loading history...
264
                            $rights = $this->HasSecretaryACLs($userstore, '', $storeProps[PR_IPM_SUBTREE_ENTRYID]);
0 ignored issues
show
Bug introduced by
$userstore of type true is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

264
                            $rights = $this->HasSecretaryACLs(/** @scrutinizer ignore-type */ $userstore, '', $storeProps[PR_IPM_SUBTREE_ENTRYID]);
Loading history...
265
                            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for secretary ACLs on root folder of impersonated store '%s': '%s'", $user, Utils::PrintAsString($rights)));
266
                        }
267
                        else {
268
                            $zarafauserinfo = @nsp_getuserinfo($this->mainUser);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

268
                            $zarafauserinfo = @/** @scrutinizer ignore-call */ nsp_getuserinfo($this->mainUser);
Loading history...
269
                            $rights = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'])?true:false;
270
                            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for admin ACLs on store '%s': '%s'", $user, Utils::PrintAsString($rights)));
271
                        }
272
                    }
273
                    // the user has always full access to his own store
274
                    else {
275
                        $rights = true;
276
                        ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->Setup(): the user has always full access to his own store");
277
                    }
278
279
280
                    return $rights;
281
                }
282
                // check permissions on this folder
283
                else {
284
                    $rights = $this->HasSecretaryACLs($userstore, $folderid);
285
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for secretary ACLs on '%s' of store '%s': '%s'", $folderid, $user, Utils::PrintAsString($rights)));
286
                    return $rights;
287
                }
288
            }
289
290
            // switch operations store
291
            // this should also be done if called with user = mainuser or user = false
292
            // which means to switch back to the default store
293
            else {
294
                // switch active store
295
                $this->store = $userstore;
296
                $this->storeName = $user;
297
                return true;
298
            }
299
        }
300
        return false;
301
    }
302
303
    /**
304
     * Logs off
305
     * Free/Busy information is updated for modified calendars
306
     * This is done after the synchronization process is completed
307
     *
308
     * @access public
309
     * @return boolean
310
     */
311
    public function Logoff() {
312
        return true;
313
    }
314
315
    /**
316
     * Returns an array of SyncFolder types with the entire folder hierarchy
317
     * on the server (the array itself is flat, but refers to parents via the 'parent' property
318
     *
319
     * provides AS 1.0 compatibility
320
     *
321
     * @access public
322
     * @return array SYNC_FOLDER
323
     */
324
    public function GetHierarchy() {
325
        $folders = array();
326
        $mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
        $mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
        $mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
327
        $storeProps = $mapiprovider->GetStoreProps();
328
329
        // for SYSTEM user open the public folders
330
        if (strtoupper($this->storeName) == "SYSTEM") {
0 ignored issues
show
Bug introduced by
$this->storeName of type boolean is incompatible with the type string expected by parameter $string of strtoupper(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

330
        if (strtoupper(/** @scrutinizer ignore-type */ $this->storeName) == "SYSTEM") {
Loading history...
331
            $rootfolder = mapi_msgstore_openentry($this->store, $storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

331
            $rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID]);
Loading history...
332
        }
333
        else {
334
            $rootfolder = mapi_msgstore_openentry($this->store);
335
        }
336
337
        $rootfolderprops = mapi_getprops($rootfolder, array(PR_SOURCE_KEY));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

337
        $rootfolderprops = /** @scrutinizer ignore-call */ mapi_getprops($rootfolder, array(PR_SOURCE_KEY));
Loading history...
338
339
        $hierarchy =  mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

339
        $hierarchy =  /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
340
        $rows = mapi_table_queryallrows($hierarchy, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

340
        $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE));
Loading history...
341
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): fetched %d folders from MAPI", count($rows)));
342
343
        foreach ($rows as $row) {
344
            // do not display hidden and search folders
345
            if ((isset($row[PR_ATTR_HIDDEN]) && $row[PR_ATTR_HIDDEN]) ||
346
                (isset($row[PR_FOLDER_TYPE]) && $row[PR_FOLDER_TYPE] == FOLDER_SEARCH) ||
347
                // for SYSTEM user $row[PR_PARENT_SOURCE_KEY] == $rootfolderprops[PR_SOURCE_KEY] is true, but we need those folders
348
                (isset($row[PR_PARENT_SOURCE_KEY]) && $row[PR_PARENT_SOURCE_KEY] == $rootfolderprops[PR_SOURCE_KEY] && strtoupper($this->storeName) != "SYSTEM")) {
349
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): ignoring folder '%s' as it's a hidden/search/root folder", (isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "unknown")));
350
                    continue;
351
            }
352
            $folder = $mapiprovider->GetFolder($row);
353
            if ($folder) {
354
                $folders[] = $folder;
355
            }
356
            else {
357
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): ignoring folder '%s' as MAPIProvider->GetFolder() did not return a SyncFolder object", (isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "unknown")));
358
            }
359
        }
360
361
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): processed %d folders, starting parent remap", count($folders)));
362
        // reloop the folders to make sure all parentids are mapped correctly
363
        $dm = ZPush::GetDeviceManager();
364
        foreach ($folders as $folder) {
365
            if ($folder->parentid !== "0") {
366
                // SYSTEM user's parentid points to $rootfolderprops[PR_SOURCE_KEY], but they need to be on the top level
367
                $folder->parentid = (strtoupper($this->storeName) == "SYSTEM" && $folder->parentid == bin2hex($rootfolderprops[PR_SOURCE_KEY])) ? '0' : $dm->GetFolderIdForBackendId($folder->parentid);
368
            }
369
        }
370
371
        return $folders;
372
    }
373
374
    /**
375
     * Returns the importer to process changes from the mobile
376
     * If no $folderid is given, hierarchy importer is expected
377
     *
378
     * @param string        $folderid (opt)
379
     *
380
     * @access public
381
     * @return object(ImportChanges)
382
     */
383
    public function GetImporter($folderid = false) {
384
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter() folderid: '%s'", Utils::PrintAsString($folderid)));
0 ignored issues
show
Bug introduced by
It seems like $folderid can also be of type false; however, parameter $var of Utils::PrintAsString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

384
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter() folderid: '%s'", Utils::PrintAsString(/** @scrutinizer ignore-type */ $folderid)));
Loading history...
385
        if($folderid !== false) {
386
            // check if the user of the current store has permissions to import to this folderid
387
            if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) {
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

387
            if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs(/** @scrutinizer ignore-type */ $this->store, $folderid)) {
Loading history...
388
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid)));
389
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type object.
Loading history...
390
            }
391
            return new ImportChangesICS($this->session, $this->store, hex2bin($folderid));
0 ignored issues
show
Bug introduced by
$this->session of type boolean is incompatible with the type mapisession expected by parameter $session of ImportChangesICS::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

391
            return new ImportChangesICS(/** @scrutinizer ignore-type */ $this->session, $this->store, hex2bin($folderid));
Loading history...
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of ImportChangesICS::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

391
            return new ImportChangesICS($this->session, /** @scrutinizer ignore-type */ $this->store, hex2bin($folderid));
Loading history...
392
        }
393
        else
394
            return new ImportChangesICS($this->session, $this->store);
395
    }
396
397
    /**
398
     * Returns the exporter to send changes to the mobile
399
     * If no $folderid is given, hierarchy exporter is expected
400
     *
401
     * @param string        $folderid (opt)
402
     *
403
     * @access public
404
     * @return object(ExportChanges)
405
     * @throws StatusException
406
     */
407
    public function GetExporter($folderid = false) {
408
        if($folderid !== false) {
409
            // check if the user of the current store has permissions to export from this folderid
410
            if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) {
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

410
            if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs(/** @scrutinizer ignore-type */ $this->store, $folderid)) {
Loading history...
411
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetExporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid)));
412
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type object.
Loading history...
413
            }
414
            return new ExportChangesICS($this->session, $this->store, hex2bin($folderid));
0 ignored issues
show
Bug introduced by
$this->session of type boolean is incompatible with the type mapisession expected by parameter $session of ExportChangesICS::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

414
            return new ExportChangesICS(/** @scrutinizer ignore-type */ $this->session, $this->store, hex2bin($folderid));
Loading history...
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of ExportChangesICS::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

414
            return new ExportChangesICS($this->session, /** @scrutinizer ignore-type */ $this->store, hex2bin($folderid));
Loading history...
415
        }
416
        else
417
            return new ExportChangesICS($this->session, $this->store);
418
    }
419
420
    /**
421
     * Sends an e-mail
422
     * This messages needs to be saved into the 'sent items' folder
423
     *
424
     * @param SyncSendMail  $sm     SyncSendMail object
425
     *
426
     * @access public
427
     * @return boolean
428
     * @throws StatusException
429
     */
430
    public function SendMail($sm) {
431
        // Check if imtomapi function is available and use it to send the mime message.
432
        // It is available since ZCP 7.0.6
433
        // @see http://jira.zarafa.com/browse/ZCP-9508
434
        if (!(function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI'))) {
435
            throw new StatusException("Grommunio->SendMail(): ZCP/KC version is too old, INETMAPI_IMTOMAPI is not available. Install at least ZCP version 7.0.6 or later.", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED, null, LOGLEVEL_FATAL);
436
            return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
437
        }
438
        $mimeLength = strlen($sm->mime);
439
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): RFC822: %d bytes  forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'",
440
                                            $mimeLength, Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag),
441
                                            Utils::PrintAsString((isset($sm->source->folderid) ? $sm->source->folderid : false)),
0 ignored issues
show
Bug introduced by
It seems like IssetNode ? $sm->source->folderid : false can also be of type false; however, parameter $var of Utils::PrintAsString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

441
                                            Utils::PrintAsString(/** @scrutinizer ignore-type */ (isset($sm->source->folderid) ? $sm->source->folderid : false)),
Loading history...
442
                                            Utils::PrintAsString(($sm->saveinsent)), Utils::PrintAsString(isset($sm->replacemime)) ));
443
        if ($mimeLength == 0) {
444
            throw new StatusException("Grommunio->SendMail(): empty mail data", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED);
445
        }
446
447
        $sendMailProps = MAPIMapping::GetSendMailProperties();
448
        $sendMailProps = getPropIdsFromStrings($this->defaultstore, $sendMailProps);
449
450
        // Open the outbox and create the message there
451
        $storeprops = mapi_getprops($this->defaultstore, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"]));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

451
        $storeprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"]));
Loading history...
452
        if(isset($storeprops[$sendMailProps["outboxentryid"]]))
453
            $outbox = mapi_msgstore_openentry($this->defaultstore, $storeprops[$sendMailProps["outboxentryid"]]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

453
            $outbox = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore, $storeprops[$sendMailProps["outboxentryid"]]);
Loading history...
454
455
        if(!$outbox)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $outbox does not seem to be defined for all execution paths leading up to this point.
Loading history...
456
            throw new StatusException(sprintf("Grommunio->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

456
            throw new StatusException(sprintf("Grommunio->SendMail(): No Outbox found or unable to create message: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR);
Loading history...
457
458
        $mapimessage = mapi_folder_createmessage($outbox);
1 ignored issue
show
Bug introduced by
The function mapi_folder_createmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

458
        $mapimessage = /** @scrutinizer ignore-call */ mapi_folder_createmessage($outbox);
Loading history...
459
460
        //message properties to be set
461
        $mapiprops = array();
462
        // only save the outgoing in sent items folder if the mobile requests it
463
        $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]];
464
465
        ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): Use the mapi_inetmapi_imtomapi function");
466
        $ab = mapi_openaddressbook($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

466
        $ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
467
        mapi_inetmapi_imtomapi($this->session, $this->defaultstore, $ab, $mapimessage, $sm->mime, array());
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtomapi was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

467
        /** @scrutinizer ignore-call */ 
468
        mapi_inetmapi_imtomapi($this->session, $this->defaultstore, $ab, $mapimessage, $sm->mime, array());
Loading history...
468
469
        // Set the appSeqNr so that tracking tab can be updated for meeting request updates
470
        // @see http://jira.zarafa.com/browse/ZP-68
471
        $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties();
472
        $meetingRequestProps = getPropIdsFromStrings($this->defaultstore, $meetingRequestProps);
473
        $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"], $sendMailProps["body"], $sendMailProps["html"], $sendMailProps["rtf"], $sendMailProps["rtfinsync"]));
474
475
        // Convert sent message's body to UTF-8 if it was a HTML message.
476
        // @see http://jira.zarafa.com/browse/ZP-505 and http://jira.zarafa.com/browse/ZP-555
477
        if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8 && MAPIUtils::GetNativeBodyType($props) == SYNC_BODYPREFERENCE_HTML) {
478
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): Sent email cpid is not unicode (%d). Set it to unicode and convert email html body.", $props[$sendMailProps["internetcpid"]]));
479
            $mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8;
480
481
            $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML);
482
            $bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml);
483
            $mapiprops[$sendMailProps["html"]] = $bodyHtml;
484
485
            mapi_setprops($mapimessage, $mapiprops);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

485
            /** @scrutinizer ignore-call */ 
486
            mapi_setprops($mapimessage, $mapiprops);
Loading history...
486
        }
487
        if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) {
488
            // search for calendar items using goid
489
            $mr = new Meetingrequest($this->defaultstore, $mapimessage);
490
            $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]);
491
            if (is_array($appointments) && !empty($appointments)) {
492
                $app = mapi_msgstore_openentry($this->defaultstore, $appointments[0]);
493
                $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"]));
494
                if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) {
495
                    $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]];
496
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]]));
497
                }
498
            }
499
        }
500
501
        // Delete the PR_SENT_REPRESENTING_* properties because some android devices
502
        // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and
503
        // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID
504
        // which results in spooler not being able to send the message.
505
        // @see http://jira.zarafa.com/browse/ZP-85
506
        mapi_deleteprops($mapimessage,
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

506
        /** @scrutinizer ignore-call */ 
507
        mapi_deleteprops($mapimessage,
Loading history...
507
            array(  $sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"],
508
                    $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"]));
509
510
        if(isset($sm->source->itemid) && $sm->source->itemid) {
511
            // answering an email in a public/shared folder
512
            // TODO as the store is setup, we should actually user $this->store instead of $this->defaultstore - nevertheless we need to make sure this store is able to send mail (has an outbox)
513
            if (!$this->Setup(ZPush::GetAdditionalSyncFolderStore($sm->source->folderid)))
514
                throw new StatusException(sprintf("Grommunio->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR);
515
516
            $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

516
            $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid));
Loading history...
517
            if ($entryid)
518
                $fwmessage = mapi_msgstore_openentry($this->store, $entryid);
519
520
            if (isset($fwmessage) && $fwmessage) {
521
                // update icon and last_verb when forwarding or replying message
522
                // reply-all (verb 103) is not supported, as we cannot really detect this case
523
                if ($sm->forwardflag) {
524
                    $updateProps = array(
525
                            PR_ICON_INDEX           => 262,
526
                            PR_LAST_VERB_EXECUTED   => 104,
527
                    );
528
                }
529
                elseif ($sm->replyflag) {
530
                    $updateProps = array(
531
                            PR_ICON_INDEX           => 261,
532
                            PR_LAST_VERB_EXECUTED   => 102,
533
                    );
534
                }
535
                if (isset($updateProps)) {
536
                    $updateProps[PR_LAST_VERB_EXECUTION_TIME] = time();
537
                    mapi_setprops($fwmessage, $updateProps);
538
                    mapi_savechanges($fwmessage);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

538
                    /** @scrutinizer ignore-call */ 
539
                    mapi_savechanges($fwmessage);
Loading history...
539
                }
540
541
                // only attach the original message if the mobile does not send it itself
542
                if (!isset($sm->replacemime)) {
543
                    // get message's body in order to append forward or reply text
544
                    if (!isset($body)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $body seems to never exist and therefore isset should always be false.
Loading history...
545
                        $body = MAPIUtils::readPropStream($mapimessage, PR_BODY);
546
                    }
547
                    if (!isset($bodyHtml)) {
548
                        $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML);
549
                    }
550
                    $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"]));
551
                    if($sm->forwardflag) {
552
                        // attach the original attachments to the outgoing message
553
                        $this->copyAttachments($mapimessage, $fwmessage);
554
                    }
555
556
                    // regarding the conversion @see ZP-470
557
                    if (strlen($body) > 0) {
558
                        $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY);
559
                        // if only the old message's cpid is set, convert from old charset to utf-8
560
                        if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) {
561
                            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]]));
562
                            $fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody);
563
                        }
564
                        // otherwise to the general conversion
565
                        else {
566
                            ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): no charset conversion done for plain forwarded message");
567
                            $fwbody = w2u($fwbody);
568
                        }
569
570
                        $mapiprops[$sendMailProps["body"]] = $body."\r\n\r\n".$fwbody;
0 ignored issues
show
Bug introduced by
Are you sure $fwbody of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

570
                        $mapiprops[$sendMailProps["body"]] = $body."\r\n\r\n"./** @scrutinizer ignore-type */ $fwbody;
Loading history...
571
                    }
572
573
                    if (strlen($bodyHtml) > 0) {
574
                        $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML);
575
                        // if only new message's cpid is set, convert to UTF-8
576
                        if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) {
577
                            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]]));
578
                            $fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml);
579
                        }
580
                        // otherwise to the general conversion
581
                        else {
582
                            ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): no charset conversion done for html forwarded message");
583
                            $fwbodyHtml = w2u($fwbodyHtml);
584
                        }
585
586
                        $mapiprops[$sendMailProps["html"]] = $bodyHtml."<br><br>".$fwbodyHtml;
0 ignored issues
show
Bug introduced by
Are you sure $fwbodyHtml of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

586
                        $mapiprops[$sendMailProps["html"]] = $bodyHtml."<br><br>"./** @scrutinizer ignore-type */ $fwbodyHtml;
Loading history...
587
                    }
588
                }
589
            }
590
            else {
591
                // no fwmessage could be opened and we need it because we do not replace mime
592
                if (!isset($sm->replacemime) || $sm->replacemime == false) {
593
                    throw new StatusException(sprintf("Grommunio->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND);
594
                }
595
            }
596
        }
597
598
        mapi_setprops($mapimessage, $mapiprops);
599
        mapi_savechanges($mapimessage);
600
        mapi_message_submitmessage($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_submitmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

600
        /** @scrutinizer ignore-call */ 
601
        mapi_message_submitmessage($mapimessage);
Loading history...
601
        $hr = mapi_last_hresult();
602
603
        if ($hr) {
604
            switch ($hr) {
605
                case MAPI_E_STORE_FULL:
606
                    $code = SYNC_COMMONSTATUS_MAILBOXQUOTAEXCEEDED;
607
                    break;
608
                default:
609
                    $code = SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED;
610
            }
611
            throw new StatusException(sprintf("Grommunio->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", $hr), $code);
612
        }
613
614
        ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): email submitted");
615
        return true;
616
    }
617
618
    /**
619
     * Returns all available data of a single message
620
     *
621
     * @param string            $folderid
622
     * @param string            $id
623
     * @param ContentParameters $contentparameters flag
624
     *
625
     * @access public
626
     * @return object(SyncObject)
627
     * @throws StatusException
628
     */
629
    public function Fetch($folderid, $id, $contentparameters) {
630
        // SEARCH fetches with folderid == false and PR_ENTRYID as ID
631
        if (! $folderid) {
632
            $entryid = hex2bin($id);
633
            $sk = $id;
634
        }
635
        else {
636
            // id might be in the new longid format, so we have to split it here
637
            list($fsk, $sk) = Utils::SplitMessageId($id);
638
            // get the entry id of the message
639
            $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($sk));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

639
            $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($sk));
Loading history...
640
641
        }
642
        if(!$entryid)
643
            throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $sk, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

643
            throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $sk, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
Loading history...
644
645
        // open the message
646
        $message = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

646
        $message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
647
        if(!$message)
648
            throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error, unable to open message: 0x%X", $folderid, $sk, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
649
650
        // convert the mapi message into a SyncObject and return it
651
        $mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

651
        $mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

651
        $mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
652
653
        // override truncation
654
        $contentparameters->SetTruncation(SYNC_TRUNCATION_ALL);
0 ignored issues
show
Bug introduced by
The method SetTruncation() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

654
        $contentparameters->/** @scrutinizer ignore-call */ 
655
                            SetTruncation(SYNC_TRUNCATION_ALL);
Loading history...
655
        // TODO check for body preferences
656
        return $mapiprovider->GetMessage($message, $contentparameters);
657
    }
658
659
    /**
660
     * Returns the waste basket
661
     *
662
     * @access public
663
     * @return string
664
     */
665
    public function GetWasteBasket() {
666
        if ($this->wastebasket) {
667
            return $this->wastebasket;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->wastebasket returns the type true which is incompatible with the documented return type string.
Loading history...
668
        }
669
670
        $storeprops = mapi_getprops($this->defaultstore, array(PR_IPM_WASTEBASKET_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

670
        $storeprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, array(PR_IPM_WASTEBASKET_ENTRYID));
Loading history...
671
        if (isset($storeprops[PR_IPM_WASTEBASKET_ENTRYID])) {
672
            $wastebasket = mapi_msgstore_openentry($this->defaultstore, $storeprops[PR_IPM_WASTEBASKET_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

672
            $wastebasket = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore, $storeprops[PR_IPM_WASTEBASKET_ENTRYID]);
Loading history...
673
            $wastebasketprops = mapi_getprops($wastebasket, array(PR_SOURCE_KEY));
674
            if (isset($wastebasketprops[PR_SOURCE_KEY])) {
675
                $this->wastebasket = bin2hex($wastebasketprops[PR_SOURCE_KEY]);
676
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetWasteBasket(): Got waste basket with id '%s'", $this->wastebasket));
677
                return $this->wastebasket;
678
            }
679
        }
680
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
681
    }
682
683
    /**
684
     * Returns the content of the named attachment as stream
685
     *
686
     * @param string        $attname
687
     * @access public
688
     * @return SyncItemOperationsAttachment
689
     * @throws StatusException
690
     */
691
    public function GetAttachmentData($attname) {
692
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetAttachmentData('%s')", $attname));
693
694
        if(!strpos($attname, ":"))
695
            throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, attachment requested for non-existing item", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
696
697
        list($id, $attachnum, $parentEntryid) = explode(":", $attname);
698
        if (isset($parentEntryid)) {
699
            $this->Setup(ZPush::GetAdditionalSyncFolderStore($parentEntryid));
700
        }
701
702
        $entryid = hex2bin($id);
703
        $message = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

703
        $message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
704
        if(!$message)
705
            throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

705
            throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
Loading history...
706
707
        MAPIUtils::ParseSmime($this->session, $this->defaultstore, $this->getAddressbook(), $message);
0 ignored issues
show
Bug introduced by
$this->session of type boolean is incompatible with the type MAPISession expected by parameter $session of MAPIUtils::ParseSmime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

707
        MAPIUtils::ParseSmime(/** @scrutinizer ignore-type */ $this->session, $this->defaultstore, $this->getAddressbook(), $message);
Loading history...
708
        $attach = mapi_message_openattach($message, $attachnum);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

708
        $attach = /** @scrutinizer ignore-call */ mapi_message_openattach($message, $attachnum);
Loading history...
709
        if(!$attach)
710
            throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open attachment number '%s' with: 0x%X", $attname, $attachnum, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
711
712
        // get necessary attachment props
713
        $attprops = mapi_getprops($attach, array(PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

713
        $attprops = /** @scrutinizer ignore-call */ mapi_getprops($attach, array(PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD));
Loading history...
714
        $attachment = new SyncItemOperationsAttachment();
715
        // check if it's an embedded message and open it in such a case
716
        if (isset($attprops[PR_ATTACH_METHOD]) && $attprops[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG) {
717
            $embMessage = mapi_attach_openobj($attach);
1 ignored issue
show
Bug introduced by
The function mapi_attach_openobj was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

717
            $embMessage = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach);
Loading history...
718
            $addrbook = $this->getAddressbook();
719
            $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1));
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtoinet was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

719
            $stream = /** @scrutinizer ignore-call */ mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1));
Loading history...
720
            // set the default contenttype for this kind of messages
721
            $attachment->contenttype = "message/rfc822";
722
        }
723
        else
724
            $stream = mapi_openproperty($attach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

724
            $stream = /** @scrutinizer ignore-call */ mapi_openproperty($attach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
Loading history...
725
726
        if(!$stream)
727
            throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open attachment data stream: 0x%X", $attname, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
728
729
        // put the mapi stream into a wrapper to get a standard stream
730
        $attachment->data = MAPIStreamWrapper::Open($stream);
731
        if (isset($attprops[PR_ATTACH_MIME_TAG]))
732
            $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG];
733
        elseif (isset($attprops[PR_ATTACH_MIME_TAG_W]))
734
            $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG_W];
735
            //TODO default contenttype
736
        return $attachment;
737
    }
738
739
740
    /**
741
     * Deletes all contents of the specified folder.
742
     * This is generally used to empty the trash (wastebasked), but could also be used on any
743
     * other folder.
744
     *
745
     * @param string        $folderid
746
     * @param boolean       $includeSubfolders      (opt) also delete sub folders, default true
747
     *
748
     * @access public
749
     * @return boolean
750
     * @throws StatusException
751
     */
752
    public function EmptyFolder($folderid, $includeSubfolders = true) {
753
        $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

753
        $folderentryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
Loading history...
754
        if (!$folderentryid)
755
            throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, unable to open folder (no entry id)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
756
        $folder = mapi_msgstore_openentry($this->store, $folderentryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

756
        $folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $folderentryid);
Loading history...
757
758
        if (!$folder)
759
            throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, unable to open parent folder (open entry)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
760
761
        $flags = 0;
762
        if ($includeSubfolders)
763
            $flags = DEL_ASSOCIATED;
764
765
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->EmptyFolder('%s','%s'): emptying folder",$folderid, Utils::PrintAsString($includeSubfolders)));
766
767
        // empty folder!
768
        mapi_folder_emptyfolder($folder, $flags);
1 ignored issue
show
Bug introduced by
The function mapi_folder_emptyfolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

768
        /** @scrutinizer ignore-call */ 
769
        mapi_folder_emptyfolder($folder, $flags);
Loading history...
769
        if (mapi_last_hresult())
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

769
        if (/** @scrutinizer ignore-call */ mapi_last_hresult())
Loading history...
770
            throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, mapi_folder_emptyfolder() failed: 0x%X", $folderid, Utils::PrintAsString($includeSubfolders), mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
771
772
        return true;
773
    }
774
775
    /**
776
     * Processes a response to a meeting request.
777
     * CalendarID is a reference and has to be set if a new calendar item is created
778
     *
779
     * @param string        $requestid      id of the object containing the request
780
     * @param string        $folderid       id of the parent folder of $requestid
781
     * @param string        $response
782
     *
783
     * @access public
784
     * @return string       id of the created/updated calendar obj
785
     * @throws StatusException
786
     */
787
    public function MeetingResponse($requestid, $folderid, $response) {
788
        // Use standard meeting response code to process meeting request
789
        list($fid, $requestid) = Utils::SplitMessageId($requestid);
790
        $reqentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

790
        $reqentryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid));
Loading history...
791
        if (!$reqentryid)
792
            throw new StatusException(sprintf("Grommunio->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

792
            throw new StatusException(sprintf("Grommunio->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
Loading history...
793
794
        $mapimessage = mapi_msgstore_openentry($this->store, $reqentryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

794
        $mapimessage = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $reqentryid);
Loading history...
795
        if(!$mapimessage)
796
            throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, unable to open request message for response 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
797
798
        // ios sends calendar item in MeetingResponse
799
        // @see https://jira.z-hub.io/browse/ZP-1524
800
        $folderClass = ZPush::GetDeviceManager()->GetFolderClassFromCacheByID($fid);
801
        // find the corresponding meeting request
802
        if ($folderClass != 'Email') {
803
            $props = MAPIMapping::GetMeetingRequestProperties();
804
            $props = getPropIdsFromStrings($this->store, $props);
805
806
            $messageprops = mapi_getprops($mapimessage, array($props["goidtag"]));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

806
            $messageprops = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, array($props["goidtag"]));
Loading history...
807
            $goid = $messageprops[$props["goidtag"]];
808
809
            $mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

809
            $mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

809
            $mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
810
            $inboxprops = $mapiprovider->GetInboxProps();
811
            $folder = mapi_msgstore_openentry($this->store, $inboxprops[PR_ENTRYID]);
812
813
            // Find the item by restricting all items to the correct ID
814
            $restrict = array(RES_AND, array(
815
                array(RES_PROPERTY,
816
                    array(
817
                        RELOP => RELOP_EQ,
818
                        ULPROPTAG => $props["goidtag"],
819
                        VALUE => $goid
820
                    )
821
                )
822
            ));
823
824
            $inboxcontents = mapi_folder_getcontentstable($folder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

824
            $inboxcontents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($folder);
Loading history...
825
826
            $rows = mapi_table_queryallrows($inboxcontents, array(PR_ENTRYID), $restrict);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

826
            $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($inboxcontents, array(PR_ENTRYID), $restrict);
Loading history...
827
            if (empty($rows)) {
828
                throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, meeting request not found in the inbox", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
829
            }
830
            ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->MeetingResponse found meeting request in the inbox");
831
            $mapimessage = mapi_msgstore_openentry($this->store, $rows[0][PR_ENTRYID]);
832
            $reqentryid = $rows[0][PR_ENTRYID];
833
        }
834
835
        $meetingrequest = new Meetingrequest($this->store, $mapimessage, $this->session);
836
837
        if(!$meetingrequest->isMeetingRequest())
838
            throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, attempt to respond to non-meeting request", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
839
840
        if($meetingrequest->isLocalOrganiser())
841
            throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, attempt to response to meeting request that we organized", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
842
843
        // Process the meeting response. We don't have to send the actual meeting response
844
        // e-mail, because the device will send it itself. This seems not to be the case
845
        // anymore for the ios devices since at least version 12.4. grommunio-sync will send the
846
        // accepted email in such a case.
847
        // @see https://jira.z-hub.io/browse/ZP-1524
848
        $sendresponse = false;
849
        $deviceType = strtolower(Request::GetDeviceType());
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceType() can also be of type false; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

849
        $deviceType = strtolower(/** @scrutinizer ignore-type */ Request::GetDeviceType());
Loading history...
850
        if ($deviceType == 'iphone' || $deviceType == 'ipad' || $deviceType == 'ipod') {
851
            $matches = array();
852
            if (preg_match("/^Apple-.*?\/(\d{4})\./", Request::GetUserAgent(), $matches) && isset($matches[1]) && $matches[1] >= 1607) {
853
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse: iOS device %s->%s", Request::GetDeviceType(), Request::GetUserAgent()));
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceType() can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

853
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse: iOS device %s->%s", /** @scrutinizer ignore-type */ Request::GetDeviceType(), Request::GetUserAgent()));
Loading history...
854
                $sendresponse = true;
855
            }
856
        }
857
        switch($response) {
858
            case 1:     // accept
859
            default:
860
                $entryid = $meetingrequest->doAccept(false, $sendresponse, false, false, false, false, true); // last true is the $userAction
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $newProposedStartTime of Meetingrequest::doAccept(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

860
                $entryid = $meetingrequest->doAccept(false, $sendresponse, false, /** @scrutinizer ignore-type */ false, false, false, true); // last true is the $userAction
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $newProposedEndTime of Meetingrequest::doAccept(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

860
                $entryid = $meetingrequest->doAccept(false, $sendresponse, false, false, /** @scrutinizer ignore-type */ false, false, true); // last true is the $userAction
Loading history...
861
                break;
862
            case 2:        // tentative
863
                $entryid = $meetingrequest->doAccept(true, $sendresponse, false, false, false, false, true); // last true is the $userAction
864
                break;
865
            case 3:        // decline
866
                $meetingrequest->doDecline(false);
867
                break;
868
        }
869
870
        // F/B will be updated on logoff
871
872
        // We have to return the ID of the new calendar item, so do that here
873
        $calendarid = "";
874
        $calFolderId = "";
875
        if (isset($entryid)) {
876
            $newitem = mapi_msgstore_openentry($this->store, $entryid);
877
            // new item might be in a delegator's store. ActiveSync does not support accepting them.
878
            if (!$newitem) {
879
                throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Object with entryid '%s' was not found in user's store (0x%X). It might be in a delegator's store.", $requestid, $folderid, $response, bin2hex($entryid), mapi_last_hresult()), SYNC_MEETRESPSTATUS_SERVERERROR, null, LOGLEVEL_WARN);
880
            }
881
882
            $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY));
883
            $calendarid = bin2hex($newprops[PR_SOURCE_KEY]);
884
            $calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]);
885
        }
886
887
        // on recurring items, the MeetingRequest class responds with a wrong entryid
888
        if ($requestid == $calendarid) {
889
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): returned calendar id is the same as the requestid - re-searching", $requestid, $folderid, $response));
890
891
            if (empty($props)) {
892
                $props = MAPIMapping::GetMeetingRequestProperties();
893
                $props = getPropIdsFromStrings($this->store, $props);
894
895
                $messageprops = mapi_getprops($mapimessage, Array($props["goidtag"]));
896
                $goid = $messageprops[$props["goidtag"]];
897
            }
898
899
            $items = $meetingrequest->findCalendarItems($goid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $goid does not seem to be defined for all execution paths leading up to this point.
Loading history...
900
901
            if (is_array($items)) {
902
               $newitem = mapi_msgstore_openentry($this->store, $items[0]);
903
               $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY));
904
               $calendarid = bin2hex($newprops[PR_SOURCE_KEY]);
905
               $calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]);
906
               ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): found other calendar entryid", $requestid, $folderid, $response));
907
            }
908
909
            if ($requestid == $calendarid)
910
                throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error finding the accepted meeting response in the calendar", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
911
        }
912
913
        // delete meeting request from Inbox
914
        if ($folderClass == 'Email') {
915
            $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
916
            $folder = mapi_msgstore_openentry($this->store, $folderentryid);
917
        }
918
        mapi_folder_deletemessages($folder, array($reqentryid), 0);
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $folder does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
The function mapi_folder_deletemessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

918
        /** @scrutinizer ignore-call */ 
919
        mapi_folder_deletemessages($folder, array($reqentryid), 0);
Loading history...
919
920
        $prefix = '';
921
        // prepend the short folderid of the target calendar: if available and short ids are used
922
        if ($calFolderId) {
923
            $shortFolderId = ZPush::GetDeviceManager()->GetFolderIdForBackendId($calFolderId);
924
            if ($calFolderId != $shortFolderId) {
925
                $prefix = $shortFolderId . ':';
926
            }
927
        }
928
        return $prefix . $calendarid;
929
    }
930
931
    /**
932
     * Indicates if the backend has a ChangesSink.
933
     * A sink is an active notification mechanism which does not need polling.
934
     * Since Zarafa 7.0.5 such a sink is available.
935
     * The grommunio backend uses this method to initialize the sink with mapi.
936
     *
937
     * @access public
938
     * @return boolean
939
     */
940
    public function HasChangesSink() {
941
        if (!$this->notifications) {
942
            ZLog::Write(LOGLEVEL_DEBUG, "Grommunio->HasChangesSink(): sink is not available");
943
            return false;
944
        }
945
946
        $this->changesSink = @mapi_sink_create();
1 ignored issue
show
Bug introduced by
The function mapi_sink_create was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

946
        $this->changesSink = @/** @scrutinizer ignore-call */ mapi_sink_create();
Loading history...
947
948
        if (! $this->changesSink || mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

948
        if (! $this->changesSink || /** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
949
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasChangesSink(): sink could not be created with  0x%X", mapi_last_hresult()));
950
            return false;
951
        }
952
953
        $this->changesSinkHierarchyHash = $this->getHierarchyHash();
954
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->HasChangesSink(): created - HierarchyHash: %s", $this->changesSinkHierarchyHash));
955
956
        // advise the main store and also to check if the connection supports it
957
        return $this->adviseStoreToSink($this->defaultstore);
958
    }
959
960
    /**
961
     * The folder should be considered by the sink.
962
     * Folders which were not initialized should not result in a notification
963
     * of IBackend->ChangesSink().
964
     *
965
     * @param string        $folderid
966
     *
967
     * @access public
968
     * @return boolean      false if entryid can not be found for that folder
969
     */
970
    public function ChangesSinkInitialize($folderid) {
971
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ChangesSinkInitialize(): folderid '%s'", $folderid));
972
973
        $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

973
        $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
Loading history...
974
        if (!$entryid)
975
            return false;
976
977
        // add entryid to the monitored folders
978
        $this->changesSinkFolders[$entryid] = $folderid;
979
980
        // advise the current store to the sink
981
        return $this->adviseStoreToSink($this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of Grommunio::adviseStoreToSink(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

981
        return $this->adviseStoreToSink(/** @scrutinizer ignore-type */ $this->store);
Loading history...
982
    }
983
984
    /**
985
     * The actual ChangesSink.
986
     * For max. the $timeout value this method should block and if no changes
987
     * are available return an empty array.
988
     * If changes are available a list of folderids is expected.
989
     *
990
     * @param int           $timeout        max. amount of seconds to block
991
     *
992
     * @access public
993
     * @return array
994
     */
995
    public function ChangesSink($timeout = 30) {
996
        // clear the folder stats cache
997
        unset($this->folderStatCache);
998
999
        $notifications = array();
1000
        $hierarchyNotifications = array();
1001
        $sinkresult = @mapi_sink_timedwait($this->changesSink, $timeout * 1000);
1 ignored issue
show
Bug introduced by
The function mapi_sink_timedwait was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1001
        $sinkresult = @/** @scrutinizer ignore-call */ mapi_sink_timedwait($this->changesSink, $timeout * 1000);
Loading history...
1002
1003
        if (!is_array($sinkresult))
1004
            throw new StatusException("Grommunio->ChangesSink(): Sink returned invalid notification, aborting", SyncCollections::OBSOLETE_CONNECTION);
1005
1006
        // reverse array so that the changes on folders are before changes on messages and
1007
        // it's possible to filter such notifications
1008
        $sinkresult = array_reverse($sinkresult, true);
1009
        foreach ($sinkresult as $sinknotif) {
1010
            // add a notification on a folder
1011
            if ($sinknotif['objtype'] == MAPI_FOLDER) {
1012
                $hierarchyNotifications[$sinknotif['entryid']] = IBackend::HIERARCHYNOTIFICATION;
1013
            }
1014
            // change on a message, remove hierarchy notification
1015
            if (isset($sinknotif['parentid']) && $sinknotif['objtype'] == MAPI_MESSAGE && isset($notifications[$sinknotif['parentid']])) {
1016
                unset($hierarchyNotifications[$sinknotif['parentid']]);
1017
            }
1018
1019
            // TODO check if adding $sinknotif['objtype'] = MAPI_MESSAGE wouldn't break anything
1020
            // check if something in the monitored folders changed
1021
            if (isset($sinknotif['parentid']) && array_key_exists($sinknotif['parentid'], $this->changesSinkFolders)) {
1022
                $notifications[] = $this->changesSinkFolders[$sinknotif['parentid']];
1023
            }
1024
            // deletes and moves
1025
            if (isset($sinknotif['oldparentid']) && array_key_exists($sinknotif['oldparentid'], $this->changesSinkFolders)) {
1026
                $notifications[] = $this->changesSinkFolders[$sinknotif['oldparentid']];
1027
            }
1028
        }
1029
1030
        // validate hierarchy notifications by comparing the hierarchy hashes (too many false positives otherwise)
1031
        if (!empty($hierarchyNotifications)) {
1032
            $hash = $this->getHierarchyHash();
1033
            if ($hash !== $this->changesSinkHierarchyHash) {
1034
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ChangesSink() Hierarchy notification, pending validation. New hierarchyHash: %s", $hash));
1035
                $notifications[] = IBackend::HIERARCHYNOTIFICATION;
1036
                $this->changesSinkHierarchyHash = $hash;
1037
            }
1038
        }
1039
        return $notifications;
1040
    }
1041
1042
    /**
1043
     * Applies settings to and gets information from the device.
1044
     *
1045
     * @param SyncObject    $settings (SyncOOF, SyncUserInformation, SyncRightsManagementTemplates possible)
1046
     *
1047
     * @access public
1048
     * @return SyncObject       $settings
1049
     */
1050
    public function Settings($settings) {
1051
        if ($settings instanceof SyncOOF) {
1052
            $this->settingsOOF($settings);
1053
        }
1054
1055
        if ($settings instanceof SyncUserInformation) {
1056
            $this->settingsUserInformation($settings);
1057
        }
1058
1059
        if ($settings instanceof SyncRightsManagementTemplates) {
1060
            $this->settingsRightsManagementTemplates($settings);
1061
        }
1062
1063
        return $settings;
1064
    }
1065
1066
    /**
1067
     * Resolves recipients
1068
     *
1069
     * @param SyncObject        $resolveRecipients
1070
     *
1071
     * @access public
1072
     * @return SyncObject       $resolveRecipients
1073
     */
1074
    public function ResolveRecipients($resolveRecipients) {
1075
        if ($resolveRecipients instanceof SyncResolveRecipients) {
1076
            $resolveRecipients->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
1077
            $resolveRecipients->response = array();
1078
            $resolveRecipientsOptions = new SyncResolveRecipientsOptions();
1079
            $maxAmbiguousRecipients = self::MAXAMBIGUOUSRECIPIENTS;
1080
1081
            if (isset($resolveRecipients->options)) {
1082
                $resolveRecipientsOptions = $resolveRecipients->options;
1083
                // only limit ambiguous recipients if the client requests it.
1084
1085
                if (isset($resolveRecipientsOptions->maxambiguousrecipients) &&
1086
                        $resolveRecipientsOptions->maxambiguousrecipients >= 0 &&
1087
                        $resolveRecipientsOptions->maxambiguousrecipients <= self::MAXAMBIGUOUSRECIPIENTS) {
1088
                    $maxAmbiguousRecipients = $resolveRecipientsOptions->maxambiguousrecipients;
1089
                    ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ResolveRecipients(): The client requested %d max ambiguous recipients to resolve.", $maxAmbiguousRecipients));
1090
                }
1091
            }
1092
1093
            foreach ($resolveRecipients->to as $i => $to) {
1094
                $response = new SyncResolveRecipientsResponse();
1095
                $response->to = $to;
1096
                $response->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
1097
1098
                // do not expand distlists here
1099
                $recipient = $this->resolveRecipient($to, $maxAmbiguousRecipients, false);
1100
                if (is_array($recipient) && !empty($recipient)) {
1101
                    $response->recipientcount = 0;
1102
                    foreach ($recipient as $entry) {
1103
                        if ($entry instanceof SyncResolveRecipient) {
1104
                            // certificates are already set. Unset them if they weren't required.
1105
                            if (!isset($resolveRecipientsOptions->certificateretrieval)) {
1106
                                unset($entry->certificates);
1107
                            }
1108
                            if (isset($resolveRecipientsOptions->availability)) {
1109
                                if (!isset($resolveRecipientsOptions->starttime)) {
1110
                                    // TODO error, the request must include a valid StartTime element value
1111
                                }
1112
                                $entry->availability = $this->getAvailability($to, $entry, $resolveRecipientsOptions);
1113
                            }
1114
                            if (isset($resolveRecipientsOptions->picture)) {
1115
                                // TODO implement picture retrieval of the recipient
1116
                            }
1117
                            $response->recipientcount++;
1118
                            $response->recipient[] = $entry;
1119
                        }
1120
                        elseif (is_int($recipient)) {
1121
                            $response->status = $recipient;
1122
                        }
1123
                    }
1124
                }
1125
1126
                $resolveRecipients->response[$i] = $response;
1127
            }
1128
1129
            return $resolveRecipients;
1130
        }
1131
1132
        ZLog::Write(LOGLEVEL_WARN, "Grommunio->ResolveRecipients(): Not a valid SyncResolveRecipients object.");
1133
        // return a SyncResolveRecipients object so that sync doesn't fail
1134
        $r = new SyncResolveRecipients();
1135
        $r->status = SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR;
1136
        return $r;
1137
    }
1138
1139
    /**----------------------------------------------------------------------------------------------------------
1140
     * Implementation of the ISearchProvider interface
1141
     */
1142
1143
    /**
1144
     * Indicates if a search type is supported by this SearchProvider
1145
     * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented
1146
     *
1147
     * @param string        $searchtype
1148
     *
1149
     * @access public
1150
     * @return boolean
1151
     */
1152
    public function SupportsType($searchtype) {
1153
        return ($searchtype == ISearchProvider::SEARCH_GAL) || ($searchtype == ISearchProvider::SEARCH_MAILBOX);
1154
    }
1155
1156
    /**
1157
     * Searches the GAB of Grommunio
1158
     * Can be overwritten globally by configuring a SearchBackend
1159
     *
1160
     * @param string                        $searchquery        string to be searched for
1161
     * @param string                        $searchrange        specified searchrange
1162
     * @param SyncResolveRecipientsPicture  $searchpicture      limitations for picture
1163
     *
1164
     * @access public
1165
     * @return array        search results
1166
     * @throws StatusException
1167
     */
1168
    public function GetGALSearchResults($searchquery, $searchrange, $searchpicture) {
1169
        // only return users whose displayName or the username starts with $name
1170
        //TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
1171
        $addrbook = $this->getAddressbook();
1172
        // FIXME: create a function to get the adressbook contentstable
1173
        if ($addrbook)
0 ignored issues
show
introduced by
$addrbook is of type MAPIAddressbook, thus it always evaluated to true.
Loading history...
1174
            $ab_entryid = mapi_ab_getdefaultdir($addrbook);
0 ignored issues
show
Bug introduced by
The function mapi_ab_getdefaultdir was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1174
            $ab_entryid = /** @scrutinizer ignore-call */ mapi_ab_getdefaultdir($addrbook);
Loading history...
1175
        if ($ab_entryid)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_entryid does not seem to be defined for all execution paths leading up to this point.
Loading history...
1176
            $ab_dir = mapi_ab_openentry($addrbook, $ab_entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1176
            $ab_dir = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $ab_entryid);
Loading history...
1177
        if ($ab_dir)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_dir does not seem to be defined for all execution paths leading up to this point.
Loading history...
1178
            $table = mapi_folder_getcontentstable($ab_dir);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1178
            $table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($ab_dir);
Loading history...
1179
1180
        if (!$table)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table does not seem to be defined for all execution paths leading up to this point.
Loading history...
1181
            throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not open addressbook: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1181
            throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not open addressbook: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED);
Loading history...
1182
1183
        $restriction = MAPIUtils::GetSearchRestriction(u2w($searchquery));
0 ignored issues
show
Bug introduced by
It seems like u2w($searchquery) can also be of type false; however, parameter $query of MAPIUtils::GetSearchRestriction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1183
        $restriction = MAPIUtils::GetSearchRestriction(/** @scrutinizer ignore-type */ u2w($searchquery));
Loading history...
1184
        mapi_table_restrict($table, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1184
        /** @scrutinizer ignore-call */ 
1185
        mapi_table_restrict($table, $restriction);
Loading history...
1185
        mapi_table_sort($table, array(PR_DISPLAY_NAME => TABLE_SORT_ASCEND));
0 ignored issues
show
Bug introduced by
The function mapi_table_sort was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1185
        /** @scrutinizer ignore-call */ 
1186
        mapi_table_sort($table, array(PR_DISPLAY_NAME => TABLE_SORT_ASCEND));
Loading history...
1186
1187
        if (mapi_last_hresult())
1188
            throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not apply restriction: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_TOOCOMPLEX);
1189
1190
        //range for the search results, default symbian range end is 50, wm 99,
1191
        //so we'll use that of nokia
1192
        $rangestart = 0;
1193
        $rangeend = 50;
1194
1195
        if ($searchrange != '0') {
1196
            $pos = strpos($searchrange, '-');
1197
            $rangestart = substr($searchrange, 0, $pos);
1198
            $rangeend = substr($searchrange, ($pos + 1));
1199
        }
1200
        $items = array();
1201
1202
        $querycnt = mapi_table_getrowcount($table);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1202
        $querycnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($table);
Loading history...
1203
        //do not return more results as requested in range
1204
        $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt;
1205
1206
        if ($querycnt > 0)
1207
            $abentries = mapi_table_queryrows($table, array(PR_ENTRYID, PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION, PR_EMS_AB_THUMBNAIL_PHOTO), $rangestart, $querylimit);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1207
            $abentries = /** @scrutinizer ignore-call */ mapi_table_queryrows($table, array(PR_ENTRYID, PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION, PR_EMS_AB_THUMBNAIL_PHOTO), $rangestart, $querylimit);
Loading history...
1208
1209
        for ($i = 0; $i < $querylimit; $i++) {
1210
            if (!isset($abentries[$i][PR_SMTP_ADDRESS])) {
1211
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetGALSearchResults(): The GAL entry '%s' does not have an email address and will be ignored.", w2u($abentries[$i][PR_DISPLAY_NAME])));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $abentries does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
It seems like w2u($abentries[$i][PR_DISPLAY_NAME]) can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1211
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetGALSearchResults(): The GAL entry '%s' does not have an email address and will be ignored.", /** @scrutinizer ignore-type */ w2u($abentries[$i][PR_DISPLAY_NAME])));
Loading history...
1212
                continue;
1213
            }
1214
1215
            $items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_DISPLAY_NAME]);
1216
1217
            if (strlen(trim($items[$i][SYNC_GAL_DISPLAYNAME])) == 0)
1218
                $items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_ACCOUNT]);
1219
1220
            $items[$i][SYNC_GAL_ALIAS] = w2u($abentries[$i][PR_ACCOUNT]);
1221
            //it's not possible not get first and last name of an user
1222
            //from the gab and user functions, so we just set lastname
1223
            //to displayname and leave firstname unset
1224
            //this was changed in Zarafa 6.40, so we try to get first and
1225
            //last name and fall back to the old behaviour if these values are not set
1226
            if (isset($abentries[$i][PR_GIVEN_NAME]))
1227
                $items[$i][SYNC_GAL_FIRSTNAME] = w2u($abentries[$i][PR_GIVEN_NAME]);
1228
            if (isset($abentries[$i][PR_SURNAME]))
1229
                $items[$i][SYNC_GAL_LASTNAME] = w2u($abentries[$i][PR_SURNAME]);
1230
1231
            if (!isset($items[$i][SYNC_GAL_LASTNAME])) $items[$i][SYNC_GAL_LASTNAME] = $items[$i][SYNC_GAL_DISPLAYNAME];
1232
1233
            $items[$i][SYNC_GAL_EMAILADDRESS] = w2u($abentries[$i][PR_SMTP_ADDRESS]);
1234
            //check if an user has an office number or it might produce warnings in the log
1235
            if (isset($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER]))
1236
                $items[$i][SYNC_GAL_PHONE] = w2u($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER]);
1237
            //check if an user has a mobile number or it might produce warnings in the log
1238
            if (isset($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER]))
1239
                $items[$i][SYNC_GAL_MOBILEPHONE] = w2u($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER]);
1240
            //check if an user has a home number or it might produce warnings in the log
1241
            if (isset($abentries[$i][PR_HOME_TELEPHONE_NUMBER]))
1242
                $items[$i][SYNC_GAL_HOMEPHONE] = w2u($abentries[$i][PR_HOME_TELEPHONE_NUMBER]);
1243
1244
            if (isset($abentries[$i][PR_COMPANY_NAME]))
1245
                $items[$i][SYNC_GAL_COMPANY] = w2u($abentries[$i][PR_COMPANY_NAME]);
1246
1247
            if (isset($abentries[$i][PR_TITLE]))
1248
                $items[$i][SYNC_GAL_TITLE] = w2u($abentries[$i][PR_TITLE]);
1249
1250
            if (isset($abentries[$i][PR_OFFICE_LOCATION]))
1251
                $items[$i][SYNC_GAL_OFFICE] = w2u($abentries[$i][PR_OFFICE_LOCATION]);
1252
1253
            if ($searchpicture !== false && isset($abentries[$i][PR_EMS_AB_THUMBNAIL_PHOTO])) {
1254
                $items[$i][SYNC_GAL_PICTURE] = StringStreamWrapper::Open($abentries[$i][PR_EMS_AB_THUMBNAIL_PHOTO]);
1255
            }
1256
        }
1257
        $nrResults = count($items);
1258
        $items['range'] = ($nrResults > 0) ? $rangestart.'-'.($nrResults - 1) : '0-0';
1259
        $items['searchtotal'] = $nrResults;
1260
        return $items;
1261
    }
1262
1263
    /**
1264
     * Searches for the emails on the server
1265
     *
1266
     * @param ContentParameter $cpo
1267
     *
1268
     * @return array
1269
     */
1270
    public function GetMailboxSearchResults($cpo) {
1271
        $searchFolder = $this->getSearchFolder();
1272
        $searchRestriction = $this->getSearchRestriction($cpo);
1273
        $searchRange = explode('-', $cpo->GetSearchRange());
1274
        $searchFolderId = $cpo->GetSearchFolderid();
1275
        $searchFolders = array();
1276
        // search only in required folders
1277
        if (!empty($searchFolderId)) {
1278
            $searchFolderEntryId = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1278
            $searchFolderEntryId = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId));
Loading history...
1279
            $searchFolders[] = $searchFolderEntryId;
1280
        }
1281
        // if no folder was required then search in the entire store
1282
        else {
1283
            $tmp = mapi_getprops($this->store, array(PR_ENTRYID,PR_DISPLAY_NAME,PR_IPM_SUBTREE_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1283
            $tmp = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_ENTRYID,PR_DISPLAY_NAME,PR_IPM_SUBTREE_ENTRYID));
Loading history...
1284
            $searchFolders[] = $tmp[PR_IPM_SUBTREE_ENTRYID];
1285
        }
1286
        $items = array();
1287
        $flags = 0;
1288
        // if subfolders are required, do a recursive search
1289
        if ($cpo->GetSearchDeepTraversal()) {
1290
            $flags |= SEARCH_RECURSIVE;
1291
        }
1292
1293
        mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags);
0 ignored issues
show
Bug introduced by
The function mapi_folder_setsearchcriteria was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1293
        /** @scrutinizer ignore-call */ 
1294
        mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags);
Loading history...
1294
1295
        $table = mapi_folder_getcontentstable($searchFolder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1295
        $table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($searchFolder);
Loading history...
1296
        $searchStart = time();
1297
        // do the search and wait for all the results available
1298
        while (time() - $searchStart < SEARCH_WAIT) {
1299
            $searchcriteria = mapi_folder_getsearchcriteria($searchFolder);
0 ignored issues
show
Bug introduced by
The function mapi_folder_getsearchcriteria was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1299
            $searchcriteria = /** @scrutinizer ignore-call */ mapi_folder_getsearchcriteria($searchFolder);
Loading history...
1300
            if(($searchcriteria["searchstate"] & SEARCH_REBUILD) == 0)
1301
                break; // Search is done
1302
            sleep(1);
1303
        }
1304
1305
        // if the search range is set limit the result to it, otherwise return all found messages
1306
        $rows = (is_array($searchRange) && isset($searchRange[0], $searchRange[1])) ?
1307
            mapi_table_queryrows($table, array(PR_ENTRYID), $searchRange[0], $searchRange[1] - $searchRange[0] + 1) :
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1307
            /** @scrutinizer ignore-call */ 
1308
            mapi_table_queryrows($table, array(PR_ENTRYID), $searchRange[0], $searchRange[1] - $searchRange[0] + 1) :
Loading history...
1308
            mapi_table_queryrows($table, array(PR_ENTRYID), 0, SEARCH_MAXRESULTS);
1309
1310
        $cnt = count($rows);
1311
        $items['searchtotal'] = $cnt;
1312
        $items["range"] = $cpo->GetSearchRange();
1313
        for ($i = 0; $i < $cnt; $i++) {
1314
            $items[$i]['class'] = 'Email';
1315
            $items[$i]['longid'] = bin2hex($rows[$i][PR_ENTRYID]);
1316
            //$items[$i]['folderid'] = bin2hex($rows[$i][PR_PARENT_SOURCE_KEY]);
1317
        }
1318
        return $items;
1319
    }
1320
1321
    /**
1322
    * Terminates a search for a given PID
1323
    *
1324
    * @param int $pid
1325
    *
1326
    * @return boolean
1327
    */
1328
    public function TerminateSearch($pid) {
1329
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->TerminateSearch(): terminating search for pid %d", $pid));
1330
        if (!isset($this->store) || $this->store === false) {
1331
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->TerminateSearch(): The store is not available. It is not possible to remove search folder with pid %d", $pid));
1332
            return false;
1333
        }
1334
1335
        $storeProps = mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1335
        $storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID));
Loading history...
1336
        if (($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) {
1337
            ZLog::Write(LOGLEVEL_WARN, "Grommunio->TerminateSearch(): Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder");
1338
            return false;
1339
        }
1340
1341
        $finderfolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1341
        $finderfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
Loading history...
1342
        if(mapi_last_hresult() != NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1342
        if(/** @scrutinizer ignore-call */ mapi_last_hresult() != NOERROR) {
Loading history...
1343
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->TerminateSearch(): Unable to open search folder (0x%X)", mapi_last_hresult()));
1344
            return false;
1345
        }
1346
1347
        $hierarchytable = mapi_folder_gethierarchytable($finderfolder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1347
        $hierarchytable = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($finderfolder);
Loading history...
1348
        mapi_table_restrict($hierarchytable,
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1348
        /** @scrutinizer ignore-call */ 
1349
        mapi_table_restrict($hierarchytable,
Loading history...
1349
            array(RES_CONTENT,
1350
                array(
1351
                    FUZZYLEVEL      => FL_PREFIX,
1352
                    ULPROPTAG       => PR_DISPLAY_NAME,
1353
                    VALUE           => array(PR_DISPLAY_NAME=>"grommunio-sync Search Folder ".$pid)
1354
                )
1355
            ),
1356
            TBL_BATCH);
1357
1358
        $folders = mapi_table_queryallrows($hierarchytable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1358
        $folders = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchytable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME));
Loading history...
1359
        foreach($folders as $folder) {
1360
            mapi_folder_deletefolder($finderfolder, $folder[PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
The function mapi_folder_deletefolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1360
            /** @scrutinizer ignore-call */ 
1361
            mapi_folder_deletefolder($finderfolder, $folder[PR_ENTRYID]);
Loading history...
1361
        }
1362
        return true;
1363
    }
1364
1365
    /**
1366
     * Disconnects from the current search provider
1367
     *
1368
     * @access public
1369
     * @return boolean
1370
     */
1371
    public function Disconnect() {
1372
        return true;
1373
    }
1374
1375
    /**
1376
     * Returns the MAPI store resource for a folderid
1377
     * This is not part of IBackend but necessary for the ImportChangesICS->MoveMessage() operation if
1378
     * the destination folder is not in the default store
1379
     * Note: The current backend store might be changed as IBackend->Setup() is executed
1380
     *
1381
     * @param string        $store              target store, could contain a "domain\user" value - if empty default store is returned
1382
     * @param string        $folderid
1383
     *
1384
     * @access public
1385
     * @return Resource/boolean
0 ignored issues
show
Documentation Bug introduced by
The doc comment Resource/boolean at position 0 could not be parsed: Unknown type name 'Resource/boolean' at position 0 in Resource/boolean.
Loading history...
1386
     */
1387
    public function GetMAPIStoreForFolderId($store, $folderid) {
1388
        if ($store == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $store of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
1389
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetMAPIStoreForFolderId('%s', '%s'): no store specified, returning default store", $store, $folderid));
1390
            return $this->defaultstore;
1391
        }
1392
1393
        // setup the correct store
1394
        if ($this->Setup($store, false, $folderid)) {
1395
            return $this->store;
1396
        }
1397
        else {
1398
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetMAPIStoreForFolderId('%s', '%s'): store is not available", $store, $folderid));
1399
            return false;
1400
        }
1401
    }
1402
1403
    /**
1404
     * Returns the email address and the display name of the user. Used by autodiscover.
1405
     *
1406
     * @param string        $username           The username
1407
     *
1408
     * @access public
1409
     * @return Array
1410
     */
1411
    public function GetUserDetails($username) {
1412
        ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->GetUserDetails for '%s'.", $username));
1413
        $zarafauserinfo = @nsp_getuserinfo($username);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1413
        $zarafauserinfo = @/** @scrutinizer ignore-call */ nsp_getuserinfo($username);
Loading history...
1414
        $userDetails['emailaddress'] = (isset($zarafauserinfo['primary_email']) && $zarafauserinfo['primary_email']) ? $zarafauserinfo['primary_email'] : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$userDetails was never initialized. Although not strictly required by PHP, it is generally a good practice to add $userDetails = array(); before regardless.
Loading history...
1415
        $userDetails['fullname'] = (isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname']) ? $zarafauserinfo['fullname'] : false;
1416
        return $userDetails;
1417
    }
1418
1419
    /**
1420
     * Returns the username of the currently active user
1421
     *
1422
     * @access public
1423
     * @return String
1424
     */
1425
    public function GetCurrentUsername() {
1426
        return $this->storeName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storeName returns the type boolean which is incompatible with the documented return type string.
Loading history...
1427
    }
1428
1429
    /**
1430
     * Returns the impersonated user name.
1431
     *
1432
     * @access public
1433
     * @return string or false if no user is impersonated
1434
     */
1435
    public function GetImpersonatedUser() {
1436
        return $this->impersonateUser;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->impersonateUser returns the type boolean which is incompatible with the documented return type string.
Loading history...
1437
    }
1438
1439
    /**
1440
     * Returns the authenticated user name.
1441
     *
1442
     * @access public
1443
     * @return string
1444
     */
1445
    public function GetMainUser() {
1446
        return $this->mainUser;
1447
    }
1448
1449
    /**
1450
     * Indicates if the Backend supports folder statistics.
1451
     *
1452
     * @access public
1453
     * @return boolean
1454
     */
1455
    public function HasFolderStats() {
1456
        return true;
1457
    }
1458
1459
    /**
1460
     * Returns a status indication of the folder.
1461
     * If there are changes in the folder, the returned value must change.
1462
     * The returned values are compared with '===' to determine if a folder needs synchronization or not.
1463
     *
1464
     * @param string $store         the store where the folder resides
1465
     * @param string $folderid      the folder id
1466
     *
1467
     * @access public
1468
     * @return string
1469
     */
1470
    public function GetFolderStat($store, $folderid) {
1471
        list($user, $domain) = Utils::SplitDomainUser($store);
1472
        if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
1473
            $user = $this->mainUser;
1474
            if ($this->impersonateUser) {
1475
                $user = $this->impersonateUser;
1476
            }
1477
        }
1478
1479
        if (!isset($this->folderStatCache[$user])) {
1480
            $this->folderStatCache[$user] = array();
1481
        }
1482
1483
        // if there is nothing in the cache for a store, load the data for all folders of it
1484
        if (empty($this->folderStatCache[$user])) {
1485
            // get the store
1486
            $userstore = $this->openMessageStore($user);
1487
            $rootfolder = mapi_msgstore_openentry($userstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1487
            $rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($userstore);
Loading history...
1488
            $hierarchy =  mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1488
            $hierarchy =  /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1489
            $rows = mapi_table_queryallrows($hierarchy, array(PR_SOURCE_KEY, PR_LOCAL_COMMIT_TIME_MAX, PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_DELETED_MSG_COUNT));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1489
            $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, array(PR_SOURCE_KEY, PR_LOCAL_COMMIT_TIME_MAX, PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_DELETED_MSG_COUNT));
Loading history...
1490
1491
            if (count($rows) == 0) {
1492
                ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->GetFolderStat(): could not access folder statistics for user '%s'. Probably missing 'read' permissions on the root folder! Folders of this store will be synchronized ONCE per hour only!", $user));
1493
            }
1494
1495
            foreach($rows as $folder) {
1496
                $commit_time = isset($folder[PR_LOCAL_COMMIT_TIME_MAX])? $folder[PR_LOCAL_COMMIT_TIME_MAX] : "0000000000";
1497
                $content_count = isset($folder[PR_CONTENT_COUNT])? $folder[PR_CONTENT_COUNT] : -1;
1498
                $content_unread = isset($folder[PR_CONTENT_UNREAD])? $folder[PR_CONTENT_UNREAD] : -1;
1499
                $content_deleted = isset($folder[PR_DELETED_MSG_COUNT])? $folder[PR_DELETED_MSG_COUNT] : -1;
1500
1501
                $this->folderStatCache[$user][bin2hex($folder[PR_SOURCE_KEY])] = $commit_time ."/". $content_count ."/". $content_unread ."/". $content_deleted;
1502
            }
1503
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetFolderStat() fetched status information of %d folders for store '%s'", count($this->folderStatCache[$user]), $user));
1504
        }
1505
1506
        if (isset($this->folderStatCache[$user][$folderid])) {
1507
            return $this->folderStatCache[$user][$folderid];
1508
        }
1509
        else {
1510
            // a timestamp that changes once per hour is returned in case there is no data found for this folder. It will be synchronized only once per hour.
1511
            return gmdate("Y-m-d-H");
1512
        }
1513
    }
1514
1515
    /**
1516
     * Returns information about the user's store:
1517
     * number of folders, store size, full name, email address.
1518
     *
1519
     * @access public
1520
     * @return UserStoreInfo
1521
     */
1522
    public function GetUserStoreInfo() {
1523
        $userStoreInfo = new UserStoreInfo();
1524
1525
        $rootfolder = mapi_msgstore_openentry($this->store);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1525
        $rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store);
Loading history...
1526
        $hierarchy =  mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1526
        $hierarchy =  /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1527
        // Do not take hidden and system folders into account
1528
        // TODO make this restriction generic and use for hierarchy?
1529
        $restrict = array(RES_AND, array(
1530
                            array(  RES_PROPERTY,
1531
                                array(  RELOP => RELOP_NE,
1532
                                        ULPROPTAG => PR_ATTR_HIDDEN,
1533
                                        VALUE => true),
1534
                            ),
1535
                            array(  RES_PROPERTY,
1536
                                array(  RELOP => RELOP_EQ,
1537
                                        ULPROPTAG => PR_FOLDER_TYPE,
1538
                                        VALUE => FOLDER_GENERIC),
1539
                            ),
1540
                            array(  RES_EXIST,
1541
                                array(  ULPROPTAG => PR_CONTAINER_CLASS),
1542
                            ),
1543
                        ));
1544
        mapi_table_restrict($hierarchy, $restrict);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1544
        /** @scrutinizer ignore-call */ 
1545
        mapi_table_restrict($hierarchy, $restrict);
Loading history...
1545
        $foldercount = mapi_table_getrowcount($hierarchy);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1545
        $foldercount = /** @scrutinizer ignore-call */ mapi_table_getrowcount($hierarchy);
Loading history...
1546
1547
        $storeProps = mapi_getprops($this->store, array(PR_MESSAGE_SIZE_EXTENDED));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1547
        $storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_MESSAGE_SIZE_EXTENDED));
Loading history...
1548
        $storesize = isset($storeProps[PR_MESSAGE_SIZE_EXTENDED]) ? $storeProps[PR_MESSAGE_SIZE_EXTENDED] : 0;
1549
1550
        $userDetails = $this->GetUserDetails($this->impersonateUser ?: $this->mainUser);
0 ignored issues
show
Bug introduced by
It seems like $this->impersonateUser ?: $this->mainUser can also be of type true; however, parameter $username of Grommunio::GetUserDetails() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1550
        $userDetails = $this->GetUserDetails(/** @scrutinizer ignore-type */ $this->impersonateUser ?: $this->mainUser);
Loading history...
1551
        $userStoreInfo->SetData($foldercount, $storesize, $userDetails['fullname'], $userDetails['emailaddress']);
1552
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetUserStoreInfo(): user %s (%s) store size is %d bytes and contains %d folders",
1553
                Utils::PrintAsString($userDetails['fullname']), Utils::PrintAsString($userDetails['emailaddress']), $storesize, $foldercount));
1554
1555
        return $userStoreInfo;
1556
    }
1557
1558
    /**----------------------------------------------------------------------------------------------------------
1559
     * Implementation of the IStateMachine interface
1560
     */
1561
1562
    /**
1563
     * Gets a hash value indicating the latest dataset of the named
1564
     * state with a specified key and counter.
1565
     * If the state is changed between two calls of this method
1566
     * the returned hash should be different
1567
     *
1568
     * @param string    $devid              the device id
1569
     * @param string    $type               the state type
1570
     * @param string    $key                (opt)
1571
     * @param string    $counter            (opt)
1572
     *
1573
     * @access public
1574
     * @return string
1575
     * @throws StateNotFoundException
1576
     */
1577
    public function GetStateHash($devid, $type, $key = false, $counter = false) {
1578
        try {
1579
            $stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1579
            $stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1579
            $stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
1580
            $stateMessageProps = mapi_getprops($stateMessage, [PR_LAST_MODIFICATION_TIME]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1580
            $stateMessageProps = /** @scrutinizer ignore-call */ mapi_getprops($stateMessage, [PR_LAST_MODIFICATION_TIME]);
Loading history...
1581
            if (isset($stateMessageProps[PR_LAST_MODIFICATION_TIME])) {
1582
                return $stateMessageProps[PR_LAST_MODIFICATION_TIME];
1583
            }
1584
        }
1585
        catch (StateNotFoundException $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1586
        return "0";
1587
    }
1588
1589
    /**
1590
     * Gets a state for a specified key and counter.
1591
     * This method should call IStateMachine->CleanStates()
1592
     * to remove older states (same key, previous counters)
1593
     *
1594
     * @param string    $devid              the device id
1595
     * @param string    $type               the state type
1596
     * @param string    $key                (opt)
1597
     * @param string    $counter            (opt)
1598
     * @param string    $cleanstates        (opt)
1599
     *
1600
     * @access public
1601
     * @return mixed
1602
     * @throws StateNotFoundException, StateInvalidException, UnavailableException
1603
     */
1604
    public function GetState($devid, $type, $key = false, $counter = false, $cleanstates = true) {
1605
        if ($counter && $cleanstates) {
1606
            $this->CleanStates($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::CleanStates() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1606
            $this->CleanStates($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1607
            // also clean Failsave state for previous counter
1608
            if ($key == false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $key of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1609
                $this->CleanStates($devid, $type, IStateMachine::FAILSAVE, $counter);
1610
            }
1611
        }
1612
        $stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1612
        $stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1612
        $stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1613
        $state = base64_decode(MAPIUtils::readPropStream($stateMessage, PR_BODY));
1614
1615
        if ($state && $state[0] === '{') {
1616
            $jsonDec = json_decode($state);
1617
            if (isset($jsonDec->gsSyncStateClass)) {
1618
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetState(): top class '%s'", $jsonDec->gsSyncStateClass));
1619
                $gsObj = new $jsonDec->gsSyncStateClass;
1620
                $gsObj->jsonDeserialize($jsonDec);
1621
                $gsObj->postUnserialize();
1622
            }
1623
        }
1624
        return isset($gsObj) && is_object($gsObj) ? $gsObj : $state;
1625
    }
1626
1627
    /**
1628
     * Writes ta state to for a key and counter
1629
     *
1630
     * @param mixed     $state
1631
     * @param string    $devid              the device id
1632
     * @param string    $type               the state type
1633
     * @param string    $key                (opt)
1634
     * @param int       $counter            (opt)
1635
     *
1636
     * @access public
1637
     * @return boolean
1638
     * @throws StateInvalidException, UnavailableException
1639
     */
1640
    public function SetState($state, $devid, $type, $key = false, $counter = false) {
1641
        return $this->setStateMessage($state, $devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::setStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1641
        return $this->setStateMessage($state, $devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::setStateMessage() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1641
        return $this->setStateMessage($state, $devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
1642
    }
1643
1644
    /**
1645
     * Cleans up all older states.
1646
     * If called with a $counter, all states previous state counter can be removed.
1647
     * If additionally the $thisCounterOnly flag is true, only that specific counter will be removed.
1648
     * If called without $counter, all keys (independently from the counter) can be removed.
1649
     *
1650
     * @param string    $devid              the device id
1651
     * @param string    $type               the state type
1652
     * @param string    $key
1653
     * @param string    $counter            (opt)
1654
     * @param string    $thisCounterOnly    (opt) if provided, the exact counter only will be removed
1655
     *
1656
     * @access public
1657
     * @return
1658
     * @throws StateInvalidException
1659
     */
1660
    public function CleanStates($devid, $type, $key, $counter = false, $thisCounterOnly = false) {
1661
        if (!$this->stateFolder) {
1662
            $this->getStateFolder($devid);
1663
            if (!$this->stateFolder) {
1664
                throw new StateNotFoundException(sprintf("Grommunio->getStateMessage(): Could not locate the state folder for device '%s'",
1665
                $devid));
1666
            }
1667
        }
1668
        $messageName = rtrim((($key !== false) ? $key."-" : "") . (($type !== "") ? $type : ""), "-");
0 ignored issues
show
introduced by
The condition $key !== false is always true.
Loading history...
1669
        $restriction = $this->getStateMessageRestriction($messageName, $counter, $thisCounterOnly);
0 ignored issues
show
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessageRestriction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1669
        $restriction = $this->getStateMessageRestriction($messageName, /** @scrutinizer ignore-type */ $counter, $thisCounterOnly);
Loading history...
Bug introduced by
It seems like $thisCounterOnly can also be of type false; however, parameter $thisCounterOnly of Grommunio::getStateMessageRestriction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1669
        $restriction = $this->getStateMessageRestriction($messageName, $counter, /** @scrutinizer ignore-type */ $thisCounterOnly);
Loading history...
1670
        $stateFolderContents = mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1670
        $stateFolderContents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1671
        if ($stateFolderContents) {
1672
            mapi_table_restrict($stateFolderContents, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1672
            /** @scrutinizer ignore-call */ 
1673
            mapi_table_restrict($stateFolderContents, $restriction);
Loading history...
1673
            $rowCnt = mapi_table_getrowcount($stateFolderContents);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1673
            $rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($stateFolderContents);
Loading history...
1674
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->CleanStates(): Found %d states to clean (%s)", $rowCnt, $messageName));
1675
            if ($rowCnt > 0) {
1676
                $rows = mapi_table_queryallrows($stateFolderContents, [PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1676
                $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($stateFolderContents, [PR_ENTRYID]);
Loading history...
1677
                $entryids = [];
1678
                foreach($rows as $row) {
1679
                    $entryids[] = $row[PR_ENTRYID];
1680
                }
1681
                mapi_folder_deletemessages($this->stateFolder, $entryids, DELETE_HARD_DELETE);
1 ignored issue
show
Bug introduced by
The function mapi_folder_deletemessages was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1681
                /** @scrutinizer ignore-call */ 
1682
                mapi_folder_deletemessages($this->stateFolder, $entryids, DELETE_HARD_DELETE);
Loading history...
1682
            }
1683
        }
1684
    }
1685
1686
    /**
1687
     * Links a user to a device
1688
     *
1689
     * @param string    $username
1690
     * @param string    $devid
1691
     *
1692
     * @access public
1693
     * @return boolean     indicating if the user was added or not (existed already)
1694
     */
1695
    public function LinkUserDevice($username, $devid) {
1696
        $device = [$devid => time()];
1697
        $this->setDeviceUserData($this->type, $device, $username, -1, $subkey=-1, $doCas="merge");
1698
        return false;
1699
    }
1700
1701
    /**
1702
     * Unlinks a device from a user
1703
     *
1704
     * @param string    $username
1705
     * @param string    $devid
1706
     *
1707
     * @access public
1708
     * @return boolean
1709
     */
1710
    public function UnLinkUserDevice($username, $devid) {
1711
        //TODO: Implement
1712
        return false;
1713
    }
1714
1715
    /**
1716
     * Returns the current version of the state files
1717
     * grommunio:  This is not relevant atm. IStateMachine::STATEVERSION_02 will match ZPush::GetLatestStateVersion().
1718
     *          If it might be required to update states in the future, this could be implemented on a store level,
1719
     *          where states are then migrated "on-the-fly"
1720
     *          or
1721
     *          in a global settings where all states in all stores are migrated once.
1722
     *
1723
     * @access public
1724
     * @return int
1725
     */
1726
    public function GetStateVersion() {
1727
        return IStateMachine::STATEVERSION_02;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IStateMachine::STATEVERSION_02 returns the type string which is incompatible with the documented return type integer.
Loading history...
1728
    }
1729
1730
    /**
1731
     * Sets the current version of the state files
1732
     *
1733
     * @param int       $version            the new supported version
1734
     *
1735
     * @access public
1736
     * @return boolean
1737
     */
1738
    public function SetStateVersion($version) {
1739
        return true;
1740
    }
1741
1742
    /**
1743
     * Returns MAPIFolder object which contains the state information.
1744
     * Creates this folder if it is not available yet.
1745
     *
1746
     * @param string    $devid              the device id
1747
     *
1748
     * @access private
1749
     * @return MAPIFolder
0 ignored issues
show
Bug introduced by
The type MAPIFolder 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1750
     */
1751
    private function getStateFolder($devid) {
1752
        // Options request doesn't send device id
1753
        if (strlen($devid) == 0) {
1754
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type MAPIFolder.
Loading history...
1755
        }
1756
        // Try to get the state folder id from redis
1757
        if (!$this->stateFolder) {
1758
            $folderentryid = $this->getDeviceUserData($this->userDeviceData, $devid, $this->mainUser, "statefolder");
1759
            if ($folderentryid) {
1760
                $this->stateFolder = mapi_msgstore_openentry($this->store, hex2bin($folderentryid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1760
                $this->stateFolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, hex2bin($folderentryid));
Loading history...
1761
            }
1762
        }
1763
1764
        // fallback code
1765
        if (!$this->stateFolder) {
1766
            ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getStateFolder(): state folder not set. Use fallback"));
1767
            $rootfolder = mapi_msgstore_openentry($this->store);
1768
            $hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1768
            $hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1769
            $restriction = $this->getStateFolderRestriction($devid);
1770
            // restrict the hierarchy to the grommunio-sync search folder only
1771
            mapi_table_restrict($hierarchy, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1771
            /** @scrutinizer ignore-call */ 
1772
            mapi_table_restrict($hierarchy, $restriction);
Loading history...
1772
            $rowCnt = mapi_table_getrowcount($hierarchy);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1772
            $rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($hierarchy);
Loading history...
1773
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): found %d device state folders", $rowCnt));
1774
            if ($rowCnt == 1) {
1775
                $hierarchyRows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1775
                $hierarchyRows = /** @scrutinizer ignore-call */ mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
Loading history...
1776
                $this->stateFolder = mapi_msgstore_openentry($this->store, $hierarchyRows[0][PR_ENTRYID]);
1777
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): %s", bin2hex($hierarchyRows[0][PR_ENTRYID])));
1778
                // put found id in redis
1779
                if ($devid) {
1780
                    $this->setDeviceUserData($this->userDeviceData, bin2hex($hierarchyRows[0][PR_ENTRYID]), $devid, $this->mainUser, "statefolder");
1781
                }
1782
            }
1783
            elseif ($rowCnt == 0) {
1784
                // legacy code: create the hidden state folder and the device subfolder
1785
                // this should happen when the user configures the device (autodiscover or first sync if no autodiscover)
1786
1787
                $hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1788
                $restriction = $this->getStateFolderRestriction(STORE_STATE_FOLDER);
1789
                mapi_table_restrict($hierarchy, $restriction);
1790
                $rowCnt = mapi_table_getrowcount($hierarchy);
1791
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): found %d store state folders", $rowCnt));
1792
                if ($rowCnt == 1) {
1793
                    $hierarchyRows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
1794
                    $stateFolder = mapi_msgstore_openentry($this->store, $hierarchyRows[0][PR_ENTRYID]);
1795
                }
1796
                elseif ($rowCnt == 0) {
1797
                    $stateFolder = mapi_folder_createfolder($rootfolder, STORE_STATE_FOLDER, "");
0 ignored issues
show
Bug introduced by
The function mapi_folder_createfolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1797
                    $stateFolder = /** @scrutinizer ignore-call */ mapi_folder_createfolder($rootfolder, STORE_STATE_FOLDER, "");
Loading history...
1798
                    mapi_setprops($stateFolder, array(PR_ATTR_HIDDEN => true));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1798
                    /** @scrutinizer ignore-call */ 
1799
                    mapi_setprops($stateFolder, array(PR_ATTR_HIDDEN => true));
Loading history...
1799
                }
1800
                else {
1801
                    // TODO: handle this
1802
                }
1803
                if (isset($stateFolder) && $stateFolder) {
1804
                    $devStateFolder = mapi_folder_createfolder($stateFolder, $devid, "");
1805
                    $devStateFolderProps = mapi_getprops($devStateFolder);
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1805
                    $devStateFolderProps = /** @scrutinizer ignore-call */ mapi_getprops($devStateFolder);
Loading history...
1806
                    $this->stateFolder = mapi_msgstore_openentry($this->store, $devStateFolderProps[PR_ENTRYID]);
1807
                    mapi_setprops($this->stateFolder, array(PR_ATTR_HIDDEN => true));
1808
                    // we don't cache the entryid in redis, because this will happen on the next request anyway
1809
                }
1810
                else {
1811
                    // TODO: unable to create state folder - throw exception
1812
                }
1813
            }
1814
            else {
1815
                // This case is rather unlikely that there would be several
1816
                // hidden folders having PR_DISPLAY_NAME the same as device id.
1817
1818
                // TODO: get the hierarchy table again, get entry id of STORE_STATE_FOLDER
1819
                // and compare it to the parent id of those folders.
1820
            }
1821
        }
1822
        return $this->stateFolder;
1823
    }
1824
1825
    /**
1826
     * Returns the associated MAPIMessage which contains the state information.
1827
     *
1828
     * @param string    $devid              the device id
1829
     * @param string    $type               the state type
1830
     * @param string    $key                (opt)
1831
     * @param string    $counter            state counter
1832
1833
     *
1834
     * @access private
1835
     * @return MAPIMessage
0 ignored issues
show
Bug introduced by
The type MAPIMessage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1836
     * @throws StateNotFoundException
1837
     */
1838
    private function getStateMessage($devid, $type, $key, $counter) {
1839
        if (!$this->stateFolder) {
1840
            $this->getStateFolder(Request::GetDeviceID());
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceID() can also be of type false; however, parameter $devid of Grommunio::getStateFolder() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1840
            $this->getStateFolder(/** @scrutinizer ignore-type */ Request::GetDeviceID());
Loading history...
1841
            if (!$this->stateFolder) {
1842
                throw new StateNotFoundException(sprintf("Grommunio->getStateMessage(): Could not locate the state folder for device '%s'",
1843
                $devid));
1844
            }
1845
        }
1846
        $messageName = rtrim((($key !== false) ? $key."-" : "") . (($type !== "") ? $type : ""), "-");
0 ignored issues
show
introduced by
The condition $key !== false is always true.
Loading history...
1847
        $restriction = $this->getStateMessageRestriction($messageName, $counter, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $thisCounterOnly of Grommunio::getStateMessageRestriction(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1847
        $restriction = $this->getStateMessageRestriction($messageName, $counter, /** @scrutinizer ignore-type */ true);
Loading history...
1848
        $stateFolderContents = mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1848
        $stateFolderContents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1849
        if ($stateFolderContents) {
1850
            mapi_table_restrict($stateFolderContents, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1850
            /** @scrutinizer ignore-call */ 
1851
            mapi_table_restrict($stateFolderContents, $restriction);
Loading history...
1851
            $rowCnt = mapi_table_getrowcount($stateFolderContents);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1851
            $rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($stateFolderContents);
Loading history...
1852
            if ($rowCnt == 1) {
1853
                $stateFolderRows = mapi_table_queryrows($stateFolderContents, [PR_ENTRYID], 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1853
                $stateFolderRows = /** @scrutinizer ignore-call */ mapi_table_queryrows($stateFolderContents, [PR_ENTRYID], 0, 1);
Loading history...
1854
                return mapi_msgstore_openentry($this->store, $stateFolderRows[0][PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1854
                return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $stateFolderRows[0][PR_ENTRYID]);
Loading history...
1855
            }
1856
            elseif($rowCnt > 1) {
1857
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getStateMessage(): Found several (%d) states for '%s'", $rowCnt, $messageName));
1858
            }
1859
        }
1860
        throw new StateNotFoundException(sprintf("Grommunio->getStateMessage(): Could not locate the state message '%s-%s'",
1861
            $messageName, Utils::PrintAsString($counter)));
1862
    }
1863
1864
    /**
1865
     * Writes ta state to for a key and counter
1866
     *
1867
     * @param mixed     $state
1868
     * @param string    $devid              the device id
1869
     * @param string    $type               the state type
1870
     * @param string    $key                (opt)
1871
     * @param int       $counter            (opt)
1872
     *
1873
     * @access public
1874
     * @return boolean
1875
     * @throws StateInvalidException, UnavailableException
1876
     */
1877
    private function setStateMessage($state, $devid, $type, $key = false, $counter = false) {
1878
        if (!$this->stateFolder) {
1879
            throw new StateNotFoundException(sprintf("Grommunio->setStateMessage(): Could not locate the state folder for device '%s'", $devid));
1880
        }
1881
        try {
1882
            $stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1882
            $stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1882
            $stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1883
        }
1884
        catch (StateNotFoundException $e) {
1885
            // if message is not available, try to create a new one
1886
            $stateMessage = mapi_folder_createmessage($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_createmessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1886
            $stateMessage = /** @scrutinizer ignore-call */ mapi_folder_createmessage($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1887
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): mapi_folder_createmessage 0x%08X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1887
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): mapi_folder_createmessage 0x%08X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
1888
1889
            $messageName = rtrim((($key !== false) ? $key."-" : "") . (($type !== "") ? $type : ""), "-");
1890
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): creating new state message '%s-%d'", $messageName, is_int($counter) ? $counter : 0));
1891
            mapi_setprops($stateMessage, [PR_DISPLAY_NAME => $messageName, PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1891
            /** @scrutinizer ignore-call */ 
1892
            mapi_setprops($stateMessage, [PR_DISPLAY_NAME => $messageName, PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
Loading history...
1892
        }
1893
        if (isset($stateMessage)) {
1894
            $jsonEncodedState = is_object($state) || is_array($state) ? json_encode($state, JSON_INVALID_UTF8_IGNORE | JSON_UNESCAPED_UNICODE) : $state;
1895
1896
            $encodedState = base64_encode($jsonEncodedState);
1897
            $encodedStateLength = strlen($encodedState);
1898
            mapi_setprops($stateMessage, [PR_LAST_VERB_EXECUTED => is_int($counter) ? $counter : 0]);
1899
            $stream = mapi_openproperty($stateMessage, PR_BODY, IID_IStream, STGM_DIRECT, MAPI_CREATE | MAPI_MODIFY);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1899
            $stream = /** @scrutinizer ignore-call */ mapi_openproperty($stateMessage, PR_BODY, IID_IStream, STGM_DIRECT, MAPI_CREATE | MAPI_MODIFY);
Loading history...
1900
            mapi_stream_setsize($stream, $encodedStateLength);
1 ignored issue
show
Bug introduced by
The function mapi_stream_setsize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1900
            /** @scrutinizer ignore-call */ 
1901
            mapi_stream_setsize($stream, $encodedStateLength);
Loading history...
1901
            mapi_stream_write($stream, $encodedState);
1 ignored issue
show
Bug introduced by
The function mapi_stream_write was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1901
            /** @scrutinizer ignore-call */ 
1902
            mapi_stream_write($stream, $encodedState);
Loading history...
1902
            mapi_stream_commit($stream);
1 ignored issue
show
Bug introduced by
The function mapi_stream_commit was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1902
            /** @scrutinizer ignore-call */ 
1903
            mapi_stream_commit($stream);
Loading history...
1903
            mapi_savechanges($stateMessage);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1903
            /** @scrutinizer ignore-call */ 
1904
            mapi_savechanges($stateMessage);
Loading history...
1904
1905
            return $encodedStateLength;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encodedStateLength returns the type integer which is incompatible with the documented return type boolean.
Loading history...
1906
        }
1907
        return false;
1908
    }
1909
1910
    /**
1911
     * Returns the restriction for the state folder name.
1912
     *
1913
     * @param string    $folderName         the state folder name
1914
     *
1915
     * @access private
1916
     * @return array
1917
     */
1918
    private function getStateFolderRestriction($folderName) {
1919
        return [RES_AND, [
1920
            [   RES_PROPERTY,
1921
                [   RELOP => RELOP_EQ,
1922
                    ULPROPTAG => PR_DISPLAY_NAME,
1923
                    VALUE => $folderName
1924
                ],
1925
            ],
1926
            [   RES_PROPERTY,
1927
                [   RELOP => RELOP_EQ,
1928
                    ULPROPTAG => PR_ATTR_HIDDEN,
1929
                    VALUE => true
1930
                ],
1931
            ]
1932
        ]];
1933
    }
1934
1935
    /**
1936
     * Returns the restriction for the associated message in the state folder.
1937
     *
1938
     * @param string    $messageName        the message name
1939
     * @param string    $counter            counter
1940
     * @param string    $thisCounterOnly    (opt) if provided, restrict to the exact counter
1941
     *
1942
     * @access private
1943
     * @return array
1944
     */
1945
    private function getStateMessageRestriction($messageName, $counter, $thisCounterOnly = false) {
1946
        return [RES_AND, [
1947
            [   RES_PROPERTY,
1948
                [   RELOP => RELOP_EQ,
1949
                    ULPROPTAG => PR_DISPLAY_NAME,
1950
                    VALUE => $messageName
1951
                ],
1952
            ],
1953
            [   RES_PROPERTY,
1954
                [   RELOP => RELOP_EQ,
1955
                    ULPROPTAG => PR_MESSAGE_CLASS,
1956
                    VALUE => 'IPM.Note.GrommunioState'
1957
                ],
1958
            ],
1959
            [   RES_PROPERTY,
1960
                [   RELOP => $thisCounterOnly ? RELOP_EQ : RELOP_LT,
1961
                    ULPROPTAG => PR_LAST_VERB_EXECUTED,
1962
                    VALUE => $counter
1963
                ],
1964
            ]
1965
        ]];
1966
    }
1967
1968
    /**----------------------------------------------------------------------------------------------------------
1969
     * Private methods
1970
     */
1971
1972
    /**
1973
     * Returns a hash representing changes in the hierarchy of the main user.
1974
     * It changes if a folder is added, renamed or deleted.
1975
     *
1976
     * @access private
1977
     * @return string
1978
     */
1979
    private function getHierarchyHash() {
1980
        $rootfolder = mapi_msgstore_openentry($this->defaultstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1980
        $rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore);
Loading history...
1981
        $hierarchy =  mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1981
        $hierarchy =  /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1982
        return md5(serialize(mapi_table_queryallrows($hierarchy, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID))));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1982
        return md5(serialize(/** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID))));
Loading history...
1983
    }
1984
1985
1986
    /**
1987
     * Advises a store to the changes sink
1988
     *
1989
     * @param mapistore $store              store to be advised
0 ignored issues
show
Bug introduced by
The type mapistore was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1990
     *
1991
     * @access private
1992
     * @return boolean
1993
     */
1994
    private function adviseStoreToSink($store) {
1995
        // check if we already advised the store
1996
        if (!in_array($store, $this->changesSinkStores)) {
1997
            mapi_msgstore_advise($store, null, fnevObjectModified | fnevObjectCreated | fnevObjectMoved | fnevObjectDeleted, $this->changesSink);
0 ignored issues
show
Bug introduced by
The function mapi_msgstore_advise was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1997
            /** @scrutinizer ignore-call */ 
1998
            mapi_msgstore_advise($store, null, fnevObjectModified | fnevObjectCreated | fnevObjectMoved | fnevObjectDeleted, $this->changesSink);
Loading history...
1998
1999
            if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1999
            if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
2000
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->adviseStoreToSink(): failed to advised store '%s' with code 0x%X. Polling will be performed.", $store, mapi_last_hresult()));
2001
                return false;
2002
            }
2003
            else {
2004
                ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->adviseStoreToSink(): advised store '%s'", $store));
2005
                $this->changesSinkStores[] = $store;
2006
            }
2007
        }
2008
        return true;
2009
    }
2010
2011
    /**
2012
     * Open the store marked with PR_DEFAULT_STORE = TRUE
2013
     * if $return_public is set, the public store is opened
2014
     *
2015
     * @param string    $user               User which store should be opened
2016
     *
2017
     * @access public
2018
     * @return boolean
2019
     */
2020
    private function openMessageStore($user) {
2021
        // During PING requests the operations store has to be switched constantly
2022
        // the cache prevents the same store opened several times
2023
        if (isset($this->storeCache[$user]))
2024
           return  $this->storeCache[$user];
2025
2026
        $entryid = false;
2027
        $return_public = false;
2028
2029
        if (strtoupper($user) == 'SYSTEM')
2030
            $return_public = true;
2031
2032
        // loop through the storestable if authenticated user of public folder
2033
        if ($user == $this->mainUser || $return_public === true) {
2034
            // Find the default store
2035
            $storestables = mapi_getmsgstorestable($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_getmsgstorestable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2035
            $storestables = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2036
            $result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2036
            $result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2037
2038
            if ($result == NOERROR){
2039
                $rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2039
                $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER));
Loading history...
2040
2041
                foreach($rows as $row) {
2042
                    if(!$return_public && isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) {
2043
                        $entryid = $row[PR_ENTRYID];
2044
                        break;
2045
                    }
2046
                    if ($return_public && isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
2047
                        $entryid = $row[PR_ENTRYID];
2048
                        break;
2049
                    }
2050
                }
2051
            }
2052
        }
2053
        else
2054
            $entryid = @mapi_msgstore_createentryid($this->defaultstore, $user);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_createentryid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2054
            $entryid = @/** @scrutinizer ignore-call */ mapi_msgstore_createentryid($this->defaultstore, $user);
Loading history...
2055
2056
        if($entryid) {
2057
            $store = @mapi_openmsgstore($this->session, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_openmsgstore was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2057
            $store = @/** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $entryid);
Loading history...
2058
2059
            if (!$store) {
2060
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->openMessageStore('%s'): Could not open store", $user));
2061
                return false;
2062
            }
2063
2064
            // add this store to the cache
2065
            if (!isset($this->storeCache[$user]))
2066
                $this->storeCache[$user] = $store;
2067
2068
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->openMessageStore('%s'): Found '%s' store: '%s'", $user, (($return_public)?'PUBLIC':'DEFAULT'),$store));
2069
            return $store;
2070
        }
2071
        else {
2072
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->openMessageStore('%s'): No store found for this user", $user));
2073
            return false;
2074
        }
2075
    }
2076
2077
    /**
2078
     * Checks if the logged in user has secretary permissions on a folder.
2079
     *
2080
     * @param resource $store
2081
     * @param string $folderid
2082
     *
2083
     * @access public
2084
     * @return boolean
2085
     */
2086
    public function HasSecretaryACLs($store, $folderid, $entryid = false) {
2087
        if (!$entryid) {
2088
            $entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2088
            $entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
Loading history...
2089
            if (!$entryid) {
2090
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, no entryid resolved for %s on store %s", $folderid, $store));
0 ignored issues
show
Bug introduced by
$store of type resource is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2090
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, no entryid resolved for %s on store %s", $folderid, /** @scrutinizer ignore-type */ $store));
Loading history...
2091
                return false;
2092
            }
2093
        }
2094
2095
        $folder = mapi_msgstore_openentry($store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2095
        $folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
2096
        if (!$folder) {
2097
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, could not open folder with entryid %s on store %s", bin2hex($entryid), $store));
2098
            return false;
2099
        }
2100
2101
        $props = mapi_getprops($folder, array(PR_RIGHTS));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2101
        $props = /** @scrutinizer ignore-call */ mapi_getprops($folder, array(PR_RIGHTS));
Loading history...
2102
        if (isset($props[PR_RIGHTS]) &&
2103
            ($props[PR_RIGHTS] & ecRightsReadAny) &&
2104
            ($props[PR_RIGHTS] & ecRightsCreate) &&
2105
            ($props[PR_RIGHTS] & ecRightsEditOwned) &&
2106
            ($props[PR_RIGHTS] & ecRightsDeleteOwned) &&
2107
            ($props[PR_RIGHTS] & ecRightsEditAny) &&
2108
            ($props[PR_RIGHTS] & ecRightsDeleteAny) &&
2109
            ($props[PR_RIGHTS] & ecRightsFolderVisible) ) {
2110
            return true;
2111
        }
2112
        return false;
2113
    }
2114
2115
    /**
2116
     * The meta function for out of office settings.
2117
     *
2118
     * @param SyncObject $oof
2119
     *
2120
     * @access private
2121
     * @return void
2122
     */
2123
    private function settingsOOF(&$oof) {
2124
        //if oof state is set it must be set of oof and get otherwise
2125
        if (isset($oof->oofstate)) {
2126
            $this->settingsOofSet($oof);
2127
        }
2128
        else {
2129
            $this->settingsOofGet($oof);
2130
        }
2131
    }
2132
2133
    /**
2134
     * Gets the out of office settings
2135
     *
2136
     * @param SyncObject $oof
2137
     *
2138
     * @access private
2139
     * @return void
2140
     */
2141
    private function settingsOofGet(&$oof) {
2142
        $oofprops = mapi_getprops($this->defaultstore, array(PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG, PR_EC_OUTOFOFFICE_SUBJECT, PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2142
        $oofprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, array(PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG, PR_EC_OUTOFOFFICE_SUBJECT, PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL));
Loading history...
2143
        $oof->oofstate = SYNC_SETTINGSOOF_DISABLED;
2144
        $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS;
2145
        if ($oofprops != false) {
2146
            $oof->oofstate = isset($oofprops[PR_EC_OUTOFOFFICE]) ? ($oofprops[PR_EC_OUTOFOFFICE] ? SYNC_SETTINGSOOF_GLOBAL : SYNC_SETTINGSOOF_DISABLED) : SYNC_SETTINGSOOF_DISABLED;
2147
            //TODO external and external unknown
2148
            $oofmessage = new SyncOOFMessage();
2149
            $oofmessage->appliesToInternal = "";
2150
            $oofmessage->enabled = $oof->oofstate;
2151
            $oofmessage->replymessage = (isset($oofprops[PR_EC_OUTOFOFFICE_MSG])) ? w2u($oofprops[PR_EC_OUTOFOFFICE_MSG]) : "";
2152
            $oofmessage->bodytype = $oof->bodytype;
2153
            unset($oofmessage->appliesToExternal, $oofmessage->appliesToExternalUnknown);
2154
            $oof->oofmessage[] = $oofmessage;
2155
2156
            // check whether time based out of office is set
2157
            if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL && isset($oofprops[PR_EC_OUTOFOFFICE_FROM], $oofprops[PR_EC_OUTOFOFFICE_UNTIL])) {
2158
                $now = time();
2159
                if ($now > $oofprops[PR_EC_OUTOFOFFICE_FROM] && $now > $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) {
2160
                    // Out of office is set but the date is in the past. Set the state to disabled.
2161
                    // @see https://jira.z-hub.io/browse/ZP-1188 for details
2162
                    $oof->oofstate = SYNC_SETTINGSOOF_DISABLED;
2163
                    @mapi_setprops($this->defaultstore, array(PR_EC_OUTOFOFFICE => false));
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2163
                    @/** @scrutinizer ignore-call */ mapi_setprops($this->defaultstore, array(PR_EC_OUTOFOFFICE => false));
Loading history...
2164
                    @mapi_deleteprops($this->defaultstore, array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL));
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2164
                    @/** @scrutinizer ignore-call */ mapi_deleteprops($this->defaultstore, array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL));
Loading history...
2165
                    ZLog::Write(LOGLEVEL_INFO, "Grommunio->settingsOofGet(): Out of office is set but the from and until are in the past. Disabling out of office.");
2166
                }
2167
                elseif ($oofprops[PR_EC_OUTOFOFFICE_FROM] < $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) {
2168
                    $oof->oofstate = SYNC_SETTINGSOOF_TIMEBASED;
2169
                    $oof->starttime = $oofprops[PR_EC_OUTOFOFFICE_FROM];
2170
                    $oof->endtime = $oofprops[PR_EC_OUTOFOFFICE_UNTIL];
2171
                }
2172
                else {
2173
                    ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->settingsOofGet(): Time based out of office set but end time ('%s') is before startime ('%s').",
2174
                        date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_FROM]), date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_UNTIL])));
2175
                    $oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2176
                }
2177
            }
2178
            elseif ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL && (isset($oofprops[PR_EC_OUTOFOFFICE_FROM]) || isset($oofprops[PR_EC_OUTOFOFFICE_UNTIL]))) {
2179
                ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->settingsOofGet(): Time based out of office set but either start time ('%s') or end time ('%s') is missing.",
2180
                    (isset($oofprops[PR_EC_OUTOFOFFICE_FROM]) ? date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_FROM]) : 'empty'),
2181
                    (isset($oofprops[PR_EC_OUTOFOFFICE_UNTIL]) ? date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) : 'empty')));
2182
                $oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2183
            }
2184
        }
2185
        else {
2186
            ZLog::Write(LOGLEVEL_WARN, "Grommunio->Unable to get out of office information");
2187
        }
2188
2189
        //unset body type for oof in order not to stream it
2190
        unset($oof->bodytype);
2191
    }
2192
2193
    /**
2194
     * Sets the out of office settings.
2195
     *
2196
     * @param SyncObject $oof
2197
     *
2198
     * @access private
2199
     * @return void
2200
     */
2201
    private function settingsOofSet(&$oof) {
2202
        $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS;
2203
        $props = array();
2204
        if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL || $oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) {
2205
            $props[PR_EC_OUTOFOFFICE] = true;
2206
            foreach ($oof->oofmessage as $oofmessage) {
2207
                if (isset($oofmessage->appliesToInternal)) {
2208
                    $props[PR_EC_OUTOFOFFICE_MSG] = isset($oofmessage->replymessage) ? u2w($oofmessage->replymessage) : "";
2209
                    $props[PR_EC_OUTOFOFFICE_SUBJECT] = "Out of office";
2210
                }
2211
            }
2212
            if ($oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) {
2213
                if(isset($oof->starttime) && isset($oof->endtime)) {
2214
                    $props[PR_EC_OUTOFOFFICE_FROM] = $oof->starttime;
2215
                    $props[PR_EC_OUTOFOFFICE_UNTIL] = $oof->endtime;
2216
                }
2217
                elseif (isset($oof->starttime) || isset($oof->endtime)) {
2218
                    $oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2219
                }
2220
            }
2221
            else {
2222
                $deleteProps = array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL);
2223
            }
2224
        }
2225
        elseif($oof->oofstate == SYNC_SETTINGSOOF_DISABLED) {
2226
            $props[PR_EC_OUTOFOFFICE] = false;
2227
            $deleteProps = array(PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL);
2228
        }
2229
2230
        if (!empty($props)) {
2231
            @mapi_setprops($this->defaultstore, $props);
1 ignored issue
show
Bug introduced by
The function mapi_setprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2231
            @/** @scrutinizer ignore-call */ mapi_setprops($this->defaultstore, $props);
Loading history...
2232
            $result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2232
            $result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2233
            if ($result != NOERROR) {
2234
                ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsOofSet(): Setting oof information failed (%X)", $result));
2235
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
2236
            }
2237
        }
2238
2239
        if (!empty($deleteProps)) {
2240
            @mapi_deleteprops($this->defaultstore, $deleteProps);
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2240
            @/** @scrutinizer ignore-call */ mapi_deleteprops($this->defaultstore, $deleteProps);
Loading history...
2241
        }
2242
2243
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
2244
    }
2245
2246
    /**
2247
     * Gets the user's email address from server
2248
     *
2249
     * @param SyncObject $userinformation
2250
     *
2251
     * @access private
2252
     * @return void
2253
     */
2254
    private function settingsUserInformation(&$userinformation) {
2255
        if (!isset($this->defaultstore) || !isset($this->mainUser)) {
2256
            ZLog::Write(LOGLEVEL_ERROR, "Grommunio->settingsUserInformation(): The store or user are not available for getting user information");
2257
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
2258
        }
2259
        $user = nsp_getuserinfo($this->mainUser);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2259
        $user = /** @scrutinizer ignore-call */ nsp_getuserinfo($this->mainUser);
Loading history...
2260
        if ($user != false) {
2261
            $userinformation->Status = SYNC_SETTINGSSTATUS_USERINFO_SUCCESS;
2262
            if (Request::GetProtocolVersion() >= 14.1) {
2263
                $account = new SyncAccount();
2264
                $emailaddresses = new SyncEmailAddresses();
2265
                $emailaddresses->smtpaddress[] = $user["primary_email"];
2266
                $emailaddresses->primarysmtpaddress = $user["primary_email"];
2267
                $account->emailaddresses = $emailaddresses;
2268
                $userinformation->accounts[] = $account;
2269
            }
2270
            else {
2271
                $userinformation->emailaddresses[] = $user["primary_email"];
2272
            }
2273
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
2274
        }
2275
        ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsUserInformation(): Getting user information failed: nsp_getuserinfo(%X)", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2275
        ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsUserInformation(): Getting user information failed: nsp_getuserinfo(%X)", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2276
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
2277
    }
2278
2279
    /**
2280
     * Gets the rights management templates from the server.
2281
     *
2282
     * @param SyncObject $rmTemplates
2283
     *
2284
     * @access private
2285
     * @return void
2286
     */
2287
    private function settingsRightsManagementTemplates(&$rmTemplates) {
2288
        /* Currently there is no information rights management feature in
2289
         * the grommunio backend, so just return the status and empty
2290
         * SyncRightsManagementTemplates tag.
2291
         * Once it's available, it would be something like:
2292
2293
        $rmTemplate = new SyncRightsManagementTemplate();
2294
        $rmTemplate->id = "some-template-id-eg-guid";
2295
        $rmTemplate->name = "Template name";
2296
        $rmTemplate->description = "What does the template do. E.g. it disables forward and reply.";
2297
        $rmTemplates->rmtemplates[] = $rmTemplate;
2298
         */
2299
        $rmTemplates->Status = SYNC_COMMONSTATUS_IRMFEATUREDISABLED;
2300
        $rmTemplates->rmtemplates = array();
2301
    }
2302
2303
    /**
2304
     * Sets the importance and priority of a message from a RFC822 message headers.
2305
     *
2306
     * @param int $xPriority
2307
     * @param array $mapiprops
2308
     *
2309
     * @return void
2310
     */
2311
    private function getImportanceAndPriority($xPriority, &$mapiprops, $sendMailProps) {
2312
        switch($xPriority) {
2313
            case 1:
2314
            case 2:
2315
                $priority = PRIO_URGENT;
2316
                $importance = IMPORTANCE_HIGH;
2317
                break;
2318
            case 4:
2319
            case 5:
2320
                $priority = PRIO_NONURGENT;
2321
                $importance = IMPORTANCE_LOW;
2322
                break;
2323
            case 3:
2324
            default:
2325
                $priority = PRIO_NORMAL;
2326
                $importance = IMPORTANCE_NORMAL;
2327
                break;
2328
        }
2329
        $mapiprops[$sendMailProps["importance"]] = $importance;
2330
        $mapiprops[$sendMailProps["priority"]] = $priority;
2331
    }
2332
2333
    /**
2334
     * Copies attachments from one message to another.
2335
     *
2336
     * @param MAPIMessage $toMessage
2337
     * @param MAPIMessage $fromMessage
2338
     *
2339
     * @return void
2340
     */
2341
    private function copyAttachments(&$toMessage, $fromMessage) {
2342
        $attachtable = mapi_message_getattachmenttable($fromMessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getattachmenttable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2342
        $attachtable = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($fromMessage);
Loading history...
2343
        $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2343
        $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
Loading history...
2344
2345
        foreach($rows as $row) {
2346
            if(isset($row[PR_ATTACH_NUM])) {
2347
                $attach = mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2347
                $attach = /** @scrutinizer ignore-call */ mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]);
Loading history...
2348
                $newattach = mapi_message_createattach($toMessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_createattach was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2348
                $newattach = /** @scrutinizer ignore-call */ mapi_message_createattach($toMessage);
Loading history...
2349
                mapi_copyto($attach, array(), array(), $newattach, 0);
1 ignored issue
show
Bug introduced by
The function mapi_copyto was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2349
                /** @scrutinizer ignore-call */ 
2350
                mapi_copyto($attach, array(), array(), $newattach, 0);
Loading history...
2350
                mapi_savechanges($newattach);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2350
                /** @scrutinizer ignore-call */ 
2351
                mapi_savechanges($newattach);
Loading history...
2351
            }
2352
        }
2353
    }
2354
2355
   /**
2356
    * Function will create a search folder in FINDER_ROOT folder
2357
    * if folder exists then it will open it
2358
    *
2359
    * @see createSearchFolder($store, $openIfExists = true) function in the webaccess
2360
    *
2361
    * @return mapiFolderObject $folder created search folder
0 ignored issues
show
Bug introduced by
The type mapiFolderObject 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2362
    */
2363
    private function getSearchFolder() {
2364
        // create new or open existing search folder
2365
        $searchFolderRoot = $this->getSearchFoldersRoot($this->store);
0 ignored issues
show
Unused Code introduced by
The call to Grommunio::getSearchFoldersRoot() has too many arguments starting with $this->store. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2365
        /** @scrutinizer ignore-call */ 
2366
        $searchFolderRoot = $this->getSearchFoldersRoot($this->store);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2366
        if($searchFolderRoot === false) {
2367
            // error in finding search root folder
2368
            // or store doesn't support search folders
2369
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2370
        }
2371
2372
        $searchFolder = $this->createSearchFolder($searchFolderRoot);
2373
2374
        if($searchFolder !== false && mapi_last_hresult() == NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2374
        if($searchFolder !== false && /** @scrutinizer ignore-call */ mapi_last_hresult() == NOERROR) {
Loading history...
2375
            return $searchFolder;
2376
        }
2377
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2378
    }
2379
2380
   /**
2381
    * Function will open FINDER_ROOT folder in root container
2382
    * public folder's don't have FINDER_ROOT folder
2383
    *
2384
    * @see getSearchFoldersRoot($store) function in the webaccess
2385
    *
2386
    * @return mapiFolderObject root folder for search folders
2387
    */
2388
    private function getSearchFoldersRoot() {
2389
        // check if we can create search folders
2390
        $storeProps = mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2390
        $storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID));
Loading history...
2391
        if(($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) {
2392
            ZLog::Write(LOGLEVEL_WARN, "Grommunio->getSearchFoldersRoot(): Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder");
2393
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2394
        }
2395
2396
        // open search folders root
2397
        $searchRootFolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2397
        $searchRootFolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
Loading history...
2398
        if(mapi_last_hresult() != NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2398
        if(/** @scrutinizer ignore-call */ mapi_last_hresult() != NOERROR) {
Loading history...
2399
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getSearchFoldersRoot(): Unable to open search folder (0x%X)", mapi_last_hresult()));
2400
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2401
        }
2402
2403
        return $searchRootFolder;
2404
    }
2405
2406
2407
    /**
2408
     * Creates a search folder if it not exists or opens an existing one
2409
     * and returns it.
2410
     *
2411
     * @param mapiFolderObject $searchFolderRoot
2412
     *
2413
     * @return mapiFolderObject
2414
     */
2415
    private function createSearchFolder($searchFolderRoot) {
2416
        $folderName = "grommunio-sync Search Folder ".@getmypid();
0 ignored issues
show
Bug introduced by
Are you sure @getmypid() of type false|integer can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2416
        $folderName = "grommunio-sync Search Folder "./** @scrutinizer ignore-type */ @getmypid();
Loading history...
2417
        $searchFolders = mapi_folder_gethierarchytable($searchFolderRoot);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2417
        $searchFolders = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($searchFolderRoot);
Loading history...
2418
        $restriction = array(
2419
            RES_CONTENT,
2420
            array(
2421
                    FUZZYLEVEL      => FL_PREFIX,
2422
                    ULPROPTAG       => PR_DISPLAY_NAME,
2423
                    VALUE           => array(PR_DISPLAY_NAME=>$folderName)
2424
            )
2425
        );
2426
        //restrict the hierarchy to the grommunio-sync search folder only
2427
        mapi_table_restrict($searchFolders, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2427
        /** @scrutinizer ignore-call */ 
2428
        mapi_table_restrict($searchFolders, $restriction);
Loading history...
2428
        if (mapi_table_getrowcount($searchFolders)) {
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2428
        if (/** @scrutinizer ignore-call */ mapi_table_getrowcount($searchFolders)) {
Loading history...
2429
            $searchFolder = mapi_table_queryrows($searchFolders, array(PR_ENTRYID), 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2429
            $searchFolder = /** @scrutinizer ignore-call */ mapi_table_queryrows($searchFolders, array(PR_ENTRYID), 0, 1);
Loading history...
2430
2431
            return mapi_msgstore_openentry($this->store, $searchFolder[0][PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2431
            return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $searchFolder[0][PR_ENTRYID]);
Loading history...
2432
        }
2433
        return mapi_folder_createfolder($searchFolderRoot, $folderName, null, 0, FOLDER_SEARCH);
0 ignored issues
show
Bug introduced by
The function mapi_folder_createfolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2433
        return /** @scrutinizer ignore-call */ mapi_folder_createfolder($searchFolderRoot, $folderName, null, 0, FOLDER_SEARCH);
Loading history...
2434
    }
2435
2436
    /**
2437
     * Creates a search restriction
2438
     *
2439
     * @param ContentParameter $cpo
2440
     * @return array
2441
     */
2442
    private function getSearchRestriction($cpo) {
2443
        $searchText = $cpo->GetSearchFreeText();
2444
2445
        $searchGreater = strtotime($cpo->GetSearchValueGreater());
2446
        $searchLess = strtotime($cpo->GetSearchValueLess());
2447
2448
        if (version_compare(phpversion(),'5.3.4') < 0) {
2449
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getSearchRestriction(): Your system's PHP version (%s) might not correctly process unicode strings. Search containing such characters might not return correct results. It is recommended to update to at least PHP 5.3.4. See ZP-541 for more information.", phpversion()));
2450
        }
2451
        // split the search on whitespache and look for every word
2452
        $searchText = preg_split("/\W+/u", $searchText);
2453
        $searchProps = array(PR_BODY, PR_SUBJECT, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS);
2454
        $resAnd = array();
2455
        foreach($searchText as $term) {
2456
            $resOr = array();
2457
2458
            foreach($searchProps as $property) {
2459
                array_push($resOr,
2460
                    array(RES_CONTENT,
2461
                        array(
2462
                            FUZZYLEVEL => FL_SUBSTRING|FL_IGNORECASE,
2463
                            ULPROPTAG => $property,
2464
                            VALUE => u2w($term)
2465
                        )
2466
                    )
2467
                );
2468
            }
2469
            array_push($resAnd, array(RES_OR, $resOr));
2470
        }
2471
2472
        // add time range restrictions
2473
        if ($searchGreater) {
2474
            array_push($resAnd, array(RES_PROPERTY, array(RELOP => RELOP_GE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => array(PR_MESSAGE_DELIVERY_TIME => $searchGreater)))); // RES_AND;
2475
        }
2476
        if ($searchLess) {
2477
            array_push($resAnd, array(RES_PROPERTY, array(RELOP => RELOP_LE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => array(PR_MESSAGE_DELIVERY_TIME => $searchLess))));
2478
        }
2479
        $mapiquery = array(RES_AND, $resAnd);
2480
2481
        return $mapiquery;
2482
    }
2483
2484
    /**
2485
     * Resolve recipient based on his email address.
2486
     *
2487
     * @param string $to
2488
     * @param int $maxAmbiguousRecipients
2489
     * @param boolean $expandDistlist
2490
     *
2491
     * @return SyncResolveRecipient|boolean
2492
     */
2493
    private function resolveRecipient($to, $maxAmbiguousRecipients, $expandDistlist = true) {
2494
        $recipient = $this->resolveRecipientGAL($to, $maxAmbiguousRecipients, $expandDistlist);
2495
2496
        if ($recipient !== false) {
0 ignored issues
show
introduced by
The condition $recipient !== false is always true.
Loading history...
2497
            return $recipient;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $recipient returns the type array which is incompatible with the documented return type SyncResolveRecipient|boolean.
Loading history...
2498
        }
2499
2500
        $recipient = $this->resolveRecipientContact($to, $maxAmbiguousRecipients);
2501
2502
        if ($recipient !== false) {
2503
            return $recipient;
2504
        }
2505
2506
        return false;
2507
    }
2508
2509
    /**
2510
     * Resolves recipient from the GAL and gets his certificates.
2511
     *
2512
     * @param string $to
2513
     * @param int $maxAmbiguousRecipients
2514
     * @param boolean $expandDistlist
2515
     * @return array|boolean
2516
     */
2517
    private function resolveRecipientGAL($to, $maxAmbiguousRecipients, $expandDistlist = true) {
2518
        ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientGAL(): Resolving recipient '%s' in GAL", $to));
2519
        $addrbook = $this->getAddressbook();
2520
        // FIXME: create a function to get the adressbook contentstable
2521
        $ab_entryid = mapi_ab_getdefaultdir($addrbook);
0 ignored issues
show
Bug introduced by
The function mapi_ab_getdefaultdir was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2521
        $ab_entryid = /** @scrutinizer ignore-call */ mapi_ab_getdefaultdir($addrbook);
Loading history...
2522
        if ($ab_entryid)
2523
            $ab_dir = mapi_ab_openentry($addrbook, $ab_entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2523
            $ab_dir = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $ab_entryid);
Loading history...
2524
        if ($ab_dir)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_dir does not seem to be defined for all execution paths leading up to this point.
Loading history...
2525
            $table = mapi_folder_getcontentstable($ab_dir);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2525
            $table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($ab_dir);
Loading history...
2526
2527
        if (!$table) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table does not seem to be defined for all execution paths leading up to this point.
Loading history...
2528
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): Unable to open addressbook:0x%X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2528
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): Unable to open addressbook:0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2529
            return false;
2530
        }
2531
2532
        $restriction = MAPIUtils::GetSearchRestriction(u2w($to));
0 ignored issues
show
Bug introduced by
It seems like u2w($to) can also be of type false; however, parameter $query of MAPIUtils::GetSearchRestriction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2532
        $restriction = MAPIUtils::GetSearchRestriction(/** @scrutinizer ignore-type */ u2w($to));
Loading history...
2533
        mapi_table_restrict($table, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2533
        /** @scrutinizer ignore-call */ 
2534
        mapi_table_restrict($table, $restriction);
Loading history...
2534
2535
        $querycnt = mapi_table_getrowcount($table);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2535
        $querycnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($table);
Loading history...
2536
        if ($querycnt > 0) {
2537
            $recipientGal = array();
2538
            $rowsToQuery = $maxAmbiguousRecipients;
2539
            // some devices request 0 ambiguous recipients
2540
            if ($querycnt == 1 && $maxAmbiguousRecipients == 0) {
2541
                $rowsToQuery = 1;
2542
            }
2543
            elseif ($querycnt > 1 && $maxAmbiguousRecipients == 0) {
2544
                ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->resolveRecipientGAL(): GAL search found %d recipients but the device hasn't requested ambiguous recipients", $querycnt));
2545
                return $recipientGal;
2546
            }
2547
            elseif ($querycnt > 1 && $maxAmbiguousRecipients == 1) {
2548
                $rowsToQuery = $querycnt;
2549
            }
2550
            // get the certificate every time because caching the certificate is less expensive than opening addressbook entry again
2551
            $abentries = mapi_table_queryrows($table, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT, PR_OBJECT_TYPE, PR_SMTP_ADDRESS), 0, $rowsToQuery);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2551
            $abentries = /** @scrutinizer ignore-call */ mapi_table_queryrows($table, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT, PR_OBJECT_TYPE, PR_SMTP_ADDRESS), 0, $rowsToQuery);
Loading history...
2552
            for ($i = 0, $nrEntries = count($abentries); $i < $nrEntries; $i++) {
2553
                if (strcasecmp($abentries[$i][PR_SMTP_ADDRESS], $to) !== 0 && $maxAmbiguousRecipients == 1) {
2554
                    ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->resolveRecipientGAL(): maxAmbiguousRecipients is 1 and found non-matching user (to '%s' found: '%s')", $to, $abentries[$i][PR_SMTP_ADDRESS]));
2555
                    continue;
2556
                }
2557
                if ($abentries[$i][PR_OBJECT_TYPE] == MAPI_DISTLIST) {
2558
                    // check whether to expand dist list
2559
                    if ($expandDistlist) {
2560
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->resolveRecipientGAL(): '%s' is a dist list. Expand it to members.", $to));
2561
                        $distList = mapi_ab_openentry($addrbook, $abentries[$i][PR_ENTRYID]);
2562
                        $distListContent = mapi_folder_getcontentstable($distList);
2563
                        $distListMembers = mapi_table_queryallrows($distListContent, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2563
                        $distListMembers = /** @scrutinizer ignore-call */ mapi_table_queryallrows($distListContent, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT));
Loading history...
2564
                        for ($j = 0, $nrDistListMembers = mapi_table_getrowcount($distListContent); $j < $nrDistListMembers; $j++) {
2565
                            ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientGAL(): distlist's '%s' member: '%s'", $to, $distListMembers[$j][PR_DISPLAY_NAME]));
2566
                            $recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $to, $distListMembers[$j], $nrDistListMembers);
2567
                        }
2568
                    }
2569
                    else {
2570
                        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->resolveRecipientGAL(): '%s' is a dist list, but return it as is.", $to));
2571
                        $recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $abentries[$i][PR_SMTP_ADDRESS], $abentries[$i]);
2572
                    }
2573
                }
2574
                elseif ($abentries[$i][PR_OBJECT_TYPE] == MAPI_MAILUSER) {
2575
                    $recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $abentries[$i][PR_SMTP_ADDRESS], $abentries[$i]);
2576
                }
2577
            }
2578
2579
            ZLog::Write(LOGLEVEL_WBXML, "Grommunio->resolveRecipientGAL(): Found a recipient in GAL");
2580
            return $recipientGal;
2581
        }
2582
        else {
2583
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): No recipient found for: '%s' in GAL", $to));
2584
            return SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_RESOLVERECIP...ESPONSE_UNRESOLVEDRECIP returns the type integer which is incompatible with the documented return type array|boolean.
Loading history...
2585
        }
2586
        return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2587
    }
2588
2589
    /**
2590
     * Resolves recipient from the contact list and gets his certificates.
2591
     *
2592
     * @param string $to
2593
     * @param int $maxAmbiguousRecipients
2594
     *
2595
     * @return array|boolean
2596
     */
2597
    private function resolveRecipientContact($to, $maxAmbiguousRecipients) {
2598
        ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Resolving recipient '%s' in user's contacts", $to));
2599
        // go through all contact folders of the user and
2600
        // check if there's a contact with the given email address
2601
        $root = mapi_msgstore_openentry($this->defaultstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2601
        $root = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore);
Loading history...
2602
        if (!$root) {
2603
            ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->resolveRecipientContact(): Unable to open default store: 0x%X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2603
            ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->resolveRecipientContact(): Unable to open default store: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2604
        }
2605
        $rootprops = mapi_getprops($root, array(PR_IPM_CONTACT_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2605
        $rootprops = /** @scrutinizer ignore-call */ mapi_getprops($root, array(PR_IPM_CONTACT_ENTRYID));
Loading history...
2606
        $contacts = $this->getContactsFromFolder($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID], $to);
2607
        $recipients = array();
2608
2609
        if ($contacts !== false) {
2610
            ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in main contacts folder.", count($contacts)));
0 ignored issues
show
Bug introduced by
It seems like $contacts can also be of type true; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2610
            ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in main contacts folder.", count(/** @scrutinizer ignore-type */ $contacts)));
Loading history...
2611
            // create resolve recipient object
2612
            foreach ($contacts as $contact) {
2613
                $recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2614
            }
2615
        }
2616
2617
        $contactfolder = mapi_msgstore_openentry($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID]);
2618
        $subfolders = MAPIUtils::GetSubfoldersForType($contactfolder, "IPF.Contact");
2619
        if ($subfolders !== false) {
2620
            foreach($subfolders as $folder) {
2621
                $contacts = $this->getContactsFromFolder($this->defaultstore, $folder[PR_ENTRYID], $to);
2622
                if ($contacts !== false) {
2623
                    ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in contacts' subfolder.", count($contacts)));
2624
                    foreach ($contacts as $contact) {
2625
                        $recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2626
                    }
2627
                }
2628
            }
2629
        }
2630
2631
        // search contacts in public folders
2632
        $storestables = mapi_getmsgstorestable($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_getmsgstorestable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2632
        $storestables = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2633
        $result = mapi_last_hresult();
2634
2635
        if ($result == NOERROR){
2636
            $rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2636
            $rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER));
Loading history...
2637
            foreach($rows as $row) {
2638
                if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
2639
                    // TODO refactor public store
2640
                    $publicstore = mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_openmsgstore was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2640
                    $publicstore = /** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
Loading history...
2641
                    $publicfolder = mapi_msgstore_openentry($publicstore);
2642
2643
                    $subfolders = MAPIUtils::GetSubfoldersForType($publicfolder, "IPF.Contact");
2644
                    if ($subfolders !== false) {
2645
                        foreach($subfolders as $folder) {
2646
                            $contacts = $this->getContactsFromFolder($publicstore, $folder[PR_ENTRYID], $to);
2647
                            if ($contacts !== false) {
2648
                                ZLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in public contacts folder.", count($contacts)));
2649
                                foreach ($contacts as $contact) {
2650
                                    $recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2651
                                }
2652
                            }
2653
                        }
2654
                    }
2655
                    break;
2656
                }
2657
            }
2658
        }
2659
        else {
2660
            ZLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientContact(): Unable to open public store: 0x%X", $result));
2661
        }
2662
2663
        if (empty($recipients)) {
2664
            $contactProperties = array();
2665
            $contactProperties[PR_DISPLAY_NAME] = $to;
2666
            $contactProperties[PR_USER_X509_CERTIFICATE] = false;
2667
2668
            $recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contactProperties);
2669
        }
2670
        return $recipients;
2671
    }
2672
2673
    /**
2674
     * Creates SyncResolveRecipientsCertificates object for ResolveRecipients
2675
     *
2676
     * @param binary $certificates
0 ignored issues
show
Bug introduced by
The type binary 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2677
     * @param int $recipientCount
2678
     *
2679
     * @return SyncResolveRecipientsCertificates
2680
     */
2681
    private function getCertificates($certificates, $recipientCount = 0) {
2682
        $cert = new SyncResolveRecipientsCertificates();
2683
        if ($certificates === false) {
0 ignored issues
show
introduced by
The condition $certificates === false is always false.
Loading history...
2684
            $cert->status = SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_NOVALIDCERT;
2685
            return $cert;
2686
        }
2687
        $cert->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
2688
        $cert->certificatecount = count ($certificates);
2689
        $cert->recipientcount = $recipientCount;
2690
        $cert->certificate = array();
2691
        foreach ($certificates as $certificate) {
2692
            $cert->certificate[] = base64_encode($certificate);
2693
        }
2694
        return $cert;
2695
    }
2696
2697
    /**
2698
     * Creates SyncResolveRecipient object for ResolveRecipientsResponse.
2699
     * @param int $type
2700
     * @param string $email
2701
     * @param array $recipientProperties
2702
     * @param int $recipientCount
2703
     *
2704
     * @return SyncResolveRecipient
2705
     */
2706
    private function createResolveRecipient($type, $email, $recipientProperties, $recipientCount = 0) {
2707
        $recipient = new SyncResolveRecipient();
2708
        $recipient->type = $type;
2709
        $recipient->displayname = u2w($recipientProperties[PR_DISPLAY_NAME]);
2710
        $recipient->emailaddress = $email;
2711
2712
        if ($type == SYNC_RESOLVERECIPIENTS_TYPE_GAL) {
2713
            $certificateProp = PR_EMS_AB_TAGGED_X509_CERT;
2714
        }
2715
        elseif ($type == SYNC_RESOLVERECIPIENTS_TYPE_CONTACT) {
2716
            $certificateProp = PR_USER_X509_CERTIFICATE;
2717
        }
2718
        else {
2719
            $certificateProp = null;
2720
        }
2721
2722
        if (isset($recipientProperties[$certificateProp]) && is_array($recipientProperties[$certificateProp]) && !empty($recipientProperties[$certificateProp])) {
2723
            $certificates = $this->getCertificates($recipientProperties[$certificateProp], $recipientCount);
2724
        }
2725
        else {
2726
            $certificates = $this->getCertificates(false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type binary expected by parameter $certificates of Grommunio::getCertificates(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2726
            $certificates = $this->getCertificates(/** @scrutinizer ignore-type */ false);
Loading history...
2727
            ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->createResolveRecipient(): No certificate found for '%s' (requested email address: '%s')", $recipientProperties[PR_DISPLAY_NAME], $email));
2728
        }
2729
        $recipient->certificates = $certificates;
2730
2731
        if (isset($recipientProperties[PR_ENTRYID])) {
2732
            $recipient->id = $recipientProperties[PR_ENTRYID];
2733
        }
2734
        return $recipient;
2735
    }
2736
2737
    /**
2738
     * Gets the availability of a user for the given time window.
2739
     *
2740
     * @param string $to
2741
     * @param SyncResolveRecipient $resolveRecipient
2742
     * @param SyncResolveRecipientsOptions $resolveRecipientsOptions
2743
     *
2744
     * @access private
2745
     * @return SyncResolveRecipientsAvailability
2746
     */
2747
    private function getAvailability($to, $resolveRecipient, $resolveRecipientsOptions) {
2748
        $availability = new SyncResolveRecipientsAvailability();
2749
        $availability->status = SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_SUCCESS;
2750
2751
        if (!isset($resolveRecipient->id)) {
2752
            // TODO this shouldn't happen but try to get the recipient in such a case
2753
        }
2754
2755
        $start = strtotime($resolveRecipientsOptions->availability->starttime);
2756
        $end = strtotime($resolveRecipientsOptions->availability->endtime);
2757
        // Each digit in the MergedFreeBusy indicates the free/busy status for the user for every 30 minute interval.
2758
        $timeslots = intval(ceil(($end - $start) / self::HALFHOURSECONDS));
2759
2760
        if ($timeslots > self::MAXFREEBUSYSLOTS) {
2761
            throw new StatusException("Grommunio->getAvailability(): the requested free busy range is too large.", SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR);
2762
        }
2763
2764
        $mergedFreeBusy = str_pad(fbNoData, $timeslots, fbNoData);
2765
2766
        $retval = mapi_getuseravailability($this->session, $resolveRecipient->id, $start, $end);
0 ignored issues
show
Bug introduced by
The function mapi_getuseravailability was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2766
        $retval = /** @scrutinizer ignore-call */ mapi_getuseravailability($this->session, $resolveRecipient->id, $start, $end);
Loading history...
2767
        ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getAvailability(): free busy '%s'", print_r($retval, 1)));
0 ignored issues
show
Bug introduced by
It seems like print_r($retval, 1) can also be of type true; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2767
        ZLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getAvailability(): free busy '%s'", /** @scrutinizer ignore-type */ print_r($retval, 1)));
Loading history...
2768
2769
        if (!empty($retval)) {
2770
            $freebusy = json_decode($retval, true);
2771
            // freebusy is available, assume that the user is free
2772
            $mergedFreeBusy = str_pad(fbFree, $timeslots, fbFree);
2773
            foreach ($freebusy['events'] as $event) {
2774
                // calculate which timeslot of mergedFreeBusy should be replaced.
2775
                $startSlot = intval(floor(($event['StartTime'] - $start) / self::HALFHOURSECONDS));
2776
                $endSlot = intval(floor(($event['EndTime'] - $start) / self::HALFHOURSECONDS));
2777
                // if event started at a multiple of half an hour from requested freebusy time and
2778
                // its duration is also a multiple of half an hour
2779
                // then it's necessary to reduce endSlot by one
2780
                if ((($event['StartTime'] - $start) % self::HALFHOURSECONDS == 0) && (($event['EndTime'] - $event['StartTime']) % self::HALFHOURSECONDS == 0)) {
2781
                    $endSlot--;
2782
                }
2783
                $fbType = Utils::GetFbStatusFromType($event['BusyType']);
2784
                for ($i = $startSlot; $i <= $endSlot && $i < $timeslots; $i++) {
2785
                    // only set the new slot's free busy status if it's higher than the current one
2786
                    if ($fbType > $mergedFreeBusy[$i]) {
2787
                        $mergedFreeBusy[$i] = $fbType;
2788
                    }
2789
                }
2790
            }
2791
        }
2792
        $availability->mergedfreebusy = $mergedFreeBusy;
2793
        return $availability;
2794
    }
2795
2796
    /**
2797
     * Returns contacts matching given email address from a folder.
2798
     *
2799
     * @param MAPIStore $store
0 ignored issues
show
Bug introduced by
The type MAPIStore was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2800
     * @param binary $folderEntryid
2801
     * @param string $email
2802
     *
2803
     * @return array|boolean
2804
     */
2805
    private function getContactsFromFolder($store, $folderEntryid, $email) {
2806
        $folder = mapi_msgstore_openentry($store, $folderEntryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2806
        $folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $folderEntryid);
Loading history...
2807
        $folderContent = mapi_folder_getcontentstable($folder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2807
        $folderContent = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($folder);
Loading history...
2808
        mapi_table_restrict($folderContent, MAPIUtils::GetEmailAddressRestriction($store, $email));
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2808
        /** @scrutinizer ignore-call */ 
2809
        mapi_table_restrict($folderContent, MAPIUtils::GetEmailAddressRestriction($store, $email));
Loading history...
2809
        // TODO max limit
2810
        if (mapi_table_getrowcount($folderContent) > 0) {
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2810
        if (/** @scrutinizer ignore-call */ mapi_table_getrowcount($folderContent) > 0) {
Loading history...
2811
            return mapi_table_queryallrows($folderContent, array(PR_DISPLAY_NAME, PR_USER_X509_CERTIFICATE, PR_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2811
            return /** @scrutinizer ignore-call */ mapi_table_queryallrows($folderContent, array(PR_DISPLAY_NAME, PR_USER_X509_CERTIFICATE, PR_ENTRYID));
Loading history...
2812
        }
2813
        return false;
2814
    }
2815
2816
    /**
2817
     * Get MAPI addressbook object
2818
     *
2819
     * @access private
2820
     * @return MAPIAddressbook object to be used with mapi_ab_* or false on failure
0 ignored issues
show
Bug introduced by
The type MAPIAddressbook 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. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2821
     */
2822
    private function getAddressbook() {
2823
        if (isset($this->addressbook) && $this->addressbook) {
2824
            return $this->addressbook;
2825
        }
2826
        $this->addressbook = mapi_openaddressbook($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_openaddressbook was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2826
        $this->addressbook = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2827
        $result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2827
        $result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2828
        if ($result && $this->addressbook === false) {
2829
            ZLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->getAddressbook error opening addressbook 0x%X", $result));
2830
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type MAPIAddressbook.
Loading history...
2831
        }
2832
        return $this->addressbook;
2833
    }
2834
2835
    /**
2836
     * Checks if the user is not disabled for grommunio-sync.
2837
     *
2838
     * @access private
2839
     * @throws FatalException if user is disabled for grommunio-sync
2840
     *
2841
     * @return boolean
2842
     */
2843
    private function isZPushEnabled() {
2844
        $addressbook = $this->getAddressbook();
2845
        // this check needs to be performed on the store of the main (authenticated) user
2846
        $store = $this->storeCache[$this->mainUser];
2847
        $userEntryid = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2847
        $userEntryid = /** @scrutinizer ignore-call */ mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID));
Loading history...
2848
        $mailuser = mapi_ab_openentry($addressbook, $userEntryid[PR_MAILBOX_OWNER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2848
        $mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($addressbook, $userEntryid[PR_MAILBOX_OWNER_ENTRYID]);
Loading history...
2849
        $enabledFeatures = mapi_getprops($mailuser, array(PR_EC_DISABLED_FEATURES));
2850
        if (isset($enabledFeatures[PR_EC_DISABLED_FEATURES]) && is_array($enabledFeatures[PR_EC_DISABLED_FEATURES])) {
2851
            $mobileDisabled = in_array(self::MOBILE_ENABLED, $enabledFeatures[PR_EC_DISABLED_FEATURES]);
2852
            $deviceId = Request::GetDeviceID();
2853
            // Checks for deviceId present in zarafaDisabledFeatures LDAP array attribute. Check is performed case insensitive.
2854
            $deviceIdDisabled = ( ($deviceId !==null) && in_array($deviceId, array_map('strtolower', $enabledFeatures[PR_EC_DISABLED_FEATURES])) )? true : false;
2855
            if ($mobileDisabled) {
2856
                throw new FatalException("User is disabled for grommunio-sync.");
2857
            }
2858
            elseif ($deviceIdDisabled) {
2859
                throw new FatalException(sprintf("User has deviceId %s disabled for usage with grommunio-sync.", $deviceId));
2860
            }
2861
        }
2862
        return true;
2863
    }
2864
}
2865