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

ZPush::CheckConfig()   F

Complexity

Conditions 34
Paths 3680

Size

Total Lines 101
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 34
eloc 58
nc 3680
nop 0
dl 0
loc 101
rs 0
c 2
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * Core functionalities
8
 */
9
10
class ZPush {
11
    const UNAUTHENTICATED = 1;
12
    const UNPROVISIONED = 2;
13
    const NOACTIVESYNCCOMMAND = 3;
14
    const WEBSERVICECOMMAND = 4;    // DEPRECATED
15
    const HIERARCHYCOMMAND = 5;
16
    const PLAININPUT = 6;
17
    const REQUESTHANDLER = 7;
18
    const CLASS_NAME = 1;
19
    const CLASS_REQUIRESPROTOCOLVERSION = 2;
20
    const CLASS_DEFAULTTYPE = 3;
21
    const CLASS_OTHERTYPES = 4;
22
23
    // AS versions
24
    const ASV_1 = "1.0";
25
    const ASV_2 = "2.0";
26
    const ASV_21 = "2.1";
27
    const ASV_25 = "2.5";
28
    const ASV_12 = "12.0";
29
    const ASV_121 = "12.1";
30
    const ASV_14 = "14.0";
31
    const ASV_141 = "14.1";
32
33
    /**
34
     * Command codes for base64 encoded requests (AS >= 12.1)
35
     */
36
    const COMMAND_SYNC = 0;
37
    const COMMAND_SENDMAIL = 1;
38
    const COMMAND_SMARTFORWARD = 2;
39
    const COMMAND_SMARTREPLY = 3;
40
    const COMMAND_GETATTACHMENT = 4;
41
    const COMMAND_FOLDERSYNC = 9;
42
    const COMMAND_FOLDERCREATE = 10;
43
    const COMMAND_FOLDERDELETE = 11;
44
    const COMMAND_FOLDERUPDATE = 12;
45
    const COMMAND_MOVEITEMS = 13;
46
    const COMMAND_GETITEMESTIMATE = 14;
47
    const COMMAND_MEETINGRESPONSE = 15;
48
    const COMMAND_SEARCH = 16;
49
    const COMMAND_SETTINGS = 17;
50
    const COMMAND_PING = 18;
51
    const COMMAND_ITEMOPERATIONS = 19;
52
    const COMMAND_PROVISION = 20;
53
    const COMMAND_RESOLVERECIPIENTS = 21;
54
    const COMMAND_VALIDATECERT = 22;
55
56
    // Deprecated commands
57
    const COMMAND_GETHIERARCHY = -1;
58
    const COMMAND_CREATECOLLECTION = -2;
59
    const COMMAND_DELETECOLLECTION = -3;
60
    const COMMAND_MOVECOLLECTION = -4;
61
    const COMMAND_NOTIFY = -5;
62
63
    // Latest supported State version
64
    const STATE_VERSION = IStateMachine::STATEVERSION_02;
65
66
    // Versions 1.0, 2.0, 2.1 and 2.5 are deprecated (ZP-604)
67
    static private $supportedASVersions = array(
68
                    self::ASV_12,
69
                    self::ASV_121,
70
                    self::ASV_14,
71
                    self::ASV_141
72
                );
73
74
    static private $supportedCommands = array(
75
                    // COMMAND                             // AS VERSION   // REQUESTHANDLER                        // OTHER SETTINGS
76
                    self::COMMAND_SYNC              => array(self::ASV_1,  self::REQUESTHANDLER => "Sync"),
77
                    self::COMMAND_SENDMAIL          => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
78
                    self::COMMAND_SMARTFORWARD      => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
79
                    self::COMMAND_SMARTREPLY        => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
80
                    self::COMMAND_GETATTACHMENT     => array(self::ASV_1,  self::REQUESTHANDLER => "GetAttachment"),
81
                    self::COMMAND_GETHIERARCHY      => array(self::ASV_1,  self::REQUESTHANDLER => "GetHierarchy",  self::HIERARCHYCOMMAND),            // deprecated but implemented
82
                    self::COMMAND_CREATECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
83
                    self::COMMAND_DELETECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
84
                    self::COMMAND_MOVECOLLECTION    => array(self::ASV_1),                                                                              // deprecated & not implemented
85
                    self::COMMAND_FOLDERSYNC        => array(self::ASV_2,  self::REQUESTHANDLER => "FolderSync",    self::HIERARCHYCOMMAND),
86
                    self::COMMAND_FOLDERCREATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
87
                    self::COMMAND_FOLDERDELETE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
88
                    self::COMMAND_FOLDERUPDATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
89
                    self::COMMAND_MOVEITEMS         => array(self::ASV_1,  self::REQUESTHANDLER => "MoveItems"),
90
                    self::COMMAND_GETITEMESTIMATE   => array(self::ASV_1,  self::REQUESTHANDLER => "GetItemEstimate"),
91
                    self::COMMAND_MEETINGRESPONSE   => array(self::ASV_1,  self::REQUESTHANDLER => "MeetingResponse"),
92
                    self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_1,  self::REQUESTHANDLER => "ResolveRecipients"),
93
                    self::COMMAND_VALIDATECERT      => array(self::ASV_1,  self::REQUESTHANDLER => "ValidateCert"),
94
                    self::COMMAND_PROVISION         => array(self::ASV_25, self::REQUESTHANDLER => "Provisioning",  self::UNAUTHENTICATED, self::UNPROVISIONED),
95
                    self::COMMAND_SEARCH            => array(self::ASV_1,  self::REQUESTHANDLER => "Search"),
96
                    self::COMMAND_PING              => array(self::ASV_2,  self::REQUESTHANDLER => "Ping",          self::UNPROVISIONED),
97
                    self::COMMAND_NOTIFY            => array(self::ASV_1,  self::REQUESTHANDLER => "Notify"),                                           // deprecated & not implemented
98
                    self::COMMAND_ITEMOPERATIONS    => array(self::ASV_12, self::REQUESTHANDLER => "ItemOperations"),
99
                    self::COMMAND_SETTINGS          => array(self::ASV_12, self::REQUESTHANDLER => "Settings"),
100
            );
101
102
103
104
    static private $classes = array(
105
                    "Email"     => array(
106
                                        self::CLASS_NAME => "SyncMail",
107
                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
108
                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_INBOX,
109
                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_OTHER, SYNC_FOLDER_TYPE_DRAFTS, SYNC_FOLDER_TYPE_WASTEBASKET,
110
                                                                        SYNC_FOLDER_TYPE_SENTMAIL, SYNC_FOLDER_TYPE_OUTBOX, SYNC_FOLDER_TYPE_USER_MAIL,
111
                                                                        SYNC_FOLDER_TYPE_JOURNAL, SYNC_FOLDER_TYPE_USER_JOURNAL),
112
                                   ),
113
                    "Contacts"  => array(
114
                                        self::CLASS_NAME => "SyncContact",
115
                                        self::CLASS_REQUIRESPROTOCOLVERSION => true,
116
                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_CONTACT,
117
                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_UNKNOWN),
118
                                   ),
119
                    "Calendar"  => array(
120
                                        self::CLASS_NAME => "SyncAppointment",
121
                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
122
                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_APPOINTMENT,
123
                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_APPOINTMENT),
124
                                   ),
125
                    "Tasks"     => array(
126
                                        self::CLASS_NAME => "SyncTask",
127
                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
128
                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_TASK,
129
                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_TASK),
130
                                   ),
131
                    "Notes" => array(
132
                                        self::CLASS_NAME => "SyncNote",
133
                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
134
                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_NOTE,
135
                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_NOTE),
136
                                   ),
137
                );
138
139
140
    static private $stateMachine;
141
    static private $deviceManager;
142
    static private $provisioningManager;
143
    static private $topCollector;
144
    static private $backend;
145
    static private $addSyncFolders;
146
    static private $policies;
0 ignored issues
show
introduced by
The private property $policies is not used, and could be removed.
Loading history...
147
    static private $redis;
148
149
150
    /**
151
     * Verifies configuration
152
     *
153
     * @access public
154
     * @return boolean
155
     * @throws FatalMisconfigurationException
156
     */
157
    static public function CheckConfig() {
158
        // check the php version
159
        if (version_compare(phpversion(),'5.4.0') < 0) {
160
            throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.4 is used.");
161
        }
162
163
        // some basic checks
164
        if (!defined('BASE_PATH'))
165
            throw new FatalMisconfigurationException("The BASE_PATH is not configured. Check if the config.php file is in place.");
166
167
        if (substr(BASE_PATH, -1,1) != "/")
168
            throw new FatalMisconfigurationException("The BASE_PATH should terminate with a '/'");
169
170
        if (!file_exists(BASE_PATH))
171
            throw new FatalMisconfigurationException("The configured BASE_PATH does not exist or can not be accessed.");
172
173
        if (defined('BASE_PATH_CLI') && file_exists(BASE_PATH_CLI))
174
            define('REAL_BASE_PATH', BASE_PATH_CLI);
175
        else
176
            define('REAL_BASE_PATH', BASE_PATH);
177
178
        if (!defined('LOGBACKEND')) {
179
            define('LOGBACKEND', 'filelog');
180
        }
181
182
        if (strtolower(LOGBACKEND) == 'syslog') {
0 ignored issues
show
introduced by
The condition strtolower(LOGBACKEND) == 'syslog' is always false.
Loading history...
183
            define('LOGBACKEND_CLASS', 'Syslog');
184
            if (!defined('LOG_SYSLOG_FACILITY')) {
185
                define('LOG_SYSLOG_FACILITY', LOG_LOCAL0);
186
            }
187
188
            if (!defined('LOG_SYSLOG_HOST')) {
189
                define('LOG_SYSLOG_HOST', false);
190
            }
191
192
            if (!defined('LOG_SYSLOG_PORT')) {
193
                define('LOG_SYSLOG_PORT', 514);
194
            }
195
196
            if (!defined('LOG_SYSLOG_PROGRAM')) {
197
                define('LOG_SYSLOG_PROGRAM', 'grommunio-sync');
198
            }
199
200
            if (!is_numeric(LOG_SYSLOG_PORT)) {
201
                throw new FatalMisconfigurationException("The LOG_SYSLOG_PORT must a be a number.");
202
            }
203
204
            if (LOG_SYSLOG_HOST && LOG_SYSLOG_PORT <= 0) {
205
                throw new FatalMisconfigurationException("LOG_SYSLOG_HOST is defined but the LOG_SYSLOG_PORT does not seem to be valid.");
206
            }
207
        }
208
        elseif (strtolower(LOGBACKEND) == 'filelog') {
0 ignored issues
show
introduced by
The condition strtolower(LOGBACKEND) == 'filelog' is always true.
Loading history...
209
            define('LOGBACKEND_CLASS', 'FileLog');
210
            if (!defined('LOGFILEDIR'))
211
                throw new FatalMisconfigurationException("The LOGFILEDIR is not configured. Check if the config.php file is in place.");
212
213
            if (substr(LOGFILEDIR, -1,1) != "/")
214
                throw new FatalMisconfigurationException("The LOGFILEDIR should terminate with a '/'");
215
216
            if (!file_exists(LOGFILEDIR))
217
                throw new FatalMisconfigurationException("The configured LOGFILEDIR does not exist or can not be accessed.");
218
219
            if ((!file_exists(LOGFILE) && !touch(LOGFILE)) || !is_writable(LOGFILE))
220
                throw new FatalMisconfigurationException("The configured LOGFILE can not be modified.");
221
222
            if ((!file_exists(LOGERRORFILE) && !touch(LOGERRORFILE)) || !is_writable(LOGERRORFILE))
223
                throw new FatalMisconfigurationException("The configured LOGERRORFILE can not be modified.");
224
225
            // check ownership on the (eventually) just created files
226
            Utils::FixFileOwner(LOGFILE);
227
            Utils::FixFileOwner(LOGERRORFILE);
228
        }
229
        else {
230
            define('LOGBACKEND_CLASS', LOGBACKEND);
231
        }
232
233
        // set time zone
234
        // code contributed by Robert Scheck (rsc)
235
        if(defined('TIMEZONE') ? constant('TIMEZONE') : false) {
236
            if (! @date_default_timezone_set(TIMEZONE))
237
                throw new FatalMisconfigurationException(sprintf("The configured TIMEZONE '%s' is not valid. Please check supported timezones at http://www.php.net/manual/en/timezones.php", constant('TIMEZONE')));
238
        }
239
        else if(!ini_get('date.timezone')) {
240
            date_default_timezone_set('Europe/Vienna');
241
        }
242
243
        if (defined('USE_X_FORWARDED_FOR_HEADER')) {
244
            ZLog::Write(LOGLEVEL_INFO, "The configuration parameter 'USE_X_FORWARDED_FOR_HEADER' was deprecated in favor of 'USE_CUSTOM_REMOTE_IP_HEADER'. Please update your configuration.");
245
        }
246
247
        // check redis configuration - set defaults
248
        if (!defined('REDIS_HOST')) {
249
            define('REDIS_HOST', 'localhost');
250
        }
251
        if (!defined('REDIS_PORT')) {
252
            define('REDIS_PORT', 6379);
253
        }
254
        if (!defined('REDIS_AUTH')) {
255
            define('REDIS_AUTH', '');
256
        }
257
        return true;
258
    }
259
260
    /**
261
     * Verifies Timezone, StateMachine and Backend configuration
262
     *
263
     * @access public
264
     * @return boolean
265
     * @trows FatalMisconfigurationException
266
     */
267
    static public function CheckAdvancedConfig() {
268
        global $specialLogUsers, $additionalFolders;
269
270
        if (!is_array($specialLogUsers))
271
            throw new FatalMisconfigurationException("The WBXML log users is not an array.");
272
273
        if (!defined('SYNC_CONTACTS_MAXPICTURESIZE')) {
274
            define('SYNC_CONTACTS_MAXPICTURESIZE', 49152);
275
        }
276
        else if ((!is_int(SYNC_CONTACTS_MAXPICTURESIZE) || SYNC_CONTACTS_MAXPICTURESIZE < 1))
0 ignored issues
show
introduced by
The condition is_int(SYNC_CONTACTS_MAXPICTURESIZE) is always true.
Loading history...
277
            throw new FatalMisconfigurationException("The SYNC_CONTACTS_MAXPICTURESIZE value must be a number higher than 0.");
278
279
        if (!defined('USE_PARTIAL_FOLDERSYNC')) {
280
            define('USE_PARTIAL_FOLDERSYNC', false);
281
        }
282
283
        if (!defined('PING_LOWER_BOUND_LIFETIME')) {
284
            define('PING_LOWER_BOUND_LIFETIME', false);
285
        }
286
        elseif(PING_LOWER_BOUND_LIFETIME !== false && (!is_int(PING_LOWER_BOUND_LIFETIME) || PING_LOWER_BOUND_LIFETIME < 1 || PING_LOWER_BOUND_LIFETIME > 3540)){
0 ignored issues
show
introduced by
The condition PING_LOWER_BOUND_LIFETIME !== false is always false.
Loading history...
287
            throw new FatalMisconfigurationException("The PING_LOWER_BOUND_LIFETIME value must be 'false' or a number between 1 and 3540 inclusively.");
288
        }
289
        if (!defined('PING_HIGHER_BOUND_LIFETIME')) {
290
            define('PING_HIGHER_BOUND_LIFETIME', false);
291
        }
292
        elseif(PING_HIGHER_BOUND_LIFETIME !== false && (!is_int(PING_HIGHER_BOUND_LIFETIME) || PING_HIGHER_BOUND_LIFETIME < 1 || PING_HIGHER_BOUND_LIFETIME > 3540)){
0 ignored issues
show
introduced by
The condition PING_HIGHER_BOUND_LIFETIME !== false is always false.
Loading history...
293
            throw new FatalMisconfigurationException("The PING_HIGHER_BOUND_LIFETIME value must be 'false' or a number between 1 and 3540 inclusively.");
294
        }
295
        if(PING_HIGHER_BOUND_LIFETIME !== false && PING_LOWER_BOUND_LIFETIME !== false && PING_HIGHER_BOUND_LIFETIME < PING_LOWER_BOUND_LIFETIME){
0 ignored issues
show
introduced by
The condition PING_HIGHER_BOUND_LIFETIME !== false is always false.
Loading history...
296
            throw new FatalMisconfigurationException("The PING_HIGHER_BOUND_LIFETIME value must be greater or equal to PING_LOWER_BOUND_LIFETIME.");
297
        }
298
299
        if (!defined('RETRY_AFTER_DELAY')) {
300
            define('RETRY_AFTER_DELAY', 300);
301
        }
302
        elseif (RETRY_AFTER_DELAY !== false && (!is_int(RETRY_AFTER_DELAY) || RETRY_AFTER_DELAY < 1))  {
0 ignored issues
show
introduced by
The condition is_int(RETRY_AFTER_DELAY) is always true.
Loading history...
303
            throw new FatalMisconfigurationException("The RETRY_AFTER_DELAY value must be 'false' or a number greater than 0.");
304
        }
305
306
        // set Grommunio backend defaults if not set
307
        if (!defined('MAPI_SERVER')) {
308
            define('MAPI_SERVER', 'default:');
309
        }
310
        if (!defined('STORE_STATE_FOLDER')) {
311
            define('STORE_STATE_FOLDER', 'GS-SyncState');
312
        }
313
314
        // the check on additional folders will not throw hard errors, as this is probably changed on live systems
315
        if (isset($additionalFolders) && !is_array($additionalFolders))
316
            ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : The additional folders synchronization not available as array.");
317
        else {
318
            // check configured data
319
            foreach ($additionalFolders as $af) {
320
321
                if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) {
322
                    ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored.");
323
                    continue;
324
                }
325
326
                if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") {
327
                    ZLog::Write(LOGLEVEL_WARN, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored.");
328
                    continue;
329
                }
330
331
                if (!in_array($af['type'], array(SYNC_FOLDER_TYPE_USER_NOTE, SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL))) {
332
                    ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::CheckConfig() : the type of the additional synchronization folder '%s is not permitted.", $af['name']));
333
                    continue;
334
                }
335
                // the data will be initialized when used via self::getAddFolders()
336
            }
337
        }
338
339
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Used timezone '%s'", date_default_timezone_get()));
340
341
        // get the statemachine, which will also try to load the backend.. This could throw errors
342
        self::GetStateMachine();
343
        return true;
344
    }
345
346
    /**
347
     * Returns the StateMachine object
348
     * which has to be an IStateMachine implementation
349
     *
350
     * @access public
351
     * @throws FatalNotImplementedException
352
     * @throws HTTPReturnCodeException
353
     * @return object   implementation of IStateMachine
354
     */
355
    static public function GetStateMachine() {
356
        if (!isset(ZPush::$stateMachine)) {
357
            // the backend could also return an own IStateMachine implementation
358
            ZPush::$stateMachine = self::GetBackend()->GetStateMachine();
359
360
            if (ZPush::$stateMachine->GetStateVersion() !== ZPush::GetLatestStateVersion()) {
361
                if (class_exists("TopCollector")) self::GetTopCollector()->AnnounceInformation("Run migration script!", true);
362
                throw new ServiceUnavailableException(sprintf("The state version available to the %s is not the latest version - please run the state upgrade script. See release notes for more information.", get_class(ZPush::$stateMachine)));
363
            }
364
        }
365
        return ZPush::$stateMachine;
366
    }
367
368
369
    /**
370
     * Returns the Redis object
371
     *
372
     * @access public
373
     * @return object Redis
374
     */
375
    static public function GetRedis() {
376
        if (!isset(ZPush::$redis))
377
            ZPush::$redis = new RedisConnection();
378
379
        return ZPush::$redis;
380
    }
381
382
    /**
383
     * Returns the latest version of supported states
384
     *
385
     * @access public
386
     * @return int
387
     */
388
    static public function GetLatestStateVersion() {
389
        return self::STATE_VERSION;
390
    }
391
392
    /**
393
     * Returns the ProvisioningManager object
394
     *
395
     * @access public
396
     * @return object ProvisioningManager
397
     */
398
    static public function GetProvisioningManager() {
399
        if (!isset(self::$provisioningManager)) {
400
            self::$provisioningManager = new ProvisioningManager();
401
        }
402
403
        return self::$provisioningManager;
404
    }
405
406
    /**
407
     * Returns the DeviceManager object
408
     *
409
     * @param boolean   $initialize     (opt) default true: initializes the DeviceManager if not already done
410
     *
411
     * @access public
412
     * @return object DeviceManager
413
     */
414
    static public function GetDeviceManager($initialize = true) {
415
        if (!isset(ZPush::$deviceManager) && $initialize)
416
            ZPush::$deviceManager = new DeviceManager();
417
418
        return ZPush::$deviceManager;
419
    }
420
421
    /**
422
     * Returns the Top data collector object
423
     *
424
     * @access public
425
     * @return object TopCollector
426
     */
427
    static public function GetTopCollector() {
428
        if (!isset(ZPush::$topCollector))
429
            ZPush::$topCollector = new TopCollector();
430
431
        return ZPush::$topCollector;
432
    }
433
434
    /**
435
     * Loads a backend file
436
     *
437
     * @param string $backendname
438
439
     * @access public
440
     * @throws FatalNotImplementedException
441
     * @return boolean
442
     */
443
    static public function IncludeBackend($backendname) {
444
        if ($backendname == false) return false;
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $backendname of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
445
446
        $backendname = strtolower($backendname);
447
        if (substr($backendname, 0, 7) !== 'backend')
448
            throw new FatalNotImplementedException(sprintf("Backend '%s' is not allowed",$backendname));
449
450
        $rbn = substr($backendname, 7);
451
452
        $subdirbackend = REAL_BASE_PATH . "backend/" . $rbn . "/" . $rbn . ".php";
453
        $stdbackend = REAL_BASE_PATH . "backend/" . $rbn . ".php";
454
455
        if (is_file($subdirbackend))
456
            $toLoad = $subdirbackend;
457
        else if (is_file($stdbackend))
458
            $toLoad = $stdbackend;
459
        else
460
            return false;
461
462
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Including backend file: '%s'", $toLoad));
463
        return include_once($toLoad);
464
    }
465
466
    /**
467
     * Returns the Backend for this request
468
     * the backend has to be an IBackend implementation
469
     *
470
     * @access public
471
     * @return object     IBackend implementation
472
     */
473
    static public function GetBackend() {
474
        // if the backend is not yet loaded, load backend drivers and instantiate it
475
        if (!isset(ZPush::$backend)) {
476
477
            // Initialize Grommunio
478
            ZPush::$backend = new Grommunio();
479
        }
480
        return ZPush::$backend;
481
    }
482
483
    /**
484
     * Returns additional folder objects which should be synchronized to the device
485
     *
486
     * @param boolean $backendIdsAsKeys     if true the keys are backendids else folderids, default: true
487
     *
488
     * @access public
489
     * @return array
490
     */
491
    static public function GetAdditionalSyncFolders($backendIdsAsKeys = true) {
492
        // get user based folders which should be synchronized
493
        $userFolder = self::GetDeviceManager()->GetAdditionalUserSyncFolders();
494
        $addfolders = self::getAddSyncFolders() + $userFolder;
495
        // if requested, we rewrite the backendids to folderids here
496
        if ($backendIdsAsKeys === false && !empty($addfolders)) {
497
            ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetAdditionalSyncFolders(): Requested AS folderids as keys for additional folders array, converting");
498
            $faddfolders = array();
499
            foreach ($addfolders as $backendId => $addFolder) {
500
                $fid = self::GetDeviceManager()->GetFolderIdForBackendId($backendId);
501
                $faddfolders[$fid] = $addFolder;
502
            }
503
            $addfolders = $faddfolders;
504
        }
505
506
        return $addfolders;
507
    }
508
509
    /**
510
     * Returns additional folder objects which should be synchronized to the device
511
     *
512
     * @param string        $backendid
513
     * @param boolean       $noDebug        (opt) by default, debug message is shown
514
     *
515
     * @access public
516
     * @return string
517
     */
518
    static public function GetAdditionalSyncFolderStore($backendid, $noDebug = false) {
519
        if(isset(self::getAddSyncFolders()[$backendid]->Store)) {
520
            $val = self::getAddSyncFolders()[$backendid]->Store;
521
        }
522
        else {
523
            $val = self::GetDeviceManager()->GetAdditionalUserSyncFolder($backendid);
524
            if (isset($val['store'])) {
525
                $val = $val['store'];
526
            }
527
        }
528
529
        if (!$noDebug)
530
            ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetAdditionalSyncFolderStore('%s'): '%s'", $backendid, Utils::PrintAsString($val)));
531
        return $val;
532
    }
533
534
    /**
535
     * Returns a SyncObject class name for a folder class
536
     *
537
     * @param string $folderclass
538
     *
539
     * @access public
540
     * @return string
541
     * @throws FatalNotImplementedException
542
     */
543
    static public function getSyncObjectFromFolderClass($folderclass) {
544
        if (!isset(self::$classes[$folderclass]))
545
            throw new FatalNotImplementedException("Class '$folderclass' is not supported");
546
547
        $class = self::$classes[$folderclass][self::CLASS_NAME];
548
        if (self::$classes[$folderclass][self::CLASS_REQUIRESPROTOCOLVERSION])
549
            return new $class(Request::GetProtocolVersion());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new $class(Request::GetProtocolVersion()) returns the type object which is incompatible with the documented return type string.
Loading history...
550
        else
551
            return new $class();
0 ignored issues
show
Bug Best Practice introduced by
The expression return new $class() returns the type object which is incompatible with the documented return type string.
Loading history...
552
    }
553
554
    /**
555
     * Initializes the SyncObjects for additional folders on demand.
556
     * Uses DeviceManager->BuildSyncFolderObject() to do patching required for ZP-907.
557
     *
558
     * @access private
559
     * @return array
560
     */
561
    static private function getAddSyncFolders() {
562
        global $additionalFolders;
563
        if (!isset(self::$addSyncFolders)) {
564
            self::$addSyncFolders = array();
565
566
            if (isset($additionalFolders) && !is_array($additionalFolders)) {
567
                ZLog::Write(LOGLEVEL_ERROR, "ZPush::getAddSyncFolders() : The additional folders synchronization not available as array.");
568
            }
569
            else {
570
                foreach ($additionalFolders as $af) {
571
                    if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) {
572
                        ZLog::Write(LOGLEVEL_ERROR, "ZPush::getAddSyncFolders() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored.");
573
                        continue;
574
                    }
575
576
                    if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") {
577
                        ZLog::Write(LOGLEVEL_WARN, "ZPush::getAddSyncFolders() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored.");
578
                        continue;
579
                    }
580
581
                    if (!in_array($af['type'], array(SYNC_FOLDER_TYPE_USER_NOTE, SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL))) {
582
                        ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::getAddSyncFolders() : the type of the additional synchronization folder '%s is not permitted.", $af['name']));
583
                        continue;
584
                    }
585
586
                    // don't fail hard if no flags are set, but we at least warn about it
587
                    if (!isset($af['flags'])) {
588
                        ZLog::Write(LOGLEVEL_WARN, sprintf("ZPush::getAddSyncFolders() : the additional folder '%s' is not configured completely. Missing 'flags' parameter, defaulting to DeviceManager::FLD_FLAGS_NONE.", $af['name']));
589
                        $af['flags'] = DeviceManager::FLD_FLAGS_NONE;
590
                    }
591
592
                    $folder = self::GetDeviceManager()->BuildSyncFolderObject($af['store'], $af['folderid'], '0', $af['name'], $af['type'], $af['flags'], DeviceManager::FLD_ORIGIN_CONFIG);
593
                    self::$addSyncFolders[$folder->BackendId] = $folder;
594
                }
595
            }
596
        }
597
        return self::$addSyncFolders;
598
    }
599
600
    /**
601
     * Returns the default foldertype for a folder class
602
     *
603
     * @param string $folderclass   folderclass sent by the mobile
604
     *
605
     * @access public
606
     * @return string
607
     */
608
    static public function getDefaultFolderTypeFromFolderClass($folderclass) {
609
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::getDefaultFolderTypeFromFolderClass('%s'): '%d'", $folderclass, self::$classes[$folderclass][self::CLASS_DEFAULTTYPE]));
610
        return self::$classes[$folderclass][self::CLASS_DEFAULTTYPE];
611
    }
612
613
    /**
614
     * Returns the folder class for a foldertype
615
     *
616
     * @param string $foldertype
617
     *
618
     * @access public
619
     * @return string/false     false if no class for this type is available
0 ignored issues
show
Documentation Bug introduced by
The doc comment string/false at position 0 could not be parsed: Unknown type name 'string/false' at position 0 in string/false.
Loading history...
620
     */
621
    static public function GetFolderClassFromFolderType($foldertype) {
622
        $class = false;
623
        foreach (self::$classes as $aClass => $cprops) {
624
            if ($cprops[self::CLASS_DEFAULTTYPE] == $foldertype || in_array($foldertype, $cprops[self::CLASS_OTHERTYPES])) {
625
                $class = $aClass;
626
                break;
627
            }
628
        }
629
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetFolderClassFromFolderType('%s'): %s", $foldertype, Utils::PrintAsString($class)));
630
        return $class;
631
    }
632
633
    /**
634
     * Prints the grommunio-sync legal header to STDOUT
635
     * Using this breaks ActiveSync synchronization if wbxml is expected
636
     *
637
     * @param string $message               (opt) message to be displayed
638
     * @param string $additionalMessage     (opt) additional message to be displayed
639
640
     * @access public
641
     * @return
642
     *
643
     */
644
    static public function PrintGrommunioSyncLegal($message = "", $additionalMessage = "") {
645
        ZLog::Write(LOGLEVEL_DEBUG,"ZPush::PrintGrommunioSyncLegal()");
646
647
        if ($message)
648
            $message = "<h3>". $message . "</h3>";
649
        if ($additionalMessage)
650
            $additionalMessage .= "<br>";
651
652
        header("Content-type: text/html");
653
        print <<<END
654
        <html>
655
        <header>
656
        <title>grommunio-sync ActiveSync</title>
657
        </header>
658
        <body>
659
        <font face="verdana">
660
        <h2>grommunio-sync - Open Source ActiveSync</h2>
661
        $message $additionalMessage
662
        <br><br>
663
        More information about grommunio can be found
664
        <a href="https://grommunio.com/">at the grommunio homepage</a><br>
665
        </font>
666
        </body>
667
        </html>
668
END;
669
    }
670
671
    /**
672
     * Indicates the latest AS version supported by grommunio-sync
673
     *
674
     * @access public
675
     * @return string
676
     */
677
    static public function GetLatestSupportedASVersion() {
678
        return end(self::$supportedASVersions);
679
    }
680
681
    /**
682
     * Indicates which is the highest AS version supported by the backend
683
     *
684
     * @access public
685
     * @return string
686
     * @throws FatalNotImplementedException     if the backend returns an invalid version
687
     */
688
    static public function GetSupportedASVersion() {
689
        $version = self::GetBackend()->GetSupportedASVersion();
690
        if (!in_array($version, self::$supportedASVersions))
691
            throw new FatalNotImplementedException(sprintf("AS version '%s' reported by the backend is not supported", $version));
692
693
        return $version;
694
    }
695
696
    /**
697
     * Returns AS server header
698
     *
699
     * @access public
700
     * @return string
701
     */
702
    static public function GetServerHeader() {
703
        if (self::GetSupportedASVersion() == self::ASV_25)
704
            return "MS-Server-ActiveSync: 6.5.7638.1";
705
        else
706
            return "MS-Server-ActiveSync: ". self::GetSupportedASVersion();
707
    }
708
709
    /**
710
     * Returns AS protocol versions which are supported
711
     *
712
     * @param boolean   $valueOnly  (opt) default: false (also returns the header name)
713
     *
714
     * @access public
715
     * @return string
716
     */
717
    static public function GetSupportedProtocolVersions($valueOnly = false) {
718
        $versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
719
        ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedProtocolVersions(): " . $versions);
720
721
        if ($valueOnly === true)
722
            return $versions;
723
724
        return "MS-ASProtocolVersions: " . $versions;
725
    }
726
727
    /**
728
     * Returns AS commands which are supported
729
     *
730
     * @access public
731
     * @return string
732
     */
733
    static public function GetSupportedCommands() {
734
        $asCommands = array();
735
        // filter all non-activesync commands
736
        foreach (self::$supportedCommands as $c=>$v)
737
            if (!self::checkCommandOptions($c, self::NOACTIVESYNCCOMMAND) &&
738
                self::checkCommandOptions($c, self::GetSupportedASVersion()))
739
                $asCommands[] = Utils::GetCommandFromCode($c);
740
741
        $commands = implode(',', $asCommands);
742
        ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedCommands(): " . $commands);
743
        return "MS-ASProtocolCommands: " . $commands;
744
    }
745
746
    /**
747
     * Loads and instantiates a request processor for a command
748
     *
749
     * @param int $commandCode
750
     *
751
     * @access public
752
     * @return RequestProcessor sub-class
753
     */
754
    static public function GetRequestHandlerForCommand($commandCode) {
755
        if (!array_key_exists($commandCode, self::$supportedCommands) ||
756
            !array_key_exists(self::REQUESTHANDLER, self::$supportedCommands[$commandCode]) )
757
            throw new FatalNotImplementedException(sprintf("Command '%s' has no request handler or class", Utils::GetCommandFromCode($commandCode)));
758
759
        $class = self::$supportedCommands[$commandCode][self::REQUESTHANDLER];
760
        $handlerclass = REAL_BASE_PATH . "lib/request/" . strtolower($class) . ".php";
761
762
        if (is_file($handlerclass))
763
            include($handlerclass);
764
765
        if (class_exists($class))
766
            return new $class();
767
        else
768
            throw new FatalNotImplementedException(sprintf("Request handler '%s' can not be loaded", $class));
769
    }
770
771
    /**
772
     * Indicates if a commands requires authentication or not
773
     *
774
     * @param int $commandCode
775
     *
776
     * @access public
777
     * @return boolean
778
     */
779
    static public function CommandNeedsAuthentication($commandCode) {
780
        $stat = ! self::checkCommandOptions($commandCode, self::UNAUTHENTICATED);
781
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsAuthentication(%d): %s", $commandCode, Utils::PrintAsString($stat)));
782
        return $stat;
783
    }
784
785
    /**
786
     * Indicates if the Provisioning check has to be forced on these commands
787
     *
788
     * @param string $commandCode
789
790
     * @access public
791
     * @return boolean
792
     */
793
    static public function CommandNeedsProvisioning($commandCode) {
794
        $stat = ! self::checkCommandOptions($commandCode, self::UNPROVISIONED);
795
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsProvisioning(%s): %s", $commandCode, Utils::PrintAsString($stat)));
796
        return $stat;
797
    }
798
799
    /**
800
     * Indicates if these commands expect plain text input instead of wbxml
801
     *
802
     * @param string $commandCode
803
     *
804
     * @access public
805
     * @return boolean
806
     */
807
    static public function CommandNeedsPlainInput($commandCode) {
808
        $stat = self::checkCommandOptions($commandCode, self::PLAININPUT);
809
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsPlainInput(%d): %s", $commandCode, Utils::PrintAsString($stat)));
0 ignored issues
show
Bug introduced by
$stat of type object is incompatible with the type string expected by parameter $var of Utils::PrintAsString(). ( Ignorable by Annotation )

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

809
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsPlainInput(%d): %s", $commandCode, Utils::PrintAsString(/** @scrutinizer ignore-type */ $stat)));
Loading history...
810
        return $stat;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stat returns the type object which is incompatible with the documented return type boolean.
Loading history...
811
    }
812
813
    /**
814
     * Indicates if the command to be executed operates on the hierarchy
815
     *
816
     * @param int $commandCode
817
818
     * @access public
819
     * @return boolean
820
     */
821
    static public function HierarchyCommand($commandCode) {
822
        $stat = self::checkCommandOptions($commandCode, self::HIERARCHYCOMMAND);
823
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::HierarchyCommand(%d): %s", $commandCode, Utils::PrintAsString($stat)));
0 ignored issues
show
Bug introduced by
$stat of type object is incompatible with the type string expected by parameter $var of Utils::PrintAsString(). ( Ignorable by Annotation )

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

823
        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::HierarchyCommand(%d): %s", $commandCode, Utils::PrintAsString(/** @scrutinizer ignore-type */ $stat)));
Loading history...
824
        return $stat;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stat returns the type object which is incompatible with the documented return type boolean.
Loading history...
825
    }
826
827
    /**
828
     * Checks access types of a command
829
     *
830
     * @param string $commandCode   a commandCode
831
     * @param string $option        e.g. self::UNAUTHENTICATED
832
833
     * @access private
834
     * @throws FatalNotImplementedException
835
     * @return object StateMachine
836
     */
837
    static private function checkCommandOptions($commandCode, $option) {
838
        if ($commandCode === false) return false;
0 ignored issues
show
introduced by
The condition $commandCode === false is always false.
Loading history...
839
840
        if (!array_key_exists($commandCode, self::$supportedCommands))
841
            throw new FatalNotImplementedException(sprintf("Command '%s' is not supported", Utils::GetCommandFromCode($commandCode)));
0 ignored issues
show
Bug introduced by
$commandCode of type string is incompatible with the type integer expected by parameter $code of Utils::GetCommandFromCode(). ( Ignorable by Annotation )

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

841
            throw new FatalNotImplementedException(sprintf("Command '%s' is not supported", Utils::GetCommandFromCode(/** @scrutinizer ignore-type */ $commandCode)));
Loading history...
842
843
        $capa = self::$supportedCommands[$commandCode];
844
        $defcapa = in_array($option, $capa, true);
845
846
        // if not looking for a default capability, check if the command is supported since a previous AS version
847
        if (!$defcapa) {
848
            $verkey = array_search($option, self::$supportedASVersions, true);
849
            if ($verkey !== false && ($verkey >= array_search($capa[0], self::$supportedASVersions))) {
850
                $defcapa = true;
851
            }
852
        }
853
854
        return $defcapa;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $defcapa returns the type boolean which is incompatible with the documented return type object.
Loading history...
855
    }
856
857
}
858