source: trunk/zpush/lib/core/zpush.php @ 7589

Revision 7589, 32.0 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   zpush.php
4* Project   :   Z-Push
5* Descr     :   Core functionalities
6*
7* Created   :   12.04.2011
8*
9* Copyright 2007 - 2012 Zarafa Deutschland GmbH
10*
11* This program is free software: you can redistribute it and/or modify
12* it under the terms of the GNU Affero General Public License, version 3,
13* as published by the Free Software Foundation with the following additional
14* term according to sec. 7:
15*
16* According to sec. 7 of the GNU Affero General Public License, version 3,
17* the terms of the AGPL are supplemented with the following terms:
18*
19* "Zarafa" is a registered trademark of Zarafa B.V.
20* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
21* The licensing of the Program under the AGPL does not imply a trademark license.
22* Therefore any rights, title and interest in our trademarks remain entirely with us.
23*
24* However, if you propagate an unmodified version of the Program you are
25* allowed to use the term "Z-Push" to indicate that you distribute the Program.
26* Furthermore you may use our trademarks where it is necessary to indicate
27* the intended purpose of a product or service provided you use it in accordance
28* with honest practices in industrial or commercial matters.
29* If you want to propagate modified versions of the Program under the name "Z-Push",
30* you may only do so if you have a written permission by Zarafa Deutschland GmbH
31* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
32*
33* This program is distributed in the hope that it will be useful,
34* but WITHOUT ANY WARRANTY; without even the implied warranty of
35* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36* GNU Affero General Public License for more details.
37*
38* You should have received a copy of the GNU Affero General Public License
39* along with this program.  If not, see <http://www.gnu.org/licenses/>.
40*
41* Consult LICENSE file for details
42************************************************/
43
44
45class ZPush {
46    const UNAUTHENTICATED = 1;
47    const UNPROVISIONED = 2;
48    const NOACTIVESYNCCOMMAND = 3;
49    const WEBSERVICECOMMAND = 4;
50    const HIERARCHYCOMMAND = 5;
51    const PLAININPUT = 6;
52    const REQUESTHANDLER = 7;
53    const CLASS_NAME = 1;
54    const CLASS_REQUIRESPROTOCOLVERSION = 2;
55    const CLASS_DEFAULTTYPE = 3;
56    const CLASS_OTHERTYPES = 4;
57
58    // AS versions
59    const ASV_1 = "1.0";
60    const ASV_2 = "2.0";
61    const ASV_21 = "2.1";
62    const ASV_25 = "2.5";
63    const ASV_12 = "12.0";
64    const ASV_121 = "12.1";
65    const ASV_14 = "14.0";
66
67    /**
68     * Command codes for base64 encoded requests (AS >= 12.1)
69     */
70    const COMMAND_SYNC = 0;
71    const COMMAND_SENDMAIL = 1;
72    const COMMAND_SMARTFORWARD = 2;
73    const COMMAND_SMARTREPLY = 3;
74    const COMMAND_GETATTACHMENT = 4;
75    const COMMAND_FOLDERSYNC = 9;
76    const COMMAND_FOLDERCREATE = 10;
77    const COMMAND_FOLDERDELETE = 11;
78    const COMMAND_FOLDERUPDATE = 12;
79    const COMMAND_MOVEITEMS = 13;
80    const COMMAND_GETITEMESTIMATE = 14;
81    const COMMAND_MEETINGRESPONSE = 15;
82    const COMMAND_SEARCH = 16;
83    const COMMAND_SETTINGS = 17;
84    const COMMAND_PING = 18;
85    const COMMAND_ITEMOPERATIONS = 19;
86    const COMMAND_PROVISION = 20;
87    const COMMAND_RESOLVERECIPIENTS = 21;
88    const COMMAND_VALIDATECERT = 22;
89
90    // Deprecated commands
91    const COMMAND_GETHIERARCHY = -1;
92    const COMMAND_CREATECOLLECTION = -2;
93    const COMMAND_DELETECOLLECTION = -3;
94    const COMMAND_MOVECOLLECTION = -4;
95    const COMMAND_NOTIFY = -5;
96
97    // Webservice commands
98    const COMMAND_WEBSERVICE_DEVICE = -100;
99
100    static private $supportedASVersions = array(
101                    self::ASV_1,
102                    self::ASV_2,
103                    self::ASV_21,
104                    self::ASV_25,
105                    self::ASV_12,
106                    self::ASV_121,
107                    self::ASV_14
108                );
109
110    static private $supportedCommands = array(
111                    // COMMAND                             // AS VERSION   // REQUESTHANDLER                        // OTHER SETTINGS
112                    self::COMMAND_SYNC              => array(self::ASV_1,  self::REQUESTHANDLER => "Sync"),
113                    self::COMMAND_SENDMAIL          => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
114                    self::COMMAND_SMARTFORWARD      => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
115                    self::COMMAND_SMARTREPLY        => array(self::ASV_1,  self::REQUESTHANDLER => "SendMail"),
116                    self::COMMAND_GETATTACHMENT     => array(self::ASV_1,  self::REQUESTHANDLER => "GetAttachment"),
117                    self::COMMAND_GETHIERARCHY      => array(self::ASV_1,  self::REQUESTHANDLER => "GetHierarchy",  self::HIERARCHYCOMMAND),            // deprecated but implemented
118                    self::COMMAND_CREATECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
119                    self::COMMAND_DELETECOLLECTION  => array(self::ASV_1),                                                                              // deprecated & not implemented
120                    self::COMMAND_MOVECOLLECTION    => array(self::ASV_1),                                                                              // deprecated & not implemented
121                    self::COMMAND_FOLDERSYNC        => array(self::ASV_2,  self::REQUESTHANDLER => "FolderSync",    self::HIERARCHYCOMMAND),
122                    self::COMMAND_FOLDERCREATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
123                    self::COMMAND_FOLDERDELETE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
124                    self::COMMAND_FOLDERUPDATE      => array(self::ASV_2,  self::REQUESTHANDLER => "FolderChange",  self::HIERARCHYCOMMAND),
125                    self::COMMAND_MOVEITEMS         => array(self::ASV_1,  self::REQUESTHANDLER => "MoveItems"),
126                    self::COMMAND_GETITEMESTIMATE   => array(self::ASV_1,  self::REQUESTHANDLER => "GetItemEstimate"),
127                    self::COMMAND_MEETINGRESPONSE   => array(self::ASV_1,  self::REQUESTHANDLER => "MeetingResponse"),
128                    self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_1,  self::REQUESTHANDLER => false),
129                    self::COMMAND_VALIDATECERT      => array(self::ASV_1,  self::REQUESTHANDLER => false),
130                    self::COMMAND_PROVISION         => array(self::ASV_25, self::REQUESTHANDLER => "Provisioning",  self::UNAUTHENTICATED, self::UNPROVISIONED),
131                    self::COMMAND_SEARCH            => array(self::ASV_1,  self::REQUESTHANDLER => "Search"),
132                    self::COMMAND_PING              => array(self::ASV_2,  self::REQUESTHANDLER => "Ping",          self::UNPROVISIONED),
133                    self::COMMAND_NOTIFY            => array(self::ASV_1,  self::REQUESTHANDLER => "Notify"),                                           // deprecated & not implemented
134                    self::COMMAND_ITEMOPERATIONS    => array(self::ASV_12, self::REQUESTHANDLER => "ItemOperations"),
135                    self::COMMAND_SETTINGS          => array(self::ASV_12, self::REQUESTHANDLER => "Settings"),
136
137                    self::COMMAND_WEBSERVICE_DEVICE => array(self::REQUESTHANDLER => "Webservice", self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND),
138                );
139
140
141
142    static private $classes = array(
143                    "Email"     => array(
144                                        self::CLASS_NAME => "SyncMail",
145                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
146                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_INBOX,
147                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_OTHER, SYNC_FOLDER_TYPE_DRAFTS, SYNC_FOLDER_TYPE_WASTEBASKET,
148                                                                        SYNC_FOLDER_TYPE_SENTMAIL, SYNC_FOLDER_TYPE_OUTBOX, SYNC_FOLDER_TYPE_USER_MAIL,
149                                                                        SYNC_FOLDER_TYPE_JOURNAL, SYNC_FOLDER_TYPE_USER_JOURNAL),
150                                   ),
151                    "Contacts"  => array(
152                                        self::CLASS_NAME => "SyncContact",
153                                        self::CLASS_REQUIRESPROTOCOLVERSION => true,
154                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_CONTACT,
155                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_CONTACT),
156                                   ),
157                    "Calendar"  => array(
158                                        self::CLASS_NAME => "SyncAppointment",
159                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
160                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_APPOINTMENT,
161                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_APPOINTMENT),
162                                   ),
163                    "Tasks"     => array(
164                                        self::CLASS_NAME => "SyncTask",
165                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
166                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_TASK,
167                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_TASK),
168                                   ),
169                    "Notes" => array(
170                                        self::CLASS_NAME => "SyncNote",
171                                        self::CLASS_REQUIRESPROTOCOLVERSION => false,
172                                        self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_NOTE,
173                                        self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_NOTE),
174                                   ),
175                );
176
177
178    static private $stateMachine;
179    static private $searchProvider;
180    static private $deviceManager;
181    static private $topCollector;
182    static private $backend;
183    static private $addSyncFolders;
184
185
186    /**
187     * Verifies configuration
188     *
189     * @access public
190     * @return boolean
191     * @throws FatalMisconfigurationException
192     */
193    static public function CheckConfig() {
194        // check the php version
195        if (version_compare(phpversion(),'5.1.0') < 0)
196            throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.1 is used.");
197
198        // some basic checks
199        if (!defined('BASE_PATH'))
200            throw new FatalMisconfigurationException("The BASE_PATH is not configured. Check if the config.php file is in place.");
201
202        if (substr(BASE_PATH, -1,1) != "/")
203            throw new FatalMisconfigurationException("The BASE_PATH should terminate with a '/'");
204
205        if (!file_exists(BASE_PATH))
206            throw new FatalMisconfigurationException("The configured BASE_PATH does not exist or can not be accessed.");
207
208        if (defined('BASE_PATH_CLI') && file_exists(BASE_PATH_CLI))
209            define('REAL_BASE_PATH', BASE_PATH_CLI);
210        else
211            define('REAL_BASE_PATH', BASE_PATH);
212
213        if (!defined('LOGFILEDIR'))
214            throw new FatalMisconfigurationException("The LOGFILEDIR is not configured. Check if the config.php file is in place.");
215
216        if (substr(LOGFILEDIR, -1,1) != "/")
217            throw new FatalMisconfigurationException("The LOGFILEDIR should terminate with a '/'");
218
219        if (!file_exists(LOGFILEDIR))
220            throw new FatalMisconfigurationException("The configured LOGFILEDIR does not exist or can not be accessed.");
221
222        if (!touch(LOGFILE))
223            throw new FatalMisconfigurationException("The configured LOGFILE can not be modified.");
224
225        if (!touch(LOGERRORFILE))
226            throw new FatalMisconfigurationException("The configured LOGERRORFILE can not be modified.");
227
228        // set time zone
229        // code contributed by Robert Scheck (rsc) - more information: https://developer.berlios.de/mantis/view.php?id=479
230        if(function_exists("date_default_timezone_set")) {
231            if(defined('TIMEZONE') ? constant('TIMEZONE') : false) {
232                if (! @date_default_timezone_set(TIMEZONE))
233                    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')));
234            }
235            else if(!ini_get('date.timezone')) {
236                date_default_timezone_set('Europe/Amsterdam');
237            }
238        }
239
240        return true;
241    }
242
243    /**
244     * Verifies Timezone, StateMachine and Backend configuration
245     *
246     * @access public
247     * @return boolean
248     * @trows FatalMisconfigurationException
249     */
250    static public function CheckAdvancedConfig() {
251        global $specialLogUsers, $additionalFolders;
252
253        if (!is_array($specialLogUsers))
254            throw new FatalMisconfigurationException("The WBXML log users is not an array.");
255
256        if (!defined('SINK_FORCERECHECK')) {
257            define('SINK_FORCERECHECK', 300);
258        }
259        else if (SINK_FORCERECHECK !== false && (!is_int(SINK_FORCERECHECK) || SINK_FORCERECHECK < 1))
260            throw new FatalMisconfigurationException("The SINK_FORCERECHECK value must be 'false' or a number higher than 0.");
261
262        // the check on additional folders will not throw hard errors, as this is probably changed on live systems
263        if (isset($additionalFolders) && !is_array($additionalFolders))
264            ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : The additional folders synchronization not available as array.");
265        else {
266            self::$addSyncFolders = array();
267
268            // process configured data
269            foreach ($additionalFolders as $af) {
270
271                if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) {
272                    ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored.");
273                    continue;
274                }
275
276                if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") {
277                    ZLog::Write(LOGLEVEL_WARN, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored.");
278                    continue;
279                }
280
281                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))) {
282                    ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::CheckConfig() : the type of the additional synchronization folder '%s is not permitted.", $af['name']));
283                    continue;
284                }
285
286                $folder = new SyncFolder();
287                $folder->serverid = $af['folderid'];
288                $folder->parentid = 0;                  // only top folders are supported
289                $folder->displayname = $af['name'];
290                $folder->type = $af['type'];
291                // save store as custom property which is not streamed directly to the device
292                $folder->NoBackendFolder = true;
293                $folder->Store = $af['store'];
294                self::$addSyncFolders[$folder->serverid] = $folder;
295            }
296
297        }
298
299        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Used timezone '%s'", date_default_timezone_get()));
300
301        // get the statemachine, which will also try to load the backend.. This could throw errors
302        self::GetStateMachine();
303    }
304
305    /**
306     * Returns the StateMachine object
307     * which has to be an IStateMachine implementation
308     *
309     * @access public
310     * @return object   implementation of IStateMachine
311     * @throws FatalNotImplementedException
312     */
313    static public function GetStateMachine() {
314        if (!isset(ZPush::$stateMachine)) {
315            // the backend could also return an own IStateMachine implementation
316            $backendStateMachine = self::GetBackend()->GetStateMachine();
317
318            // if false is returned, use the default StateMachine
319            if ($backendStateMachine !== false) {
320                ZLog::Write(LOGLEVEL_DEBUG, "Backend implementation of IStateMachine: ".get_class($backendStateMachine));
321                if (in_array('IStateMachine', class_implements($backendStateMachine)))
322                    ZPush::$stateMachine = $backendStateMachine;
323                else
324                    throw new FatalNotImplementedException("State machine returned by the backend does not implement the IStateMachine interface!");
325            }
326            else {
327                // Initialize the default StateMachine
328                include_once('lib/default/filestatemachine.php');
329                ZPush::$stateMachine = new FileStateMachine();
330            }
331        }
332        return ZPush::$stateMachine;
333    }
334
335    /**
336     * Returns the DeviceManager object
337     *
338     * @param boolean   $initialize     (opt) default true: initializes the DeviceManager if not already done
339     *
340     * @access public
341     * @return object DeviceManager
342     */
343    static public function GetDeviceManager($initialize = true) {
344        if (!isset(ZPush::$deviceManager) && $initialize)
345            ZPush::$deviceManager = new DeviceManager();
346
347        return ZPush::$deviceManager;
348    }
349
350    /**
351     * Returns the Top data collector object
352     *
353     * @access public
354     * @return object TopCollector
355     */
356    static public function GetTopCollector() {
357        if (!isset(ZPush::$topCollector))
358            ZPush::$topCollector = new TopCollector();
359
360        return ZPush::$topCollector;
361    }
362
363    /**
364     * Loads a backend file
365     *
366     * @param string $backendname
367
368     * @access public
369     * @throws FatalNotImplementedException
370     * @return boolean
371     */
372    static public function IncludeBackend($backendname) {
373        if ($backendname == false) return false;
374
375        $backendname = strtolower($backendname);
376        if (substr($backendname, 0, 7) !== 'backend')
377            throw new FatalNotImplementedException(sprintf("Backend '%s' is not allowed",$backendname));
378
379        $rbn = substr($backendname, 7);
380
381        $subdirbackend = REAL_BASE_PATH . "backend/" . $rbn . "/" . $rbn . ".php";
382        $stdbackend = REAL_BASE_PATH . "backend/" . $rbn . ".php";
383
384        if (is_file($subdirbackend))
385            $toLoad = $subdirbackend;
386        else if (is_file($stdbackend))
387            $toLoad = $stdbackend;
388        else
389            return false;
390
391        ZLog::Write(LOGLEVEL_DEBUG, sprintf("Including backend file: '%s'", $toLoad));
392        include_once($toLoad);
393        return true;
394    }
395
396    /**
397     * Returns the SearchProvider object
398     * which has to be an ISearchProvider implementation
399     *
400     * @access public
401     * @return object   implementation of ISearchProvider
402     * @throws FatalMisconfigurationException, FatalNotImplementedException
403     */
404    static public function GetSearchProvider() {
405        if (!isset(ZPush::$searchProvider)) {
406            // is a global searchprovider configured ? It will  outrank the backend
407            if (defined('SEARCH_PROVIDER') && @constant('SEARCH_PROVIDER') != "") {
408                $searchClass = @constant('SEARCH_PROVIDER');
409
410                if (! class_exists($searchClass))
411                    self::IncludeBackend($searchClass);
412
413                if (class_exists($searchClass))
414                    $aSearchProvider = new $searchClass();
415                else
416                    throw new FatalMisconfigurationException(sprintf("Search provider '%s' can not be loaded. Check configuration!", $searchClass));
417            }
418            // get the searchprovider from the backend
419            else
420                $aSearchProvider = self::GetBackend()->GetSearchProvider();
421
422            if (in_array('ISearchProvider', class_implements($aSearchProvider)))
423                ZPush::$searchProvider = $aSearchProvider;
424            else
425                throw new FatalNotImplementedException("Instantiated SearchProvider does not implement the ISearchProvider interface!");
426        }
427        return ZPush::$searchProvider;
428    }
429
430    /**
431     * Returns the Backend for this request
432     * the backend has to be an IBackend implementation
433     *
434     * @access public
435     * @return object     IBackend implementation
436     */
437    static public function GetBackend() {
438        // if the backend is not yet loaded, load backend drivers and instantiate it
439        if (!isset(ZPush::$backend)) {
440            // Initialize our backend
441            $ourBackend = @constant('BACKEND_PROVIDER');
442            self::IncludeBackend($ourBackend);
443
444            if (class_exists($ourBackend))
445                ZPush::$backend = new $ourBackend();
446            else
447                throw new FatalMisconfigurationException(sprintf("Backend provider '%s' can not be loaded. Check configuration!", $ourBackend));
448        }
449        return ZPush::$backend;
450    }
451
452    /**
453     * Returns additional folder objects which should be synchronized to the device
454     *
455     * @access public
456     * @return array
457     */
458    static public function GetAdditionalSyncFolders() {
459        // TODO if there are any user based folders which should be synchronized, they have to be returned here as well!!
460        return self::$addSyncFolders;
461    }
462
463    /**
464     * Returns additional folder objects which should be synchronized to the device
465     *
466     * @param string        $folderid
467     * @param boolean       $noDebug        (opt) by default, debug message is shown
468     *
469     * @access public
470     * @return string
471     */
472    static public function GetAdditionalSyncFolderStore($folderid, $noDebug = false) {
473        $val = (isset(self::$addSyncFolders[$folderid]->Store))? self::$addSyncFolders[$folderid]->Store : false;
474        if (!$noDebug)
475            ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetAdditionalSyncFolderStore('%s'): '%s'", $folderid, Utils::PrintAsString($val)));
476        return $val;
477    }
478
479    /**
480     * Returns a SyncObject class name for a folder class
481     *
482     * @param string $folderclass
483     *
484     * @access public
485     * @return string
486     * @throws FatalNotImplementedException
487     */
488    static public function getSyncObjectFromFolderClass($folderclass) {
489        if (!isset(self::$classes[$folderclass]))
490            throw new FatalNotImplementedException("Class '$folderclass' is not supported");
491
492        $class = self::$classes[$folderclass][self::CLASS_NAME];
493        if (self::$classes[$folderclass][self::CLASS_REQUIRESPROTOCOLVERSION])
494            return new $class(Request::GetProtocolVersion());
495        else
496            return new $class();
497    }
498
499    /**
500     * Returns the default foldertype for a folder class
501     *
502     * @param string $folderclass   folderclass sent by the mobile
503     *
504     * @access public
505     * @return string
506     */
507    static public function getDefaultFolderTypeFromFolderClass($folderclass) {
508        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::getDefaultFolderTypeFromFolderClass('%s'): '%d'", $folderclass, self::$classes[$folderclass][self::CLASS_DEFAULTTYPE]));
509        return self::$classes[$folderclass][self::CLASS_DEFAULTTYPE];
510    }
511
512    /**
513     * Returns the folder class for a foldertype
514     *
515     * @param string $foldertype
516     *
517     * @access public
518     * @return string/false     false if no class for this type is available
519     */
520    static public function GetFolderClassFromFolderType($foldertype) {
521        $class = false;
522        foreach (self::$classes as $aClass => $cprops) {
523            if ($cprops[self::CLASS_DEFAULTTYPE] == $foldertype || in_array($foldertype, $cprops[self::CLASS_OTHERTYPES])) {
524                $class = $aClass;
525                break;
526            }
527        }
528        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetFolderClassFromFolderType('%s'): %s", $foldertype, Utils::PrintAsString($class)));
529        return $class;
530    }
531
532    /**
533     * Prints the Z-Push legal header to STDOUT
534     * Using this breaks ActiveSync synchronization if wbxml is expected
535     *
536     * @param string $message               (opt) message to be displayed
537     * @param string $additionalMessage     (opt) additional message to be displayed
538
539     * @access public
540     * @return
541     *
542     */
543    static public function PrintZPushLegal($message = "", $additionalMessage = "") {
544        ZLog::Write(LOGLEVEL_DEBUG,"ZPush::PrintZPushLegal()");
545        $zpush_version = @constant('ZPUSH_VERSION');
546
547        if ($message)
548            $message = "<h3>". $message . "</h3>";
549        if ($additionalMessage)
550            $additionalMessage .= "<br>";
551
552        header("Content-type: text/html");
553        print <<<END
554        <html>
555        <header>
556        <title>Z-Push ActiveSync</title>
557        </header>
558        <body>
559        <font face="verdana">
560        <h2>Z-Push - Open Source ActiveSync</h2>
561        <b>Version $zpush_version</b><br>
562        $message $additionalMessage
563        <br><br>
564        More information about Z-Push can be found at:<br>
565        <a href="http://z-push.sf.net/">Z-Push homepage</a><br>
566        <a href="http://z-push.sf.net/download">Z-Push download page at BerliOS</a><br>
567        <a href="http://z-push.sf.net/tracker">Z-Push Bugtracker and Roadmap</a><br>
568        <br>
569        All modifications to this sourcecode must be published and returned to the community.<br>
570        Please see <a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPLv3 License</a> for details.<br>
571        </font face="verdana">
572        </body>
573        </html>
574END;
575    }
576
577    /**
578     * Indicates the latest AS version supported by Z-Push
579     *
580     * @access public
581     * @return string
582     */
583    static public function GetLatestSupportedASVersion() {
584        return end(self::$supportedASVersions);
585    }
586
587    /**
588     * Indicates which is the highest AS version supported by the backend
589     *
590     * @access public
591     * @return string
592     * @throws FatalNotImplementedException     if the backend returns an invalid version
593     */
594    static public function GetSupportedASVersion() {
595        $version = self::GetBackend()->GetSupportedASVersion();
596        if (!in_array($version, self::$supportedASVersions))
597            throw new FatalNotImplementedException(sprintf("AS version '%s' reported by the backend is not supported", $version));
598
599        return $version;
600    }
601
602    /**
603     * Returns AS server header
604     *
605     * @access public
606     * @return string
607     */
608    static public function GetServerHeader() {
609        if (self::GetSupportedASVersion() == self::ASV_25)
610            return "MS-Server-ActiveSync: 6.5.7638.1";
611        else
612            return "MS-Server-ActiveSync: ". self::GetSupportedASVersion();
613    }
614
615    /**
616     * Returns AS protocol versions which are supported
617     *
618     * @param boolean   $valueOnly  (opt) default: false (also returns the header name)
619     *
620     * @access public
621     * @return string
622     */
623    static public function GetSupportedProtocolVersions($valueOnly = false) {
624        $versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1)));
625        ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedProtocolVersions(): " . $versions);
626
627        if ($valueOnly === true)
628            return $versions;
629
630        return "MS-ASProtocolVersions: " . $versions;
631    }
632
633    /**
634     * Returns AS commands which are supported
635     *
636     * @access public
637     * @return string
638     */
639    static public function GetSupportedCommands() {
640        $asCommands = array();
641        // filter all non-activesync commands
642        foreach (self::$supportedCommands as $c=>$v)
643            if (!self::checkCommandOptions($c, self::NOACTIVESYNCCOMMAND) &&
644                self::checkCommandOptions($c, self::GetSupportedASVersion()))
645                $asCommands[] = Utils::GetCommandFromCode($c);
646
647        $commands = implode(',', $asCommands);
648        ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedCommands(): " . $commands);
649        return "MS-ASProtocolCommands: " . $commands;
650    }
651
652    /**
653     * Loads and instantiates a request processor for a command
654     *
655     * @param int $commandCode
656     *
657     * @access public
658     * @return RequestProcessor sub-class
659     */
660    static public function GetRequestHandlerForCommand($commandCode) {
661        if (!array_key_exists($commandCode, self::$supportedCommands) ||
662            !array_key_exists(self::REQUESTHANDLER, self::$supportedCommands[$commandCode]) )
663            throw new FatalNotImplementedException(sprintf("Command '%s' has no request handler or class", Utils::GetCommandFromCode($commandCode)));
664
665        $class = self::$supportedCommands[$commandCode][self::REQUESTHANDLER];
666        if ($class == "Webservice")
667            $handlerclass = REAL_BASE_PATH . "lib/webservice/webservice.php";
668        else
669            $handlerclass = REAL_BASE_PATH . "lib/request/" . strtolower($class) . ".php";
670
671        if (is_file($handlerclass))
672            include($handlerclass);
673
674        if (class_exists($class))
675            return new $class();
676        else
677            throw new FatalNotImplementedException(sprintf("Request handler '%s' can not be loaded", $class));
678    }
679
680    /**
681     * Indicates if a commands requires authentication or not
682     *
683     * @param int $commandCode
684     *
685     * @access public
686     * @return boolean
687     */
688    static public function CommandNeedsAuthentication($commandCode) {
689        $stat = ! self::checkCommandOptions($commandCode, self::UNAUTHENTICATED);
690        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsAuthentication(%d): %s", $commandCode, Utils::PrintAsString($stat)));
691        return $stat;
692    }
693
694    /**
695     * Indicates if the Provisioning check has to be forced on these commands
696     *
697     * @param string $commandCode
698
699     * @access public
700     * @return boolean
701     */
702    static public function CommandNeedsProvisioning($commandCode) {
703        $stat = ! self::checkCommandOptions($commandCode, self::UNPROVISIONED);
704        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsProvisioning(%s): %s", $commandCode, Utils::PrintAsString($stat)));
705        return $stat;
706    }
707
708    /**
709     * Indicates if these commands expect plain text input instead of wbxml
710     *
711     * @param string $commandCode
712     *
713     * @access public
714     * @return boolean
715     */
716    static public function CommandNeedsPlainInput($commandCode) {
717        $stat = self::checkCommandOptions($commandCode, self::PLAININPUT);
718        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsPlainInput(%d): %s", $commandCode, Utils::PrintAsString($stat)));
719        return $stat;
720    }
721
722    /**
723     * Indicates if the comand to be executed operates on the hierarchy
724     *
725     * @param int $commandCode
726
727     * @access public
728     * @return boolean
729     */
730    static public function HierarchyCommand($commandCode) {
731        $stat = self::checkCommandOptions($commandCode, self::HIERARCHYCOMMAND);
732        ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::HierarchyCommand(%d): %s", $commandCode, Utils::PrintAsString($stat)));
733        return $stat;
734    }
735
736    /**
737     * Checks access types of a command
738     *
739     * @param string $commandCode   a commandCode
740     * @param string $option        e.g. self::UNAUTHENTICATED
741
742     * @access private
743     * @throws FatalNotImplementedException
744     * @return object StateMachine
745     */
746    static private function checkCommandOptions($commandCode, $option) {
747        if ($commandCode === false) return false;
748
749        if (!array_key_exists($commandCode, self::$supportedCommands))
750            throw new FatalNotImplementedException(sprintf("Command '%s' is not supported", Utils::GetCommandFromCode($commandCode)));
751
752        $capa = self::$supportedCommands[$commandCode];
753        $defcapa = in_array($option, $capa, true);
754
755        // if not looking for a default capability, check if the command is supported since a previous AS version
756        if (!$defcapa) {
757            $verkey = array_search($option, self::$supportedASVersions, true);
758            if ($verkey !== false && ($verkey >= array_search($capa[0], self::$supportedASVersions))) {
759                $defcapa = true;
760            }
761        }
762
763        return $defcapa;
764    }
765
766}
767?>
Note: See TracBrowser for help on using the repository browser.