/**
  * Handles a webservice command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  * @throws SoapFault
  */
 public function Handle($commandCode)
 {
     if (Request::GetDeviceType() !== "webservice" || Request::GetDeviceID() !== "webservice") {
         throw new FatalException("Invalid device id and type for webservice execution");
     }
     if (Request::GetGETUser() != Request::GetAuthUser()) {
         ZLog::Write(LOGLEVEL_INFO, sprintf("Webservice::HandleWebservice('%s'): user '%s' executing action for user '%s'", $commandCode, Request::GetAuthUser(), Request::GetGETUser()));
     }
     // initialize non-wsdl soap server
     $this->server = new SoapServer(null, array('uri' => "http://z-push.sf.net/webservice"));
     // the webservice command is handled by its class
     if ($commandCode == ZPush::COMMAND_WEBSERVICE_DEVICE) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceDevice service", $commandCode));
         include_once 'webservicedevice.php';
         $this->server->setClass("WebserviceDevice");
     }
     // the webservice command is handled by its class
     if ($commandCode == ZPush::COMMAND_WEBSERVICE_USERS) {
         if (!defined("ALLOW_WEBSERVICE_USERS_ACCESS") || ALLOW_WEBSERVICE_USERS_ACCESS !== true) {
             throw new HTTPReturnCodeException(sprintf("Access to the WebserviceUsers service is disabled in configuration. Enable setting ALLOW_WEBSERVICE_USERS_ACCESS.", Request::GetAuthUser()), 403);
         }
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceUsers service", $commandCode));
         if (ZPush::GetBackend()->Setup("SYSTEM", true) == false) {
             throw new AuthenticationRequiredException(sprintf("User '%s' has no admin privileges", Request::GetAuthUser()));
         }
         include_once 'webserviceusers.php';
         $this->server->setClass("WebserviceUsers");
     }
     $this->server->handle();
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): sucessfully sent %d bytes", $commandCode, ob_get_length()));
     return true;
 }
Example #2
0
 /**
  * Constructor
  *
  * @access public
  */
 public function __construct()
 {
     $this->statemachine = ZPush::GetStateMachine();
     $this->deviceHash = false;
     $this->devid = Request::GetDeviceID();
     $this->saveDevice = true;
     $this->windowSize = array();
     $this->latestFolder = false;
     $this->hierarchySyncRequired = false;
     // only continue if deviceid is set
     if ($this->devid) {
         $this->device = new ASDevice($this->devid, Request::GetDeviceType(), Request::GetGETUser(), Request::GetUserAgent());
         $this->loadDeviceData();
         ZPush::GetTopCollector()->SetUserAgent($this->device->GetDeviceUserAgent());
     } else {
         throw new FatalNotImplementedException("Can not proceed without a device id.");
     }
     $this->loopdetection = new LoopDetection();
     $this->loopdetection->ProcessLoopDetectionInit();
     $this->loopdetection->ProcessLoopDetectionPreviousConnectionFailed();
     $this->stateManager = new StateManager();
     $this->stateManager->SetDevice($this->device);
     $this->additionalFoldersHash = $this->getAdditionalFoldersHash();
     if ($this->IsKoe() && $this->device->GetKoeVersion() !== false) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("KOE: %s / %s / %s", $this->device->GetKoeVersion(), $this->device->GetKoeBuild(), strftime("%Y-%m-%d %H:%M", $this->device->GetKoeBuildDate())));
     }
 }
Example #3
0
 /**
  * Initializes the logging
  *
  * @access public
  * @return boolean
  */
 public static function Initialize()
 {
     global $specialLogUsers;
     // define some constants for the logging
     if (!defined('LOGUSERLEVEL')) {
         define('LOGUSERLEVEL', LOGLEVEL_OFF);
     }
     if (!defined('LOGLEVEL')) {
         define('LOGLEVEL', LOGLEVEL_OFF);
     }
     list($user, ) = Utils::SplitDomainUser(Request::GetGETUser());
     if (!defined('WBXML_DEBUG') && $user) {
         // define the WBXML_DEBUG mode on user basis depending on the configurations
         if (LOGLEVEL >= LOGLEVEL_WBXML || LOGUSERLEVEL >= LOGLEVEL_WBXML && in_array($user, $specialLogUsers)) {
             define('WBXML_DEBUG', true);
         } else {
             define('WBXML_DEBUG', false);
         }
     }
     if ($user) {
         self::$user = '[' . $user . '] ';
     } else {
         self::$user = '';
     }
     // log the device id if the global loglevel is set to log devid or the user is in  and has the right log level
     if (Request::GetDeviceID() != "" && (LOGLEVEL >= LOGLEVEL_DEVICEID || LOGUSERLEVEL >= LOGLEVEL_DEVICEID && in_array($user, $specialLogUsers))) {
         self::$devid = '[' . Request::GetDeviceID() . '] ';
     } else {
         self::$devid = '';
     }
     return true;
 }
 /**
  * Initializes internal parameters
  *
  * @access public
  * @return boolean
  */
 public function InitializeParams()
 {
     if (!isset(self::$devid)) {
         self::$devid = Request::GetDeviceID();
         self::$pid = @getmypid();
         self::$user = Request::GetAuthUser();
         self::$start = time();
     }
     return true;
 }
 /**
  * Constructor
  *
  * Performs some basic checks and initilizes the state directory
  *
  * @access public
  * @throws FatalMisconfigurationException
  */
 public function FileStateMachine()
 {
     if (!defined('STATE_DIR')) {
         throw new FatalMisconfigurationException("No configuration for the state directory available.");
     }
     if (substr(STATE_DIR, -1, 1) != "/") {
         throw new FatalMisconfigurationException("The configured state directory should terminate with a '/'");
     }
     if (!file_exists(STATE_DIR)) {
         throw new FatalMisconfigurationException("The configured state directory does not exist or can not be accessed: " . STATE_DIR);
     }
     // checks if the directory exists and tries to create the necessary subfolders if they do not exist
     $this->getDirectoryForDevice(Request::GetDeviceID());
     $this->userfilename = STATE_DIR . 'users';
     if (!touch($this->userfilename)) {
         throw new FatalMisconfigurationException("Not possible to write to the configured state directory.");
     }
 }
Example #6
0
 /**
  * Handles a webservice command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  * @throws SoapFault
  */
 public function Handle($commandCode)
 {
     if (Request::GetDeviceType() !== "webservice" || Request::GetDeviceID() !== "webservice") {
         throw new FatalException("Invalid device id and type for webservice execution");
     }
     if (Request::GetGETUser() != Request::GetAuthUser()) {
         ZLog::Write(LOGLEVEL_INFO, sprintf("Webservice::HandleWebservice('%s'): user '%s' executing action for user '%s'", $commandCode, Request::GetAuthUser(), Request::GetGETUser()));
     }
     // initialize non-wsdl soap server
     $this->server = new SoapServer(null, array('uri' => "http://z-push.sf.net/webservice"));
     // the webservice command is handled by its class
     if ($commandCode == ZPush::COMMAND_WEBSERVICE_DEVICE) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceDevice service", $commandCode));
         include_once 'webservicedevice.php';
         $this->server->setClass("WebserviceDevice");
     }
     $this->server->handle();
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): sucessfully sent %d bytes", $commandCode, ob_get_length()));
     return true;
 }
 /**
  * Constructor
  *
  * @access public
  */
 public function DeviceManager()
 {
     $this->statemachine = ZPush::GetStateMachine();
     $this->deviceHash = false;
     $this->devid = Request::GetDeviceID();
     $this->windowSize = array();
     $this->latestFolder = false;
     $this->hierarchySyncRequired = false;
     // only continue if deviceid is set
     if ($this->devid) {
         $this->device = new ASDevice($this->devid, Request::GetDeviceType(), Request::GetGETUser(), Request::GetUserAgent());
         $this->loadDeviceData();
         ZPush::GetTopCollector()->SetUserAgent($this->device->GetDeviceUserAgent());
     } else {
         throw new FatalNotImplementedException("Can not proceed without a device id.");
     }
     $this->loopdetection = new LoopDetection();
     $this->loopdetection->ProcessLoopDetectionInit();
     $this->loopdetection->ProcessLoopDetectionPreviousConnectionFailed();
     $this->stateManager = new StateManager();
     $this->stateManager->SetDevice($this->device);
 }
Example #8
0
try {
    // check config & initialize the basics
    ZPush::CheckConfig();
    Request::Initialize();
    ZLog::Initialize();
    ZLog::Write(LOGLEVEL_DEBUG, "-------- Start");
    ZLog::Write(LOGLEVEL_INFO, sprintf("Version='%s' method='%s' from='%s' cmd='%s' getUser='%s' devId='%s' devType='%s'", @constant('ZPUSH_VERSION'), Request::GetMethod(), Request::GetRemoteAddr(), Request::GetCommand(), Request::GetGETUser(), Request::GetDeviceID(), Request::GetDeviceType()));
    // Stop here if this is an OPTIONS request
    if (Request::IsMethodOPTIONS()) {
        throw new NoPostRequestException("Options request", NoPostRequestException::OPTIONS_REQUEST);
    }
    ZPush::CheckAdvancedConfig();
    // Process request headers and look for AS headers
    Request::ProcessHeaders();
    // Check required GET parameters
    if (Request::IsMethodPOST() && (Request::GetCommandCode() === false || !Request::GetGETUser() || !Request::GetDeviceID() || !Request::GetDeviceType())) {
        throw new FatalException("Requested the Z-Push URL without the required GET parameters");
    }
    // Load the backend
    $backend = ZPush::GetBackend();
    // always request the authorization header
    if (!Request::AuthenticationInfo()) {
        throw new AuthenticationRequiredException("Access denied. Please send authorisation information");
    }
    // check the provisioning information
    if (PROVISIONING === true && Request::IsMethodPOST() && ZPush::CommandNeedsProvisioning(Request::GetCommandCode()) && (Request::WasPolicyKeySent() && Request::GetPolicyKey() == 0 || ZPush::GetDeviceManager()->ProvisioningRequired(Request::GetPolicyKey())) && (LOOSE_PROVISIONING === false || LOOSE_PROVISIONING === true && Request::WasPolicyKeySent())) {
        //TODO for AS 14 send a wbxml response
        throw new ProvisioningRequiredException();
    }
    // most commands require an authenticated user
    if (ZPush::CommandNeedsAuthentication(Request::GetCommandCode())) {
Example #9
0
 /**
  * Handles the FolderSync command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Maps serverid -> clientid for items that are received from the PIM
     $map = array();
     // Parse input
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
         return false;
     }
     $synckey = self::$decoder->getElementContent();
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     $status = SYNC_FSSTATUS_SUCCESS;
     $newsynckey = $synckey;
     try {
         $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
         // We will be saving the sync state under 'newsynckey'
         $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
     } catch (StateNotFoundException $snfex) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     } catch (StateInvalidException $sive) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     }
     // The ChangesWrapper caches all imports in-memory, so we can send a change count
     // before sending the actual data.
     // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend
     $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
     // the hierarchyCache should now fully be initialized - check for changes in the additional folders
     $changesMem->Config(ZPush::GetAdditionalSyncFolders());
     // process incoming changes
     if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
         // Ignore <Count> if present
         if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
             self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Process the changes (either <Add>, <Modify>, or <Remove>)
         $element = self::$decoder->getElement();
         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
             return false;
         }
         $importer = false;
         while (1) {
             $folder = new SyncFolder();
             if (!$folder->Decode(self::$decoder)) {
                 break;
             }
             try {
                 if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) {
                     // Configure the backends importer with last state
                     $importer = self::$backend->GetImporter();
                     $importer->Config($syncstate);
                     // the messages from the PIM will be forwarded to the backend
                     $changesMem->forwardImporter($importer);
                 }
                 if ($status == SYNC_FSSTATUS_SUCCESS) {
                     switch ($element[EN_TAG]) {
                         case SYNC_ADD:
                         case SYNC_MODIFY:
                             $serverid = $changesMem->ImportFolderChange($folder);
                             break;
                         case SYNC_REMOVE:
                             $serverid = $changesMem->ImportFolderDeletion($folder);
                             break;
                     }
                     // TODO what does $map??
                     if ($serverid) {
                         $map[$serverid] = $folder->clientid;
                     }
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname));
                     self::$topCollector->AnnounceInformation("Incoming change ignored", true);
                 }
             } catch (StatusException $stex) {
                 $status = $stex->getCode();
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
     } else {
         // check for a potential process loop like described in Issue ZP-5
         if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired()) {
             $status = SYNC_FSSTATUS_SYNCKEYERROR;
         }
         self::$deviceManager->AnnounceProcessStatus(false, $status);
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     // We have processed incoming foldersync requests, now send the PIM
     // our changes
     // Output our WBXML reply now
     self::$encoder->StartWBXML();
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         try {
             // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends)
             if (!Request::IsValidDeviceID()) {
                 throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR);
             }
             // Changes from backend are sent to the MemImporter and processed for the HierarchyCache.
             // The state which is saved is from the backend, as the MemImporter is only a proxy.
             $exporter = self::$backend->GetExporter();
             $exporter->Config($syncstate);
             $exporter->InitializeExporter($changesMem);
             // Stream all changes to the ImportExportChangesMem
             while (is_array($exporter->Synchronize())) {
             }
             // get the new state from the backend
             $newsyncstate = isset($exporter) ? $exporter->GetState() : "";
         } catch (StatusException $stex) {
             if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN) {
                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
             } else {
                 $status = $stex->getCode();
             }
         }
     }
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
     self::$encoder->content($status);
     self::$encoder->endTag();
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         $synckey = $changesMem->IsStateChanged() ? $newsynckey : $synckey;
         self::$encoder->content($synckey);
         self::$encoder->endTag();
         // Stream folders directly to the PDA
         $streamimporter = new ImportChangesStream(self::$encoder, false);
         $changesMem->InitializeExporter($streamimporter);
         $changeCount = $changesMem->GetChangeCount();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
         self::$encoder->content($changeCount);
         self::$encoder->endTag();
         while ($changesMem->Synchronize()) {
         }
         self::$encoder->endTag();
         self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders", $changeCount), true);
         // everything fine, save the sync state for the next time
         if ($synckey == $newsynckey) {
             self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate);
         }
     }
     self::$encoder->endTag();
     return true;
 }
Example #10
0
 /**
  * Imports a move of a message. This occurs when a user moves an item to another folder
  *
  * @param string        $id
  * @param string        $newfolder
  *
  * @access public
  * @return boolean
  */
 public function ImportMessageMove($id, $newfolder)
 {
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove('%s', '%s')", $id, $newfolder));
     if (!$this->icc) {
         ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageMove icc not configured");
         return false;
     }
     if ($this->backend->GetBackendId($this->folderid) != $this->backend->GetBackendId($newfolder)) {
         ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportMessageMove() cannot move message between two backends");
         return false;
     }
     $res = $this->icc->ImportMessageMove($id, $this->backend->GetBackendFolder($newfolder));
     if ($res) {
         //TODO: we should add newid to new folder, instead of a full folder resync
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove(): Force resync of dest folder (%s)", $newfolder));
         ZPushAdmin::ResyncFolder(Request::GetAuthUser(), Request::GetDeviceID(), $newfolder);
     }
     return $res;
 }
Example #11
0
        ZPush::PrintZPushLegal($exclass, sprintf('<pre>%s</pre>', $ex->getMessage()));
        // log the failed login attemt e.g. for fail2ban
        if (defined('LOGAUTHFAIL') && LOGAUTHFAIL != false) {
            ZLog::Write(LOGLEVEL_WARN, sprintf("IP: %s failed to authenticate user '%s'", Request::GetRemoteAddr(), Request::GetAuthUser() ? Request::GetAuthUser() : Request::GetGETUser()));
        }
    } else {
        if ($ex instanceof WBXMLException) {
            ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this including the 'WBXML debug data' logged. Be aware that the debug data could contain confidential information.");
        } else {
            if (!$ex instanceof ZPushException || $ex->showLegalNotice()) {
                $cmdinfo = Request::GetCommand() ? sprintf(" processing command <i>%s</i>", Request::GetCommand()) : "";
                $extrace = $ex->getTrace();
                $trace = !empty($extrace) ? "\n\nTrace:\n" . print_r($extrace, 1) : "";
                ZPush::PrintZPushLegal($exclass . $cmdinfo, sprintf('<pre>%s</pre>', $ex->getMessage() . $trace));
            }
        }
    }
    // Announce exception to process loop detection
    if (ZPush::GetDeviceManager(false)) {
        ZPush::GetDeviceManager()->AnnounceProcessException($ex);
    }
    // Announce exception if the TopCollector if available
    ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true);
}
// save device data if the DeviceManager is available
if (ZPush::GetDeviceManager(false)) {
    ZPush::GetDeviceManager()->Save();
}
// end gracefully
ZLog::Write(LOGLEVEL_INFO, sprintf("cmd='%s' memory='%s/%s' time='%ss' devType='%s' devId='%s' getUser='%s' from='%s' version='%s' method='%s' httpcode='%s'", Request::GetCommand(), Utils::FormatBytes(memory_get_peak_usage(false)), Utils::FormatBytes(memory_get_peak_usage(true)), number_format(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2), Request::GetDeviceType(), Request::GetDeviceID(), Request::GetGETUser(), Request::GetRemoteAddr(), @constant('ZPUSH_VERSION'), Request::GetMethod(), http_response_code()));
ZLog::Write(LOGLEVEL_DEBUG, "-------- End");
Example #12
0
     } else {
         throw new NoPostRequestException("Options request", NoPostRequestException::OPTIONS_REQUEST);
     }
 }
 ZPush::CheckAdvancedConfig();
 // Process request headers and look for AS headers
 Request::ProcessHeaders();
 // Check required GET parameters
 if (Request::IsMethodPOST() && (Request::GetCommandCode() === false || !Request::GetDeviceID() || !Request::GetDeviceType())) {
     throw new FatalException("Requested the Z-Push URL without the required GET parameters");
 }
 // This won't be useful with Zarafa, but it will be with standalone Z-Push
 if (defined('PRE_AUTHORIZE_USERS') && PRE_AUTHORIZE_USERS === true) {
     if (!Request::IsMethodGET()) {
         // Check if User/Device are authorized
         if (ZPush::GetDeviceManager()->GetUserDevicePermission($GETUser, Request::GetDeviceID()) != SYNC_COMMONSTATUS_SUCCESS) {
             throw new AuthenticationRequiredException("Access denied. Username and Device not authorized");
         }
     }
 }
 // Load the backend
 $backend = ZPush::GetBackend();
 // always request the authorization header
 if (!$autenticationInfo || !$GETUser) {
     throw new AuthenticationRequiredException("Access denied. Please send authorisation information");
 }
 // check the provisioning information
 if (PROVISIONING === true && Request::IsMethodPOST() && ZPush::CommandNeedsProvisioning(Request::GetCommandCode()) && (Request::WasPolicyKeySent() && Request::GetPolicyKey() == 0 || ZPush::GetDeviceManager()->ProvisioningRequired(Request::GetPolicyKey())) && (LOOSE_PROVISIONING === false || LOOSE_PROVISIONING === true && Request::WasPolicyKeySent())) {
     //TODO for AS 14 send a wbxml response
     throw new ProvisioningRequiredException();
 }
Example #13
0
 /**
  * Sends an email notification to the user containing the data the user tried to save.
  *
  * @param SyncObject $message
  * @param SyncObject $oldmessage
  * @return void
  */
 private function sendNotificationEmail($message, $oldmessage)
 {
     // get email address and full name of the user
     $userinfo = ZPush::GetBackend()->GetUserDetails(Request::GetAuthUser());
     // get the name of the folder
     $foldername = "unknown";
     $folderid = bin2hex($this->folderid);
     $folders = ZPush::GetAdditionalSyncFolders();
     if (isset($folders[$folderid]) && isset($folders[$folderid]->displayname)) {
         $foldername = $folders[$folderid]->displayname;
     }
     // get the differences between the two objects
     $data = substr(get_class($oldmessage), 4) . "\r\n";
     // get the suppported fields as we need them to determine the ghosted properties
     $supportedFields = ZPush::GetDeviceManager()->GetSupportedFields(ZPush::GetDeviceManager()->GetFolderIdForBackendId($folderid));
     $dataarray = $oldmessage->EvaluateAndCompare($message, @constant('READ_ONLY_NOTIFY_YOURDATA'), $supportedFields);
     foreach ($dataarray as $key => $value) {
         $value = str_replace("\r", "", $value);
         $value = str_replace("\n", str_pad("\r\n", 25), $value);
         $data .= str_pad(ucfirst($key) . ":", 25) . $value . "\r\n";
     }
     // build a simple mime message
     $toEmail = $userinfo['emailaddress'];
     $mail = "From: Z-Push <no-reply>\r\n";
     $mail .= "To: {$toEmail}\r\n";
     $mail .= "Content-Type: text/plain; charset=utf-8\r\n";
     $mail .= "Subject: " . @constant('READ_ONLY_NOTIFY_SUBJECT') . "\r\n\r\n";
     $mail .= @constant('READ_ONLY_NOTIFY_BODY') . "\r\n";
     // replace values of template
     $mail = str_replace("**USERFULLNAME**", $userinfo['fullname'], $mail);
     $mail = str_replace("**DATE**", strftime(@constant('READ_ONLY_NOTIFY_DATE_FORMAT')), $mail);
     $mail = str_replace("**TIME**", strftime(@constant('READ_ONLY_NOTIFY_TIME_FORMAT')), $mail);
     $mail = str_replace("**FOLDERNAME**", $foldername, $mail);
     $mail = str_replace("**MOBILETYPE**", Request::GetDeviceType(), $mail);
     $mail = str_replace("**MOBILEDEVICEID**", Request::GetDeviceID(), $mail);
     $mail = str_replace("**DIFFERENCES**", $data, $mail);
     // user send email to himself
     $m = new SyncSendMail();
     $m->saveinsent = false;
     $m->replacemime = true;
     $m->mime = $mail;
     ZPush::GetBackend()->SendMail($m);
 }
Example #14
0
 /**
  * Handles the FolderSync command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Parse input
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
         return false;
     }
     $synckey = self::$decoder->getElementContent();
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     // every FolderSync with SyncKey 0 should return the supported AS version & command headers
     if ($synckey == "0") {
         self::$specialHeaders = array();
         self::$specialHeaders[] = ZPush::GetSupportedProtocolVersions();
         self::$specialHeaders[] = ZPush::GetSupportedCommands();
     }
     $status = SYNC_FSSTATUS_SUCCESS;
     $newsynckey = $synckey;
     try {
         $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
         // We will be saving the sync state under 'newsynckey'
         $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
     } catch (StateNotFoundException $snfex) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     } catch (StateInvalidException $sive) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     }
     // The ChangesWrapper caches all imports in-memory, so we can send a change count
     // before sending the actual data.
     // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend
     $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
     // the hierarchyCache should now fully be initialized - check for changes in the additional folders
     $changesMem->Config(ZPush::GetAdditionalSyncFolders());
     // process incoming changes
     if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
         // Ignore <Count> if present
         if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
             self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Process the changes (either <Add>, <Modify>, or <Remove>)
         $element = self::$decoder->getElement();
         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
             return false;
         }
         $importer = false;
         while (1) {
             $folder = new SyncFolder();
             if (!$folder->Decode(self::$decoder)) {
                 break;
             }
             try {
                 if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) {
                     // Configure the backends importer with last state
                     $importer = self::$backend->GetImporter();
                     $importer->Config($syncstate);
                     // the messages from the PIM will be forwarded to the backend
                     $changesMem->forwardImporter($importer);
                 }
                 if ($status == SYNC_FSSTATUS_SUCCESS) {
                     switch ($element[EN_TAG]) {
                         case SYNC_ADD:
                         case SYNC_MODIFY:
                             $serverid = $changesMem->ImportFolderChange($folder);
                             break;
                         case SYNC_REMOVE:
                             $serverid = $changesMem->ImportFolderDeletion($folder);
                             break;
                     }
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname));
                     self::$topCollector->AnnounceInformation("Incoming change ignored", true);
                 }
             } catch (StatusException $stex) {
                 $status = $stex->getCode();
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
     } else {
         // check for a potential process loop like described in Issue ZP-5
         if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired()) {
             $status = SYNC_FSSTATUS_SYNCKEYERROR;
         }
         self::$deviceManager->AnnounceProcessStatus(false, $status);
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     // We have processed incoming foldersync requests, now send the PIM
     // our changes
     // Output our WBXML reply now
     self::$encoder->StartWBXML();
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         try {
             // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends)
             if (!Request::IsValidDeviceID()) {
                 throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR);
             }
             // Changes from backend are sent to the MemImporter and processed for the HierarchyCache.
             // The state which is saved is from the backend, as the MemImporter is only a proxy.
             $exporter = self::$backend->GetExporter();
             $exporter->Config($syncstate);
             $exporter->InitializeExporter($changesMem);
             // Stream all changes to the ImportExportChangesMem
             $maxExporttime = Request::GetExpectedConnectionTimeout();
             $totalChanges = $exporter->GetChangeCount();
             $started = time();
             $exported = 0;
             $partial = false;
             while (is_array($exporter->Synchronize())) {
                 $exported++;
                 if (time() % 4) {
                     self::$topCollector->AnnounceInformation(sprintf("Exported %d from %d folders", $exported, $totalChanges));
                 }
                 // if partial sync is allowed, stop if this takes too long
                 if (USE_PARTIAL_FOLDERSYNC && time() - $started > $maxExporttime) {
                     ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): Exporting folders is too slow. In %d seconds only %d from %d changes were processed.", time() - $started, $exported, $totalChanges));
                     self::$topCollector->AnnounceInformation(sprintf("Partial export of %d out of %d folders", $exported, $totalChanges), true);
                     self::$deviceManager->SetFolderSyncComplete(false);
                     $partial = true;
                     break;
                 }
             }
             // update the foldersync complete flag
             if (USE_PARTIAL_FOLDERSYNC && $partial == false && self::$deviceManager->GetFolderSyncComplete() === false) {
                 // say that we are done with partial synching
                 self::$deviceManager->SetFolderSyncComplete(true);
                 // reset the loop data to prevent any loop detection to kick in now
                 self::$deviceManager->ClearLoopDetectionData(Request::GetAuthUser(), Request::GetDeviceID());
                 ZLog::Write(LOGLEVEL_INFO, "Request->HandleFolderSync(): Chunked exporting of folders completed successfully");
             }
             // get the new state from the backend
             $newsyncstate = isset($exporter) ? $exporter->GetState() : "";
         } catch (StatusException $stex) {
             if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN) {
                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
             } else {
                 $status = $stex->getCode();
             }
         }
     }
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
     self::$encoder->content($status);
     self::$encoder->endTag();
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         $synckey = $changesMem->IsStateChanged() ? $newsynckey : $synckey;
         self::$encoder->content($synckey);
         self::$encoder->endTag();
         // Stream folders directly to the PDA
         $streamimporter = new ImportChangesStream(self::$encoder, false);
         $changesMem->InitializeExporter($streamimporter);
         $changeCount = $changesMem->GetChangeCount();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
         self::$encoder->content($changeCount);
         self::$encoder->endTag();
         while ($changesMem->Synchronize()) {
         }
         self::$encoder->endTag();
         self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders", $changeCount), true);
         // everything fine, save the sync state for the next time
         if ($synckey == $newsynckey) {
             self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate);
         }
     }
     self::$encoder->endTag();
     return true;
 }
Example #15
0
 /**
  * Returns the logger object. If no logger has been initialized, FileLog will be initialized and returned.
  *
  * @access private
  * @return Log
  * @throws Exception thrown if the logger class cannot be instantiated.
  */
 private static function getLogger()
 {
     if (!self::$logger) {
         global $specialLogUsers;
         // This variable comes from the configuration file (config.php)
         $logger = LOGBACKEND_CLASS;
         if (!class_exists($logger)) {
             $errmsg = 'The configured logging class `' . $logger . '` does not exist. Check your configuration.';
             error_log($errmsg);
             throw new \Exception($errmsg);
         }
         list($user) = Utils::SplitDomainUser(strtolower(Request::GetGETUser()));
         $user = '[' . $user . ']';
         self::$logger = new $logger();
         self::$logger->SetUser($user);
         self::$logger->SetAuthUser(Request::GetAuthUser());
         self::$logger->SetSpecialLogUsers($specialLogUsers);
         self::$logger->SetDevid('[' . Request::GetDeviceID() . ']');
         self::$logger->SetPidstr('[' . str_pad(@getmypid(), 5, " ", STR_PAD_LEFT) . ']');
         self::$logger->AfterInitialize();
     }
     return self::$logger;
 }
Example #16
0
 /**
  * Authenticates the user with the configured Kopano server
  *
  * @param string        $username
  * @param string        $domain
  * @param string        $password
  *
  * @access public
  * @return boolean
  * @throws AuthenticationRequiredException
  */
 public function Logon($user, $domain, $pass)
 {
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("KopanoBackend->Logon(): Trying to authenticate user '%s'..", $user));
     $this->mainUser = strtolower($user);
     try {
         // check if notifications are available in php-mapi
         if (function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) {
             // send Z-Push version and user agent to ZCP - ZP-589
             if (Utils::CheckMapiExtVersion('7.2.0')) {
                 $zpush_version = 'Z-Push_' . @constant('ZPUSH_VERSION');
                 $user_agent = Request::GetDeviceID() ? ZPush::GetDeviceManager()->GetUserAgent() : "unknown";
                 $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent);
             } else {
                 $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0);
             }
             $this->notifications = true;
         } else {
             $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER);
             $this->notifications = false;
         }
         if (mapi_last_hresult()) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("KopanoBackend->Logon(): login failed with error code: 0x%X", mapi_last_hresult()));
             if (mapi_last_hresult() == MAPI_E_NETWORK_ERROR) {
                 throw new HTTPReturnCodeException("Error connecting to KC (login)", 503, null, LOGLEVEL_INFO);
             }
         }
     } catch (MAPIException $ex) {
         throw new AuthenticationRequiredException($ex->getDisplayMessage());
     }
     if (!$this->session) {
         ZLog::Write(LOGLEVEL_WARN, sprintf("KopanoBackend->Logon(): logon failed for user '%s'", $user));
         $this->defaultstore = false;
         return false;
     }
     // Get/open default store
     $this->defaultstore = $this->openMessageStore($this->mainUser);
     if (mapi_last_hresult() == MAPI_E_FAILONEPROVIDER) {
         throw new HTTPReturnCodeException("Error connecting to KC (open store)", 503, null, LOGLEVEL_INFO);
     }
     if ($this->defaultstore === false) {
         throw new AuthenticationRequiredException(sprintf("KopanoBackend->Logon(): User '%s' has no default store", $user));
     }
     $this->store = $this->defaultstore;
     $this->storeName = $this->mainUser;
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("KopanoBackend->Logon(): User '%s' is authenticated", $user));
     $this->isZPushEnabled();
     // check if this is a Zarafa 7 store with unicode support
     MAPIUtils::IsUnicodeStore($this->store);
     return true;
 }