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