Back to index

d-push  2.0
index.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   index.php
00004 * Project   :   Z-Push
00005 * Descr     :   This is the entry point
00006 *               through which all requests
00007 *               are processed.
00008 *
00009 * Created   :   01.10.2007
00010 *
00011 * Copyright 2007 - 2012 Zarafa Deutschland GmbH
00012 *
00013 * This program is free software: you can redistribute it and/or modify
00014 * it under the terms of the GNU Affero General Public License, version 3,
00015 * as published by the Free Software Foundation with the following additional
00016 * term according to sec. 7:
00017 *
00018 * According to sec. 7 of the GNU Affero General Public License, version 3,
00019 * the terms of the AGPL are supplemented with the following terms:
00020 *
00021 * "Zarafa" is a registered trademark of Zarafa B.V.
00022 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00023 * The licensing of the Program under the AGPL does not imply a trademark license.
00024 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00025 *
00026 * However, if you propagate an unmodified version of the Program you are
00027 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00028 * Furthermore you may use our trademarks where it is necessary to indicate
00029 * the intended purpose of a product or service provided you use it in accordance
00030 * with honest practices in industrial or commercial matters.
00031 * If you want to propagate modified versions of the Program under the name "Z-Push",
00032 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00033 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00034 *
00035 * This program is distributed in the hope that it will be useful,
00036 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00037 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00038 * GNU Affero General Public License for more details.
00039 *
00040 * You should have received a copy of the GNU Affero General Public License
00041 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00042 *
00043 * Consult LICENSE file for details
00044 ************************************************/
00045 
00046 ob_start(null, 1048576);
00047 
00048 include_once('lib/exceptions/exceptions.php');
00049 include_once('lib/utils/utils.php');
00050 include_once('lib/utils/compat.php');
00051 include_once('lib/utils/timezoneutil.php');
00052 include_once('lib/core/zpushdefs.php');
00053 include_once('lib/core/stateobject.php');
00054 include_once('lib/core/interprocessdata.php');
00055 include_once('lib/core/pingtracking.php');
00056 include_once('lib/core/topcollector.php');
00057 include_once('lib/core/loopdetection.php');
00058 include_once('lib/core/asdevice.php');
00059 include_once('lib/core/statemanager.php');
00060 include_once('lib/core/devicemanager.php');
00061 include_once('lib/core/zpush.php');
00062 include_once('lib/core/zlog.php');
00063 include_once('lib/interface/ibackend.php');
00064 include_once('lib/interface/ichanges.php');
00065 include_once('lib/interface/iexportchanges.php');
00066 include_once('lib/interface/iimportchanges.php');
00067 include_once('lib/interface/isearchprovider.php');
00068 include_once('lib/interface/istatemachine.php');
00069 include_once('lib/core/streamer.php');
00070 include_once('lib/core/streamimporter.php');
00071 include_once('lib/core/synccollections.php');
00072 include_once('lib/core/hierarchycache.php');
00073 include_once('lib/core/changesmemorywrapper.php');
00074 include_once('lib/core/syncparameters.php');
00075 include_once('lib/core/bodypreference.php');
00076 include_once('lib/core/contentparameters.php');
00077 include_once('lib/wbxml/wbxmldefs.php');
00078 include_once('lib/wbxml/wbxmldecoder.php');
00079 include_once('lib/wbxml/wbxmlencoder.php');
00080 include_once('lib/syncobjects/syncobject.php');
00081 include_once('lib/syncobjects/syncbasebody.php');
00082 include_once('lib/syncobjects/syncbaseattachment.php');
00083 include_once('lib/syncobjects/syncmailflags.php');
00084 include_once('lib/syncobjects/syncrecurrence.php');
00085 include_once('lib/syncobjects/syncappointment.php');
00086 include_once('lib/syncobjects/syncappointmentexception.php');
00087 include_once('lib/syncobjects/syncattachment.php');
00088 include_once('lib/syncobjects/syncattendee.php');
00089 include_once('lib/syncobjects/syncmeetingrequestrecurrence.php');
00090 include_once('lib/syncobjects/syncmeetingrequest.php');
00091 include_once('lib/syncobjects/syncmail.php');
00092 include_once('lib/syncobjects/syncnote.php');
00093 include_once('lib/syncobjects/synccontact.php');
00094 include_once('lib/syncobjects/syncfolder.php');
00095 include_once('lib/syncobjects/syncprovisioning.php');
00096 include_once('lib/syncobjects/synctaskrecurrence.php');
00097 include_once('lib/syncobjects/synctask.php');
00098 include_once('lib/syncobjects/syncoofmessage.php');
00099 include_once('lib/syncobjects/syncoof.php');
00100 include_once('lib/syncobjects/syncuserinformation.php');
00101 include_once('lib/syncobjects/syncdeviceinformation.php');
00102 include_once('lib/syncobjects/syncdevicepassword.php');
00103 include_once('lib/syncobjects/syncitemoperationsattachment.php');
00104 include_once('lib/syncobjects/syncsendmail.php');
00105 include_once('lib/syncobjects/syncsendmailsource.php');
00106 include_once('lib/default/backend.php');
00107 include_once('lib/default/searchprovider.php');
00108 include_once('lib/request/request.php');
00109 include_once('lib/request/requestprocessor.php');
00110 
00111 include_once('config.php');
00112 include_once('version.php');
00113 
00114 
00115     // Attempt to set maximum execution time
00116     ini_set('max_execution_time', SCRIPT_TIMEOUT);
00117     set_time_limit(SCRIPT_TIMEOUT);
00118 
00119     try {
00120         // check config & initialize the basics
00121         ZPush::CheckConfig();
00122         Request::Initialize();
00123         ZLog::Initialize();
00124 
00125         ZLog::Write(LOGLEVEL_DEBUG,"-------- Start");
00126         ZLog::Write(LOGLEVEL_INFO,
00127                     sprintf("Version='%s' method='%s' from='%s' cmd='%s' getUser='%s' devId='%s' devType='%s'",
00128                                     @constant('ZPUSH_VERSION'), Request::GetMethod(), Request::GetRemoteAddr(),
00129                                     Request::GetCommand(), Request::GetGETUser(), Request::GetDeviceID(), Request::GetDeviceType()));
00130 
00131         // Stop here if this is an OPTIONS request
00132         if (Request::IsMethodOPTIONS())
00133             throw new NoPostRequestException("Options request", NoPostRequestException::OPTIONS_REQUEST);
00134 
00135         ZPush::CheckAdvancedConfig();
00136 
00137         // Process request headers and look for AS headers
00138         Request::ProcessHeaders();
00139 
00140         // Check required GET parameters
00141         if(Request::IsMethodPOST() && (Request::GetCommandCode() === false || !Request::GetGETUser() || !Request::GetDeviceID() || !Request::GetDeviceType()))
00142             throw new FatalException("Requested the Z-Push URL without the required GET parameters");
00143 
00144         // Load the backend
00145         $backend = ZPush::GetBackend();
00146 
00147         // always request the authorization header
00148         if (! Request::AuthenticationInfo())
00149             throw new AuthenticationRequiredException("Access denied. Please send authorisation information");
00150 
00151         // check the provisioning information
00152         if (PROVISIONING === true && Request::IsMethodPOST() && ZPush::CommandNeedsProvisioning(Request::GetCommandCode()) &&
00153             ((Request::WasPolicyKeySent() && Request::GetPolicyKey() == 0) || ZPush::GetDeviceManager()->ProvisioningRequired(Request::GetPolicyKey())) &&
00154             (LOOSE_PROVISIONING === false ||
00155             (LOOSE_PROVISIONING === true && Request::WasPolicyKeySent())))
00156             //TODO for AS 14 send a wbxml response
00157             throw new ProvisioningRequiredException();
00158 
00159         // most commands require an authenticated user
00160         if (ZPush::CommandNeedsAuthentication(Request::GetCommandCode()))
00161             RequestProcessor::Authenticate();
00162 
00163         // Do the actual processing of the request
00164         if (Request::IsMethodGET())
00165             throw new NoPostRequestException("This is the Z-Push location and can only be accessed by Microsoft ActiveSync-capable devices", NoPostRequestException::GET_REQUEST);
00166 
00167         // Do the actual request
00168         header(ZPush::GetServerHeader());
00169 
00170         // announce the supported AS versions (if not already sent to device)
00171         if (ZPush::GetDeviceManager()->AnnounceASVersion()) {
00172             $versions = ZPush::GetSupportedProtocolVersions(true);
00173             ZLog::Write(LOGLEVEL_INFO, sprintf("Announcing latest AS version to device: %s", $versions));
00174             header("X-MS-RP: ". $versions);
00175         }
00176 
00177         RequestProcessor::Initialize();
00178         if(!RequestProcessor::HandleRequest())
00179             throw new WBXMLException(ZLog::GetWBXMLDebugInfo());
00180 
00181         // stream the data
00182         $len = ob_get_length();
00183         $data = ob_get_contents();
00184         ob_end_clean();
00185 
00186         // log amount of data transferred
00187         // TODO check $len when streaming more data (e.g. Attachments), as the data will be send chunked
00188         ZPush::GetDeviceManager()->SentData($len);
00189 
00190         // Unfortunately, even though Z-Push can stream the data to the client
00191         // with a chunked encoding, using chunked encoding breaks the progress bar
00192         // on the PDA. So the data is de-chunk here, written a content-length header and
00193         // data send as a 'normal' packet. If the output packet exceeds 1MB (see ob_start)
00194         // then it will be sent as a chunked packet anyway because PHP will have to flush
00195         // the buffer.
00196         if(!headers_sent())
00197             header("Content-Length: $len");
00198 
00199         // send vnd.ms-sync.wbxml content type header if there is no content
00200         // otherwise text/html content type is added which might break some devices
00201         if ($len == 0)
00202             header("Content-Type: application/vnd.ms-sync.wbxml");
00203 
00204         print $data;
00205 
00206         // destruct backend after all data is on the stream
00207         $backend->Logoff();
00208     }
00209 
00210     catch (NoPostRequestException $nopostex) {
00211         if ($nopostex->getCode() == NoPostRequestException::OPTIONS_REQUEST) {
00212             header(ZPush::GetServerHeader());
00213             header(ZPush::GetSupportedProtocolVersions());
00214             header(ZPush::GetSupportedCommands());
00215             ZLog::Write(LOGLEVEL_INFO, $nopostex->getMessage());
00216         }
00217         else if ($nopostex->getCode() == NoPostRequestException::GET_REQUEST) {
00218             if (Request::GetUserAgent())
00219                 ZLog::Write(LOGLEVEL_INFO, sprintf("User-agent: '%s'", Request::GetUserAgent()));
00220             if (!headers_sent() && $nopostex->showLegalNotice())
00221                 ZPush::PrintZPushLegal('GET not supported', $nopostex->getMessage());
00222         }
00223     }
00224 
00225     catch (Exception $ex) {
00226         if (Request::GetUserAgent())
00227             ZLog::Write(LOGLEVEL_INFO, sprintf("User-agent: '%s'", Request::GetUserAgent()));
00228         $exclass = get_class($ex);
00229 
00230         if(!headers_sent()) {
00231             if ($ex instanceof ZPushException) {
00232                 header('HTTP/1.1 '. $ex->getHTTPCodeString());
00233                 foreach ($ex->getHTTPHeaders() as $h)
00234                     header($h);
00235             }
00236             // something really unexpected happened!
00237             else
00238                 header('HTTP/1.1 500 Internal Server Error');
00239         }
00240         else
00241             ZLog::Write(LOGLEVEL_FATAL, "Exception: ($exclass) - headers were already sent. Message: ". $ex->getMessage());
00242 
00243         if ($ex instanceof AuthenticationRequiredException) {
00244             ZPush::PrintZPushLegal($exclass, sprintf('<pre>%s</pre>',$ex->getMessage()));
00245 
00246             // log the failed login attemt e.g. for fail2ban
00247             if (defined('LOGAUTHFAIL') && LOGAUTHFAIL != false)
00248                 ZLog::Write(LOGLEVEL_WARN, sprintf("IP: %s failed to authenticate user '%s'",  Request::GetRemoteAddr(), Request::GetAuthUser()? Request::GetAuthUser(): Request::GetGETUser() ));
00249         }
00250 
00251         // This could be a WBXML problem.. try to get the complete request
00252         else if ($ex instanceof WBXMLException) {
00253             ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this.");
00254         }
00255 
00256         // Try to output some kind of error information. This is only possible if
00257         // the output had not started yet. If it has started already, we can't show the user the error, and
00258         // the device will give its own (useless) error message.
00259         else if (!($ex instanceof ZPushException) || $ex->showLegalNotice()) {
00260             $cmdinfo = (Request::GetCommand())? sprintf(" processing command <i>%s</i>", Request::GetCommand()): "";
00261             $extrace = $ex->getTrace();
00262             $trace = (!empty($extrace))? "\n\nTrace:\n". print_r($extrace,1):"";
00263             ZPush::PrintZPushLegal($exclass . $cmdinfo, sprintf('<pre>%s</pre>',$ex->getMessage() . $trace));
00264         }
00265 
00266         // Announce exception to process loop detection
00267         if (ZPush::GetDeviceManager(false))
00268             ZPush::GetDeviceManager()->AnnounceProcessException($ex);
00269 
00270         // Announce exception if the TopCollector if available
00271         ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true);
00272     }
00273 
00274     // save device data if the DeviceManager is available
00275     if (ZPush::GetDeviceManager(false))
00276         ZPush::GetDeviceManager()->Save();
00277 
00278     // end gracefully
00279     ZLog::Write(LOGLEVEL_DEBUG, '-------- End');
00280 ?>