| Total Complexity | 154 |
| Total Lines | 845 |
| Duplicated Lines | 0 % |
| Changes | 3 | ||
| Bugs | 0 | Features | 0 |
Complex classes like ZPush 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 ZPush, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 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; |
||
|
|
|||
| 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') { |
||
| 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') { |
||
| 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)) |
||
| 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)){ |
||
| 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)){ |
||
| 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){ |
||
| 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)) { |
||
| 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; |
||
| 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()); |
||
| 550 | else |
||
| 551 | return new $class(); |
||
| 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 |
||
| 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() { |
||
| 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))); |
||
| 810 | return $stat; |
||
| 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) { |
||
| 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) { |
||
| 855 | } |
||
| 856 | |||
| 857 | } |
||
| 858 |