/** * 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; }
/** * 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; }
/** * Authenticates the remote user * The sent HTTP authentication information is used to on Backend->Logon(). * As second step the GET-User verified by Backend->Setup() for permission check * Request::GetGETUser() is usually the same as the Request::GetAuthUser(). * If the GETUser is different from the AuthUser, the AuthUser MUST HAVE admin * permissions on GETUsers data store. Only then the Setup() will be sucessfull. * This allows the user 'john' to do operations as user 'joe' if he has sufficient privileges. * * @access public * @return * @throws AuthenticationRequiredException */ public static function Authenticate() { self::$userIsAuthenticated = false; $backend = ZPush::GetBackend(); if ($backend->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword()) == false) { throw new AuthenticationRequiredException("Access denied. Username or password incorrect"); } // mark this request as "authenticated" self::$userIsAuthenticated = true; // check Auth-User's permissions on GETUser's store if ($backend->Setup(Request::GetGETUser(), true) == false) { throw new AuthenticationRequiredException(sprintf("Not enough privileges of '%s' to setup for user '%s': Permission denied", Request::GetAuthUser(), Request::GetGETUser())); } }
/** * Authenticates the remote user * The sent HTTP authentication information is used to on Backend->Logon(). * As second step the GET-User verified by Backend->Setup() for permission check * Request::GetGETUser() is usually the same as the Request::GetAuthUser(). * If the GETUser is different from the AuthUser, the AuthUser MUST HAVE admin * permissions on GETUsers data store. Only then the Setup() will be sucessfull. * This allows the user 'john' to do operations as user 'joe' if he has sufficient privileges. * * @access public * @return * @throws AuthenticationRequiredException */ public static function Authenticate() { self::$userIsAuthenticated = false; // when a certificate is sent, allow authentication only as the certificate owner if (defined("CERTIFICATE_OWNER_PARAMETER") && isset($_SERVER[CERTIFICATE_OWNER_PARAMETER]) && strtolower($_SERVER[CERTIFICATE_OWNER_PARAMETER]) != strtolower(Request::GetAuthUser())) { throw new AuthenticationRequiredException(sprintf("Access denied. Access is allowed only for the certificate owner '%s'", $_SERVER[CERTIFICATE_OWNER_PARAMETER])); } $backend = ZPush::GetBackend(); if ($backend->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword()) == false) { throw new AuthenticationRequiredException("Access denied. Username or password incorrect"); } // mark this request as "authenticated" self::$userIsAuthenticated = true; }
/** * 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; }
function update_calendar_attendee($uid, $mailto, $status) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee('%s', '%s', '%s'): Updating calendar event attendee", $uid, $mailto, $status)); $updated = false; if ($uid === false) { ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->update_calendar_attendee(): UID not found; report the full calendar object to developers"); } else { if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) { $caldav = new BackendCalDAV(); if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) { $events = $caldav->FindCalendar($uid); if (count($events) == 1) { $href = $events[0]["href"]; $etag = $events[0]["etag"]; ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): found event with href '%s' etag '%s'; updating", $href, $etag)); // Get Attendee status $old_status = ""; if (strcasecmp($old_status, $status) != 0) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Before <%s>", $events[0]["data"])); $ical = new iCalComponent(); $ical->ParseFrom($events[0]["data"]); $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", strtoupper($status), $mailto); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): After <%s>", $ical->Render())); $etag = $caldav->CreateUpdateCalendar($ical->Render(), $href, $etag); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Calendar updated with etag '%s'", $etag)); // Update new status $updated = true; } $caldav->Logoff(); } else { ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): event not found or duplicated event"); } } else { ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): Error connecting with BackendCalDAV"); } } } return $updated; }
if ($ex instanceof ZPushException) { header('HTTP/1.1 ' . $ex->getHTTPCodeString()); foreach ($ex->getHTTPHeaders() as $h) { header($h); } } else { header('HTTP/1.1 500 Internal Server Error'); } } else { ZLog::Write(LOGLEVEL_FATAL, "Exception: ({$exclass}) - headers were already sent. Message: " . $ex->getMessage()); } if ($ex instanceof AuthenticationRequiredException) { 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."); } 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)) {
/** * Returns the filename logs for a WBXML debug log user should be saved to * * @access private * @return string */ private static function logToUserFile() { global $specialLogUsers; if (self::$authUser === false) { if (RequestProcessor::isUserAuthenticated()) { $authuser = Request::GetAuthUser(); if ($authuser && in_array($authuser, $specialLogUsers)) { self::$authUser = preg_replace('/[^a-z0-9]/', '_', strtolower($authuser)); } } } return self::$authUser; }
/** * Marks a device of the Request::GetGETUser() to be remotely wiped * * @param string $deviceId the device id * * @access public * @return boolean * @throws SoapFault */ public function WipeDevice($deviceId) { $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::WipeDevice('%s'): mark device of user '%s' for remote wipe", $deviceId, Request::GetGETUser())); if (!ZPushAdmin::WipeDevice(Request::GetAuthUser(), Request::GetGETUser(), $deviceId)) { ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); } ZPush::GetTopCollector()->AnnounceInformation(sprintf("Wipe requested - device id '%s'", $deviceId), true); return true; }
/** * 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); }
/** * 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; }
public function decryptSmtpPassword() { if (!empty(\GO::session()->values['emailModule']['smtpPasswords'][$this->id])) { $decrypted = \GO\Base\Util\Crypt::decrypt(\GO::session()->values['emailModule']['smtpPasswords'][$this->id]); } else { //support for z-push without storing passwords if (empty($this->smtp_password) && method_exists('Request', 'GetAuthPassword') && Request::GetAuthUser() == $this->smtp_username) { $decrypted = Request::GetAuthPassword(); } else { $decrypted = \GO\Base\Util\Crypt::decrypt($this->smtp_password); } } return $decrypted ? $decrypted : $this->smtp_password; }
/** * Returns the username and store of the currently active user * * @access public * @return Array */ public function GetCurrentUsername() { return $this->GetUserDetails(Request::GetAuthUser()); }
/** * 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; } // 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; } // 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 $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; }
/** * Writes a SyncTask to MAPI * * @param mixed $mapimessage * @param SyncTask $task * * @access private * @return boolean */ private function setTask($mapimessage, $task) { mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Task")); $taskmapping = MAPIMapping::GetTaskMapping(); $taskprops = MAPIMapping::GetTaskProperties(); $this->setPropsInMAPI($mapimessage, $task, $taskmapping); $taskprops = array_merge($this->getPropIdsFromStrings($taskmapping), $this->getPropIdsFromStrings($taskprops)); // task specific properties to be set $props = array(); if (isset($task->asbody)) { $this->setASbody($task->asbody, $props, $taskprops); } if (isset($task->complete)) { if ($task->complete) { // Set completion to 100% // Set status to 'complete' $props[$taskprops["completion"]] = 1.0; $props[$taskprops["status"]] = 2; $props[$taskprops["reminderset"]] = false; } else { // Set completion to 0% // Set status to 'not started' $props[$taskprops["completion"]] = 0.0; $props[$taskprops["status"]] = 0; } } if (isset($task->recurrence) && class_exists('TaskRecurrence')) { $deadoccur = false; if (isset($task->recurrence->occurrences) && $task->recurrence->occurrences == 1 || isset($task->recurrence->deadoccur) && $task->recurrence->deadoccur == 1) { //ios5 sends deadoccur inside the recurrence $deadoccur = true; } // Set PR_ICON_INDEX to 1281 to show correct icon in category view $props[$taskprops["icon"]] = 1281; // dead occur - false if new occurrences should be generated from the task // true - if it is the last ocurrence of the task $props[$taskprops["deadoccur"]] = $deadoccur; $props[$taskprops["isrecurringtag"]] = true; $recurrence = new TaskRecurrence($this->store, $mapimessage); $recur = array(); $this->setRecurrence($task, $recur); // task specific recurrence properties which we need to set here // "start" and "end" are in GMT when passing to class.recurrence // set recurrence start here because it's calculated differently for tasks and appointments $recur["start"] = $task->recurrence->start; $recur["regen"] = $task->regenerate; //Also add dates to $recur $recur["duedate"] = $task->duedate; $recurrence->setRecurrence($recur); } //open addresss book for user resolve to set the owner $addrbook = $this->getAddressbook(); // check if there is already an owner for the task, set current user if not $p = array($taskprops["owner"]); $owner = $this->getProps($mapimessage, $p); if (!isset($owner[$taskprops["owner"]])) { $userinfo = mapi_zarafa_getuser($this->store, Request::GetAuthUser()); if (mapi_last_hresult() == NOERROR && isset($userinfo["fullname"])) { $props[$taskprops["owner"]] = $userinfo["fullname"]; } } mapi_setprops($mapimessage, $props); }
/** * Writes a SyncAppointment to MAPI * * @param mixed $mapimessage * @param SyncAppointment $message * * @access private * @return boolean */ private function setAppointment($mapimessage, $appointment) { // Get timezone info if (isset($appointment->timezone)) { $tz = $this->getTZFromSyncBlob(base64_decode($appointment->timezone)); } else { $tz = false; } //calculate duration because without it some webaccess views are broken. duration is in min $localstart = $this->getLocaltimeByTZ($appointment->starttime, $tz); $localend = $this->getLocaltimeByTZ($appointment->endtime, $tz); $duration = ($localend - $localstart) / 60; //nokia sends an yearly event with 0 mins duration but as all day event, //so make it end next day if ($appointment->starttime == $appointment->endtime && isset($appointment->alldayevent) && $appointment->alldayevent) { $duration = 1440; $appointment->endtime = $appointment->starttime + 24 * 60 * 60; $localend = $localstart + 24 * 60 * 60; } // is the transmitted UID OL compatible? // if not, encapsulate the transmitted uid $appointment->uid = Utils::GetOLUidFromICalUid($appointment->uid); mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment")); $appointmentmapping = MAPIMapping::GetAppointmentMapping(); $this->setPropsInMAPI($mapimessage, $appointment, $appointmentmapping); $appointmentprops = MAPIMapping::GetAppointmentProperties(); $appointmentprops = array_merge($this->getPropIdsFromStrings($appointmentmapping), $this->getPropIdsFromStrings($appointmentprops)); //appointment specific properties to be set $props = array(); //we also have to set the responsestatus and not only meetingstatus, so we use another mapi tag $props[$appointmentprops["responsestatus"]] = isset($appointment->responsestatus) ? $appointment->responsestatus : olResponseNone; //sensitivity is not enough to mark an appointment as private, so we use another mapi tag $private = isset($appointment->sensitivity) && $appointment->sensitivity == 0 ? false : true; // Set commonstart/commonend to start/end and remindertime to start, duration, private and cleanGlobalObjectId $props[$appointmentprops["commonstart"]] = $appointment->starttime; $props[$appointmentprops["commonend"]] = $appointment->endtime; $props[$appointmentprops["reminderstart"]] = $appointment->starttime; // Set reminder boolean to 'true' if reminder is set $props[$appointmentprops["reminderset"]] = isset($appointment->reminder) ? true : false; $props[$appointmentprops["duration"]] = $duration; $props[$appointmentprops["private"]] = $private; $props[$appointmentprops["uid"]] = $appointment->uid; // Set named prop 8510, unknown property, but enables deleting a single occurrence of a recurring // type in OLK2003. $props[$appointmentprops["sideeffects"]] = 369; if (isset($appointment->reminder) && $appointment->reminder >= 0) { // Set 'flagdueby' to correct value (start - reminderminutes) $props[$appointmentprops["flagdueby"]] = $appointment->starttime - $appointment->reminder * 60; $props[$appointmentprops["remindertime"]] = $appointment->reminder; } else { $props[$appointmentprops["reminderset"]] = false; } if (isset($appointment->asbody)) { $this->setASbody($appointment->asbody, $props, $appointmentprops); } if (isset($appointment->recurrence)) { // Set PR_ICON_INDEX to 1025 to show correct icon in category view $props[$appointmentprops["icon"]] = 1025; //if there aren't any exceptions, use the 'old style' set recurrence $noexceptions = true; $recurrence = new Recurrence($this->store, $mapimessage); $recur = array(); $this->setRecurrence($appointment, $recur); // set the recurrence type to that of the MAPI $props[$appointmentprops["recurrencetype"]] = $recur["recurrencetype"]; $starttime = $this->gmtime($localstart); $endtime = $this->gmtime($localend); //set recurrence start here because it's calculated differently for tasks and appointments $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, $tz)); $recur["startocc"] = $starttime["tm_hour"] * 60 + $starttime["tm_min"]; $recur["endocc"] = $recur["startocc"] + $duration; // Note that this may be > 24*60 if multi-day //only tasks can regenerate $recur["regen"] = false; // Process exceptions. The PDA will send all exceptions for this recurring item. if (isset($appointment->exceptions)) { foreach ($appointment->exceptions as $exception) { // we always need the base date if (!isset($exception->exceptionstarttime)) { continue; } if (isset($exception->deleted) && $exception->deleted) { // Delete exception if (!isset($recur["deleted_occurences"])) { $recur["deleted_occurences"] = array(); } array_push($recur["deleted_occurences"], $this->getDayStartOfTimestamp($exception->exceptionstarttime)); } else { // Change exception $basedate = $this->getDayStartOfTimestamp($exception->exceptionstarttime); $mapiexception = array("basedate" => $basedate); //other exception properties which are not handled in recurrence $exceptionprops = array(); if (isset($exception->starttime)) { $mapiexception["start"] = $this->getLocaltimeByTZ($exception->starttime, $tz); $exceptionprops[$appointmentprops["starttime"]] = $exception->starttime; } if (isset($exception->endtime)) { $mapiexception["end"] = $this->getLocaltimeByTZ($exception->endtime, $tz); $exceptionprops[$appointmentprops["endtime"]] = $exception->endtime; } if (isset($exception->subject)) { $exceptionprops[$appointmentprops["subject"]] = $mapiexception["subject"] = u2w($exception->subject); } if (isset($exception->location)) { $exceptionprops[$appointmentprops["location"]] = $mapiexception["location"] = u2w($exception->location); } if (isset($exception->busystatus)) { $exceptionprops[$appointmentprops["busystatus"]] = $mapiexception["busystatus"] = $exception->busystatus; } if (isset($exception->reminder)) { $exceptionprops[$appointmentprops["reminderset"]] = $mapiexception["reminder_set"] = 1; $exceptionprops[$appointmentprops["remindertime"]] = $mapiexception["remind_before"] = $exception->reminder; } if (isset($exception->alldayevent)) { $exceptionprops[$appointmentprops["alldayevent"]] = $mapiexception["alldayevent"] = $exception->alldayevent; } if (!isset($recur["changed_occurences"])) { $recur["changed_occurences"] = array(); } if (isset($exception->body)) { $exceptionprops[$appointmentprops["body"]] = u2w($exception->body); } array_push($recur["changed_occurences"], $mapiexception); if (!empty($exceptionprops)) { $noexceptions = false; if ($recurrence->isException($basedate)) { $recurrence->modifyException($exceptionprops, $basedate); } else { $recurrence->createException($exceptionprops, $basedate); } } } } } //setRecurrence deletes the attachments from an appointment if ($noexceptions) { $recurrence->setRecurrence($tz, $recur); } } else { $props[$appointmentprops["isrecurring"]] = false; } //always set the PR_SENT_REPRESENTING_* props so that the attendee status update also works with the webaccess $p = array($appointmentprops["representingentryid"], $appointmentprops["representingname"], $appointmentprops["sentrepresentingaddt"], $appointmentprops["sentrepresentingemail"], $appointmentprops["sentrepresentinsrchk"]); $representingprops = $this->getProps($mapimessage, $p); if (!isset($representingprops[$appointmentprops["representingentryid"]])) { $props[$appointmentprops["representingname"]] = Request::GetAuthUser(); $props[$appointmentprops["sentrepresentingemail"]] = Request::GetAuthUser(); $props[$appointmentprops["sentrepresentingaddt"]] = "ZARAFA"; $props[$appointmentprops["representingentryid"]] = mapi_createoneoff(Request::GetAuthUser(), "ZARAFA", Request::GetAuthUser()); $props[$appointmentprops["sentrepresentinsrchk"]] = $props[$appointmentprops["sentrepresentingaddt"]] . ":" . $props[$appointmentprops["sentrepresentingemail"]]; } // Do attendees if (isset($appointment->attendees) && is_array($appointment->attendees)) { $recips = array(); // Outlook XP requires organizer in the attendee list as well $org = array(); $org[PR_ENTRYID] = isset($representingprops[$appointmentprops["representingentryid"]]) ? $representingprops[$appointmentprops["representingentryid"]] : $props[$appointmentprops["representingentryid"]]; $org[PR_DISPLAY_NAME] = isset($representingprops[$appointmentprops["representingname"]]) ? $representingprops[$appointmentprops["representingname"]] : $props[$appointmentprops["representingname"]]; $org[PR_ADDRTYPE] = isset($representingprops[$appointmentprops["sentrepresentingaddt"]]) ? $representingprops[$appointmentprops["sentrepresentingaddt"]] : $props[$appointmentprops["sentrepresentingaddt"]]; $org[PR_EMAIL_ADDRESS] = isset($representingprops[$appointmentprops["sentrepresentingemail"]]) ? $representingprops[$appointmentprops["sentrepresentingemail"]] : $props[$appointmentprops["sentrepresentingemail"]]; $org[PR_SEARCH_KEY] = isset($representingprops[$appointmentprops["sentrepresentinsrchk"]]) ? $representingprops[$appointmentprops["sentrepresentinsrchk"]] : $props[$appointmentprops["sentrepresentinsrchk"]]; $org[PR_RECIPIENT_FLAGS] = recipOrganizer | recipSendable; $org[PR_RECIPIENT_TYPE] = MAPI_TO; array_push($recips, $org); //open addresss book for user resolve $addrbook = $this->getAddressbook(); foreach ($appointment->attendees as $attendee) { $recip = array(); $recip[PR_EMAIL_ADDRESS] = u2w($attendee->email); // lookup information in GAB if possible so we have up-to-date name for given address $userinfo = array(array(PR_DISPLAY_NAME => $recip[PR_EMAIL_ADDRESS])); $userinfo = mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP); if (mapi_last_hresult() == NOERROR) { $recip[PR_DISPLAY_NAME] = $userinfo[0][PR_DISPLAY_NAME]; $recip[PR_EMAIL_ADDRESS] = $userinfo[0][PR_EMAIL_ADDRESS]; $recip[PR_SEARCH_KEY] = $userinfo[0][PR_SEARCH_KEY]; $recip[PR_ADDRTYPE] = $userinfo[0][PR_ADDRTYPE]; $recip[PR_ENTRYID] = $userinfo[0][PR_ENTRYID]; $recip[PR_RECIPIENT_TYPE] = MAPI_TO; $recip[PR_RECIPIENT_FLAGS] = recipSendable; } else { $recip[PR_DISPLAY_NAME] = u2w($attendee->name); $recip[PR_SEARCH_KEY] = "SMTP:" . $recip[PR_EMAIL_ADDRESS] . ""; $recip[PR_ADDRTYPE] = "SMTP"; $recip[PR_RECIPIENT_TYPE] = MAPI_TO; $recip[PR_ENTRYID] = mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]); } array_push($recips, $recip); } mapi_message_modifyrecipients($mapimessage, 0, $recips); $props[$appointmentprops["icon"]] = 1026; $props[$appointmentprops["mrwassent"]] = true; } mapi_setprops($mapimessage, $props); }
/** * 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; }