Back to index

d-push  2.0
zpush.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   zpush.php
00004 * Project   :   Z-Push
00005 * Descr     :   Core functionalities
00006 *
00007 * Created   :   12.04.2011
00008 *
00009 * Copyright 2007 - 2011 Zarafa Deutschland GmbH
00010 *
00011 * This program is free software: you can redistribute it and/or modify
00012 * it under the terms of the GNU Affero General Public License, version 3,
00013 * as published by the Free Software Foundation with the following additional
00014 * term according to sec. 7:
00015 *
00016 * According to sec. 7 of the GNU Affero General Public License, version 3,
00017 * the terms of the AGPL are supplemented with the following terms:
00018 *
00019 * "Zarafa" is a registered trademark of Zarafa B.V.
00020 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00021 * The licensing of the Program under the AGPL does not imply a trademark license.
00022 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00023 *
00024 * However, if you propagate an unmodified version of the Program you are
00025 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00026 * Furthermore you may use our trademarks where it is necessary to indicate
00027 * the intended purpose of a product or service provided you use it in accordance
00028 * with honest practices in industrial or commercial matters.
00029 * If you want to propagate modified versions of the Program under the name "Z-Push",
00030 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00031 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00032 *
00033 * This program is distributed in the hope that it will be useful,
00034 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00035 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00036 * GNU Affero General Public License for more details.
00037 *
00038 * You should have received a copy of the GNU Affero General Public License
00039 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00040 *
00041 * Consult LICENSE file for details
00042 ************************************************/
00043 
00044 
00045 class ZPush {
00046     const UNAUTHENTICATED = 1;
00047     const UNPROVISIONED = 2;
00048     const NOACTIVESYNCCOMMAND = 3;
00049     const WEBSERVICECOMMAND = 4;
00050     const HIERARCHYCOMMAND = 5;
00051     const PLAININPUT = 6;
00052     const REQUESTHANDLER = 7;
00053     const CLASS_NAME = 1;
00054     const CLASS_REQUIRESPROTOCOLVERSION = 2;
00055     const CLASS_DEFAULTTYPE = 3;
00056     const CLASS_OTHERTYPES = 4;
00057 
00058     // AS versions
00059     const ASV_1 = "1.0";
00060     const ASV_2 = "2.0";
00061     const ASV_21 = "2.1";
00062     const ASV_25 = "2.5";
00063     const ASV_12 = "12.0";
00064     const ASV_121 = "12.1";
00065     const ASV_14 = "14.0";
00066 
00070     const COMMAND_SYNC = 0;
00071     const COMMAND_SENDMAIL = 1;
00072     const COMMAND_SMARTFORWARD = 2;
00073     const COMMAND_SMARTREPLY = 3;
00074     const COMMAND_GETATTACHMENT = 4;
00075     const COMMAND_FOLDERSYNC = 9;
00076     const COMMAND_FOLDERCREATE = 10;
00077     const COMMAND_FOLDERDELETE = 11;
00078     const COMMAND_FOLDERUPDATE = 12;
00079     const COMMAND_MOVEITEMS = 13;
00080     const COMMAND_GETITEMESTIMATE = 14;
00081     const COMMAND_MEETINGRESPONSE = 15;
00082     const COMMAND_SEARCH = 16;
00083     const COMMAND_SETTINGS = 17;
00084     const COMMAND_PING = 18;
00085     const COMMAND_ITEMOPERATIONS = 19;
00086     const COMMAND_PROVISION = 20;
00087     const COMMAND_RESOLVERECIPIENTS = 21;
00088     const COMMAND_VALIDATECERT = 22;
00089 
00090     // Deprecated commands
00091     const COMMAND_GETHIERARCHY = -1;
00092     const COMMAND_CREATECOLLECTION = -2;
00093     const COMMAND_DELETECOLLECTION = -3;
00094     const COMMAND_MOVECOLLECTION = -4;
00095     const COMMAND_NOTIFY = -5;
00096 
00097     // Webservice commands
00098     const COMMAND_WEBSERVICE_DEVICE = -100;
00099 
00100     static private $supportedASVersions = array(
00101                     self::ASV_1,
00102                     self::ASV_2,
00103                     self::ASV_21,
00104                     self::ASV_25,
00105                     self::ASV_12,
00106                     self::ASV_121,
00107                     self::ASV_14
00108                 );
00109 
00110     static private $supportedCommands = array(
00111                     // COMMAND                             // AS VERSION   // REQUESTHANDLER                        // OTHER SETTINGS
00112                     self::COMMAND_SYNC              => array(self::ASV_1,  self::REQUESTHANDLER => "Sync"),
00113                     self::COMMAND_SENDMAIL          => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
00114                     self::COMMAND_SMARTFORWARD      => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
00115                     self::COMMAND_SMARTREPLY        => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
00116                     self::COMMAND_GETATTACHMENT     => array(self::ASV_1,  self::REQUESTHANDLER => "GetAttachment"),
00117                     self::COMMAND_GETHIERARCHY      => array(self::ASV_1,  self::REQUESTHANDLER => "GetHierarchy",  self::HIERARCHYCOMMAND),            // deprecated but implemented
00118                     self::COMMAND_CREATECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
00119                     self::COMMAND_DELETECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
00120                     self::COMMAND_MOVECOLLECTION    => array(self::ASV_1),                                                                              // deprecated & not implemented
00121                     self::COMMAND_FOLDERSYNC        => array(self::ASV_2,  self::REQUESTHANDLER => "FolderSync",    self::HIERARCHYCOMMAND),
00122                     self::COMMAND_FOLDERCREATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
00123                     self::COMMAND_FOLDERDELETE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
00124                     self::COMMAND_FOLDERUPDATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
00125                     self::COMMAND_MOVEITEMS         => array(self::ASV_1,  self::REQUESTHANDLER => "MoveItems"),
00126                     self::COMMAND_GETITEMESTIMATE   => array(self::ASV_1,  self::REQUESTHANDLER => "GetItemEstimate"),
00127                     self::COMMAND_MEETINGRESPONSE   => array(self::ASV_1,  self::REQUESTHANDLER => "MeetingResponse"),
00128                     self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_1,  self::REQUESTHANDLER => false),
00129                     self::COMMAND_VALIDATECERT      => array(self::ASV_1,  self::REQUESTHANDLER => false),
00130                     self::COMMAND_PROVISION         => array(self::ASV_25, self::REQUESTHANDLER => "Provisioning",  self::UNAUTHENTICATED, self::UNPROVISIONED),
00131                     self::COMMAND_SEARCH            => array(self::ASV_1,  self::REQUESTHANDLER => "Search"),
00132                     self::COMMAND_PING              => array(self::ASV_2,  self::REQUESTHANDLER => "Ping",          self::UNPROVISIONED),
00133                     self::COMMAND_NOTIFY            => array(self::ASV_1,  self::REQUESTHANDLER => "Notify"),                                           // deprecated & not implemented
00134                     self::COMMAND_ITEMOPERATIONS    => array(self::ASV_12, self::REQUESTHANDLER => "ItemOperations"),
00135                     self::COMMAND_SETTINGS          => array(self::ASV_12, self::REQUESTHANDLER => "Settings"),
00136 
00137                     self::COMMAND_WEBSERVICE_DEVICE => array(self::REQUESTHANDLER => "Webservice", self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
00138                 );
00139 
00140 
00141 
00142     static private $classes = array(
00143                     "Email"     => array(
00144                                         self::CLASS_NAME => "SyncMail",
00145                                         self::CLASS_REQUIRESPROTOCOLVERSION => false,
00146                                         self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_INBOX,
00147                                         self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_OTHER, SYNC_FOLDER_TYPE_DRAFTS, SYNC_FOLDER_TYPE_WASTEBASKET,
00148                                                                         SYNC_FOLDER_TYPE_SENTMAIL, SYNC_FOLDER_TYPE_OUTBOX, SYNC_FOLDER_TYPE_USER_MAIL,
00149                                                                         SYNC_FOLDER_TYPE_JOURNAL, SYNC_FOLDER_TYPE_USER_JOURNAL),
00150                                    ),
00151                     "Contacts"  => array(
00152                                         self::CLASS_NAME => "SyncContact",
00153                                         self::CLASS_REQUIRESPROTOCOLVERSION => true,
00154                                         self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_CONTACT,
00155                                         self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_CONTACT),
00156                                    ),
00157                     "Calendar"  => array(
00158                                         self::CLASS_NAME => "SyncAppointment",
00159                                         self::CLASS_REQUIRESPROTOCOLVERSION => false,
00160                                         self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_APPOINTMENT,
00161                                         self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_APPOINTMENT),
00162                                    ),
00163                     "Tasks"     => array(
00164                                         self::CLASS_NAME => "SyncTask",
00165                                         self::CLASS_REQUIRESPROTOCOLVERSION => false,
00166                                         self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_TASK,
00167                                         self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_TASK),
00168                                    ),
00169                     "Notes" => array(
00170                                         self::CLASS_NAME => "SyncNote",
00171                                         self::CLASS_REQUIRESPROTOCOLVERSION => false,
00172                                         self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_NOTE,
00173                                         self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_NOTE),
00174                                    ),
00175                 );
00176 
00177 
00178     static private $stateMachine;
00179     static private $searchProvider;
00180     static private $deviceManager;
00181     static private $topCollector;
00182     static private $backend;
00183     static private $addSyncFolders;
00184 
00185 
00193     static public function CheckConfig() {
00194         // check the php version
00195         if (version_compare(phpversion(),'5.1.0') < 0)
00196             throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.1 is used.");
00197 
00198         // some basic checks
00199         if (!defined('BASE_PATH'))
00200             throw new FatalMisconfigurationException("The BASE_PATH is not configured. Check if the config.php file is in place.");
00201 
00202         if (substr(BASE_PATH, -1,1) != "/")
00203             throw new FatalMisconfigurationException("The BASE_PATH should terminate with a '/'");
00204 
00205         if (!file_exists(BASE_PATH))
00206             throw new FatalMisconfigurationException("The configured BASE_PATH does not exist or can not be accessed.");
00207 
00208         if (defined('BASE_PATH_CLI') && file_exists(BASE_PATH_CLI))
00209             define('REAL_BASE_PATH', BASE_PATH_CLI);
00210         else
00211             define('REAL_BASE_PATH', BASE_PATH);
00212 
00213         if (!defined('LOGFILEDIR'))
00214             throw new FatalMisconfigurationException("The LOGFILEDIR is not configured. Check if the config.php file is in place.");
00215 
00216         if (substr(LOGFILEDIR, -1,1) != "/")
00217             throw new FatalMisconfigurationException("The LOGFILEDIR should terminate with a '/'");
00218 
00219         if (!file_exists(LOGFILEDIR))
00220             throw new FatalMisconfigurationException("The configured LOGFILEDIR does not exist or can not be accessed.");
00221 
00222         if (!touch(LOGFILE))
00223             throw new FatalMisconfigurationException("The configured LOGFILE can not be modified.");
00224 
00225         if (!touch(LOGERRORFILE))
00226             throw new FatalMisconfigurationException("The configured LOGFILE can not be modified.");
00227 
00228         // set time zone
00229         // code contributed by Robert Scheck (rsc) - more information: https://developer.berlios.de/mantis/view.php?id=479
00230         if(function_exists("date_default_timezone_set")) {
00231             if(defined('TIMEZONE') ? constant('TIMEZONE') : false) {
00232                 if (! @date_default_timezone_set(TIMEZONE))
00233                     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')));
00234             }
00235             else if(!ini_get('date.timezone')) {
00236                 date_default_timezone_set('Europe/Amsterdam');
00237             }
00238         }
00239 
00240         return true;
00241     }
00242 
00250     static public function CheckAdvancedConfig() {
00251         global $specialLogUsers, $additionalFolders;
00252 
00253         if (!is_array($specialLogUsers))
00254             throw new FatalMisconfigurationException("The WBXML log users is not an array.");
00255 
00256         if (!defined('SINK_FORCERECHECK')) {
00257             define('SINK_FORCERECHECK', 300);
00258         }
00259         else if (SINK_FORCERECHECK !== false && (!is_int(SINK_FORCERECHECK) || SINK_FORCERECHECK < 1))
00260             throw new FatalMisconfigurationException("The SINK_FORCERECHECK value must be 'false' or a number higher than 0.");
00261 
00262         // the check on additional folders will not throw hard errors, as this is probably changed on live systems
00263         if (isset($additionalFolders) && !is_array($additionalFolders))
00264             ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : The additional folders synchronization not available as array.");
00265         else {
00266             self::$addSyncFolders = array();
00267 
00268             // process configured data
00269             foreach ($additionalFolders as $af) {
00270 
00271                 if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) {
00272                     ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored.");
00273                     continue;
00274                 }
00275 
00276                 if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") {
00277                     ZLog::Write(LOGLEVEL_WARN, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored.");
00278                     continue;
00279                 }
00280 
00281                 if (!in_array($af['type'], array(SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL))) {
00282                     ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::CheckConfig() : the type of the additional synchronization folder '%s is not permitted.", $af['name']));
00283                     continue;
00284                 }
00285 
00286                 $folder = new SyncFolder();
00287                 $folder->serverid = $af['folderid'];
00288                 $folder->parentid = 0;                  // only top folders are supported
00289                 $folder->displayname = $af['name'];
00290                 $folder->type = $af['type'];
00291                 // save store as custom property which is not streamed directly to the device
00292                 $folder->NoBackendFolder = true;
00293                 $folder->Store = $af['store'];
00294                 self::$addSyncFolders[$folder->serverid] = $folder;
00295             }
00296 
00297         }
00298 
00299         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Used timezone '%s'", date_default_timezone_get()));
00300 
00301         // get the statemachine, which will also try to load the backend.. This could throw errors
00302         self::GetStateMachine();
00303     }
00304 
00313     static public function GetStateMachine() {
00314         if (!isset(ZPush::$stateMachine)) {
00315             // the backend could also return an own IStateMachine implementation
00316             $backendStateMachine = self::GetBackend()->GetStateMachine();
00317 
00318             // if false is returned, use the default StateMachine
00319             if ($backendStateMachine !== false) {
00320                 ZLog::Write(LOGLEVEL_DEBUG, "Backend implementation of IStateMachine: ".get_class($backendStateMachine));
00321                 if (in_array('IStateMachine', class_implements($backendStateMachine)))
00322                     ZPush::$stateMachine = $backendStateMachine;
00323                 else
00324                     throw new FatalNotImplementedException("State machine returned by the backend does not implement the IStateMachine interface!");
00325             }
00326             else {
00327                 // Initialize the default StateMachine
00328                 include_once('lib/default/filestatemachine.php');
00329                 ZPush::$stateMachine = new FileStateMachine();
00330             }
00331         }
00332         return ZPush::$stateMachine;
00333     }
00334 
00343     static public function GetDeviceManager($initialize = true) {
00344         if (!isset(ZPush::$deviceManager) && $initialize)
00345             ZPush::$deviceManager = new DeviceManager();
00346 
00347         return ZPush::$deviceManager;
00348     }
00349 
00356     static public function GetTopCollector() {
00357         if (!isset(ZPush::$topCollector))
00358             ZPush::$topCollector = new TopCollector();
00359 
00360         return ZPush::$topCollector;
00361     }
00362 
00372     static public function IncludeBackend($backendname) {
00373         if ($backendname == false) return false;
00374 
00375         $backendname = strtolower($backendname);
00376         if (substr($backendname, 0, 7) !== 'backend')
00377             throw new FatalNotImplementedException(sprintf("Backend '%s' is not allowed",$backendname));
00378 
00379         $rbn = substr($backendname, 7);
00380 
00381         $subdirbackend = REAL_BASE_PATH . "backend/" . $rbn . "/" . $rbn . ".php";
00382         $stdbackend = REAL_BASE_PATH . "backend/" . $rbn . ".php";
00383 
00384         if (is_file($subdirbackend))
00385             $toLoad = $subdirbackend;
00386         else if (is_file($stdbackend))
00387             $toLoad = $stdbackend;
00388         else
00389             return false;
00390 
00391         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Including backend file: '%s'", $toLoad));
00392         include_once($toLoad);
00393         return true;
00394     }
00395 
00404     static public function GetSearchProvider() {
00405         if (!isset(ZPush::$searchProvider)) {
00406             // is a global searchprovider configured ? It will  outrank the backend
00407             if (defined('SEARCH_PROVIDER') && @constant('SEARCH_PROVIDER') != "") {
00408                 $searchClass = @constant('SEARCH_PROVIDER');
00409 
00410                 if (! class_exists($searchClass))
00411                     self::IncludeBackend($searchClass);
00412 
00413                 if (class_exists($searchClass))
00414                     $aSearchProvider = new $searchClass();
00415                 else
00416                     throw new FatalMisconfigurationException(sprintf("Search provider '%s' can not be loaded. Check configuration!", $searchClass));
00417             }
00418             // get the searchprovider from the backend
00419             else
00420                 $aSearchProvider = self::GetBackend()->GetSearchProvider();
00421 
00422             if (in_array('ISearchProvider', class_implements($aSearchProvider)))
00423                 ZPush::$searchProvider = $aSearchProvider;
00424             else
00425                 throw new FatalNotImplementedException("Instantiated SearchProvider does not implement the ISearchProvider interface!");
00426         }
00427         return ZPush::$searchProvider;
00428     }
00429 
00437     static public function GetBackend() {
00438         // if the backend is not yet loaded, load backend drivers and instantiate it
00439         if (!isset(ZPush::$backend)) {
00440             // Initialize our backend
00441             $ourBackend = @constant('BACKEND_PROVIDER');
00442             self::IncludeBackend($ourBackend);
00443 
00444             if (class_exists($ourBackend))
00445                 ZPush::$backend = new $ourBackend();
00446             else
00447                 throw new FatalMisconfigurationException(sprintf("Backend provider '%s' can not be loaded. Check configuration!", $ourBackend));
00448         }
00449         return ZPush::$backend;
00450     }
00451 
00458     static public function GetAdditionalSyncFolders() {
00459         // TODO if there are any user based folders which should be synchronized, they have to be returned here as well!!
00460         return self::$addSyncFolders;
00461     }
00462 
00472     static public function GetAdditionalSyncFolderStore($folderid, $noDebug = false) {
00473         $val = (isset(self::$addSyncFolders[$folderid]->Store))? self::$addSyncFolders[$folderid]->Store : false;
00474         if (!$noDebug)
00475             ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetAdditionalSyncFolderStore('%s'): '%s'", $folderid, Utils::PrintAsString($val)));
00476         return $val;
00477     }
00478 
00488     static public function getSyncObjectFromFolderClass($folderclass) {
00489         if (!isset(self::$classes[$folderclass]))
00490             throw new FatalNotImplementedException("Class '$folderclass' is not supported");
00491 
00492         $class = self::$classes[$folderclass][self::CLASS_NAME];
00493         if (self::$classes[$folderclass][self::CLASS_REQUIRESPROTOCOLVERSION])
00494             return new $class(Request::GetProtocolVersion());
00495         else
00496             return new $class();
00497     }
00498 
00507     static public function getDefaultFolderTypeFromFolderClass($folderclass) {
00508         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::getDefaultFolderTypeFromFolderClass('%s'): '%d'", $folderclass, self::$classes[$folderclass][self::CLASS_DEFAULTTYPE]));
00509         return self::$classes[$folderclass][self::CLASS_DEFAULTTYPE];
00510     }
00511 
00520     static public function GetFolderClassFromFolderType($foldertype) {
00521         $class = false;
00522         foreach (self::$classes as $aClass => $cprops) {
00523             if ($cprops[self::CLASS_DEFAULTTYPE] == $foldertype || in_array($foldertype, $cprops[self::CLASS_OTHERTYPES])) {
00524                 $class = $aClass;
00525                 break;
00526             }
00527         }
00528         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetFolderClassFromFolderType('%s'): %s", $foldertype, Utils::PrintAsString($class)));
00529         return $class;
00530     }
00531 
00543     static public function PrintZPushLegal($message = "", $additionalMessage = "") {
00544         ZLog::Write(LOGLEVEL_DEBUG,"ZPush::PrintZPushLegal()");
00545         $zpush_version = @constant('ZPUSH_VERSION');
00546 
00547         if ($message)
00548             $message = "<h3>". $message . "</h3>";
00549         if ($additionalMessage)
00550             $additionalMessage .= "<br>";
00551 
00552         header("Content-type: text/html");
00553         print <<<END
00554         <html>
00555         <header>
00556         <title>Z-Push ActiveSync</title>
00557         </header>
00558         <body>
00559         <font face="verdana">
00560         <h2>Z-Push - Open Source ActiveSync</h2>
00561         <b>Version $zpush_version</b><br>
00562         $message $additionalMessage
00563         <br><br>
00564         More information about Z-Push can be found at:<br>
00565         <a href="http://z-push.sf.net/">Z-Push homepage</a><br>
00566         <a href="http://z-push.sf.net/download">Z-Push download page at BerliOS</a><br>
00567         <a href="http://z-push.sf.net/tracker">Z-Push Bugtracker and Roadmap</a><br>
00568         <br>
00569         All modifications to this sourcecode must be published and returned to the community.<br>
00570         Please see <a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPLv3 License</a> for details.<br>
00571         </font face="verdana">
00572         </body>
00573         </html>
00574 END;
00575     }
00576 
00583     static public function GetLatestSupportedASVersion() {
00584         return end(self::$supportedASVersions);
00585     }
00586 
00594     static public function GetSupportedASVersion() {
00595         $version = self::GetBackend()->GetSupportedASVersion();
00596         if (!in_array($version, self::$supportedASVersions))
00597             throw new FatalNotImplementedException(sprintf("AS version '%s' reported by the backend is not supported", $version));
00598 
00599         return $version;
00600     }
00601 
00608     static public function GetServerHeader() {
00609         if (self::GetSupportedASVersion() == self::ASV_25)
00610             return "MS-Server-ActiveSync: 6.5.7638.1";
00611         else
00612             return "MS-Server-ActiveSync: ". self::GetSupportedASVersion();
00613     }
00614 
00623     static public function GetSupportedProtocolVersions($valueOnly = false) {
00624         $versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
00625         ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedProtocolVersions(): " . $versions);
00626 
00627         if ($valueOnly === true)
00628             return $versions;
00629 
00630         return "MS-ASProtocolVersions: " . $versions;
00631     }
00632 
00639     static public function GetSupportedCommands() {
00640         $asCommands = array();
00641         // filter all non-activesync commands
00642         foreach (self::$supportedCommands as $c=>$v)
00643             if (!self::checkCommandOptions($c, self::NOACTIVESYNCCOMMAND) &&
00644                 self::checkCommandOptions($c, self::GetSupportedASVersion()))
00645                 $asCommands[] = Utils::GetCommandFromCode($c);
00646 
00647         $commands = implode(',', $asCommands);
00648         ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedCommands(): " . $commands);
00649         return "MS-ASProtocolCommands: " . $commands;
00650     }
00651 
00660     static public function GetRequestHandlerForCommand($commandCode) {
00661         if (!array_key_exists($commandCode, self::$supportedCommands) ||
00662             !array_key_exists(self::REQUESTHANDLER, self::$supportedCommands[$commandCode]) )
00663             throw new FatalNotImplementedException(sprintf("Command '%s' has no request handler or class", Utils::GetCommandFromCode($commandCode)));
00664 
00665         $class = self::$supportedCommands[$commandCode][self::REQUESTHANDLER];
00666         if ($class == "Webservice")
00667             $handlerclass = REAL_BASE_PATH . "lib/webservice/webservice.php";
00668         else
00669             $handlerclass = REAL_BASE_PATH . "lib/request/" . strtolower($class) . ".php";
00670 
00671         if (is_file($handlerclass))
00672             include($handlerclass);
00673 
00674         if (class_exists($class))
00675             return new $class();
00676         else
00677             throw new FatalNotImplementedException(sprintf("Request handler '%s' can not be loaded", $class));
00678     }
00679 
00688     static public function CommandNeedsAuthentication($commandCode) {
00689         $stat = ! self::checkCommandOptions($commandCode, self::UNAUTHENTICATED);
00690         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsAuthentication(%d): %s", $commandCode, Utils::PrintAsString($stat)));
00691         return $stat;
00692     }
00693 
00702     static public function CommandNeedsProvisioning($commandCode) {
00703         $stat = ! self::checkCommandOptions($commandCode, self::UNPROVISIONED);
00704         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsProvisioning(%s): %s", $commandCode, Utils::PrintAsString($stat)));
00705         return $stat;
00706     }
00707 
00716     static public function CommandNeedsPlainInput($commandCode) {
00717         $stat = self::checkCommandOptions($commandCode, self::PLAININPUT);
00718         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsPlainInput(%d): %s", $commandCode, Utils::PrintAsString($stat)));
00719         return $stat;
00720     }
00721 
00730     static public function HierarchyCommand($commandCode) {
00731         $stat = self::checkCommandOptions($commandCode, self::HIERARCHYCOMMAND);
00732         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::HierarchyCommand(%d): %s", $commandCode, Utils::PrintAsString($stat)));
00733         return $stat;
00734     }
00735 
00746     static private function checkCommandOptions($commandCode, $option) {
00747         if ($commandCode === false) return false;
00748 
00749         if (!array_key_exists($commandCode, self::$supportedCommands))
00750             throw new FatalNotImplementedException(sprintf("Command '%s' is not supported", Utils::GetCommandFromCode($commandCode)));
00751 
00752         $capa = self::$supportedCommands[$commandCode];
00753         $defcapa = in_array($option, $capa, true);
00754 
00755         // if not looking for a default capability, check if the command is supported since a previous AS version
00756         if (!$defcapa) {
00757             $verkey = array_search($option, self::$supportedASVersions, true);
00758             if ($verkey !== false && ($verkey >= array_search($capa[0], self::$supportedASVersions))) {
00759                 $defcapa = true;
00760             }
00761         }
00762 
00763         return $defcapa;
00764     }
00765 
00766 }
00767 ?>