Commit 69eff8c9 authored by mku's avatar mku

ZP-519 #comment Basic autodiscover functionality

git-svn-id: https://z-push.org/svn/z-push/trunk@1894 b7dd7b3b-3a3c-0410-9da9-bee62a6cc5b5
parent 49d54234
<?php
/***********************************************
* File : autodiscover.php
* Project : Z-Push
* Descr : The autodiscover service for Z-Push.
*
* Created : 14.05.2014
*
* Copyright 2007 - 2014 Zarafa Deutschland GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation with the following additional
* term according to sec. 7:
*
* According to sec. 7 of the GNU Affero General Public License, version 3,
* the terms of the AGPL are supplemented with the following terms:
*
* "Zarafa" is a registered trademark of Zarafa B.V.
* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
* The licensing of the Program under the AGPL does not imply a trademark license.
* Therefore any rights, title and interest in our trademarks remain entirely with us.
*
* However, if you propagate an unmodified version of the Program you are
* allowed to use the term "Z-Push" to indicate that you distribute the Program.
* Furthermore you may use our trademarks where it is necessary to indicate
* the intended purpose of a product or service provided you use it in accordance
* with honest practices in industrial or commercial matters.
* If you want to propagate modified versions of the Program under the name "Z-Push",
* you may only do so if you have a written permission by Zarafa Deutschland GmbH
* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
include_once('../lib/core/zpushdefs.php');
include_once('../lib/exceptions/exceptions.php');
include_once('../lib/utils/utils.php');
include_once('../lib/core/zpush.php');
include_once('../lib/core/zlog.php');
include_once('../lib/interface/ibackend.php');
include_once('../lib/interface/ichanges.php');
include_once('../lib/interface/iexportchanges.php');
include_once('../lib/interface/iimportchanges.php');
include_once('../lib/interface/isearchprovider.php');
include_once('../lib/interface/istatemachine.php');
include_once('../config.php');
class ZPushAutodiscover {
const ACCEPTABLERESPONSESCHEMA = 'http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006';
const MAXINPUTSIZE = 8192; // The autodiscover request shouldn't exceed that value
private static $instance;
/**
* Static method to start the autodiscover process.
*
* @access public
*
* @return void
*/
public static function DoZPushAutodiscover() {
ZLog::Write(LOGLEVEL_DEBUG, '-------- Start ZPushAutodiscover');
if (!isset(self::$instance)) {
self::$instance = new ZPushAutodiscover();
}
self::$instance->DoAutodiscover();
ZLog::Write(LOGLEVEL_DEBUG, '-------- End ZPushAutodiscover');
}
/**
* Does the complete autodiscover.
* @access public
* @throws AuthenticationRequiredException if login to the backend failed.
* @throws ZPushException if the incoming XML is invalid..
*
* @return void
*/
public function DoAutodiscover() {
if (!defined('REAL_BASE_PATH')) {
define('REAL_BASE_PATH', str_replace('autodiscover/', '', BASE_PATH));
}
set_include_path(get_include_path() . PATH_SEPARATOR . REAL_BASE_PATH);
$response = "";
try {
$incomingXml = $this->getIncomingXml();
$backend = ZPush::GetBackend();
$username = $this->login($backend);
$userFullname = $backend->GetUserFullname($username);
ZLog::Write(LOGLEVEL_WBXML, sprintf("Resolved user's '%s' fullname to '%s'", $username, $userFullname));
$response = $this->createResponse($incomingXml->Request->EMailAddress, $userFullname);
}
catch (AuthenticationRequiredException $ex) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because login failed for user with email '%s'", $incomingXml->Request->EMailAddress));
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Basic realm="ZPush"');
http_response_code(401);
}
catch (ZPushException $ex) {
ZLog::Write(LOGLEVEL_ERROR, "Unable to complete autodiscover because of ZPushException. Error: %s", $ex->getMessage());
if(!headers_sent()) {
header('HTTP/1.1 '. $ex->getHTTPCodeString());
foreach ($ex->getHTTPHeaders() as $h) {
header($h);
}
}
}
$this->sendResponse($response);
}
/**
* Processes the incoming XML request and parses it to a SimpleXMLElement.
*
* @access private
* @throws ZPushException if the XML is invalid.
* @throws AuthenticationRequiredException if no login data was sent.
*
* @return SimpleXMLElement
*/
private function getIncomingXml() {
if ($_SERVER['CONTENT_LENGTH'] > ZPushAutodiscover::MAXINPUTSIZE) {
throw new ZPushException('The request input size exceeds 8kb.');
}
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
throw new AuthenticationRequiredException();
}
$input = @file_get_contents('php://input');
$xml = simplexml_load_string($input);
if (!isset($xml->Request->EMailAddress)) {
throw new ZPushException('Invalid input XML: no email address.');
}
if ($xml->Request->EMailAddress != $_SERVER['PHP_AUTH_USER']) {
throw new ZPushException('Autodiscover is supported only for the auth user.');
}
if (!isset($xml->Request->AcceptableResponseSchema)) {
throw new ZPushException('Invalid input XML: no AcceptableResponseSchema.');
}
if ($xml->Request->AcceptableResponseSchema != ZPushAutodiscover::ACCEPTABLERESPONSESCHEMA) {
throw new ZPushException('Invalid input XML: not a mobilesync responseschema.');
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("loglevel:%d, wbxml:%d", LOGLEVEL, LOGLEVEL_WBXML));
if (LOGLEVEL >= LOGLEVEL_WBXML) {
ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->getIncomingXml() incoming XML data:%s%s", PHP_EOL, $xml->asXML()));
}
return $xml;
}
/**
* Logins using the backend's Logon function.
*
* @param IBackend $backend
* @access private
* @throws AuthenticationRequiredException if no login data was sent.
*
* @return string $username
*/
private function login($backend) {
// First try to logon using the complete email address.
// If that fails, try to logon using the local part.
$username = $_SERVER['PHP_AUTH_USER'];
if($backend->Logon($username, "", $_SERVER['PHP_AUTH_PW']) == false) {
$username = Utils::GetLocalPartFromEmail($username);
if($backend->Logon($username, "", $_SERVER['PHP_AUTH_PW']) == false) {
throw new AuthenticationRequiredException("Access denied. Username or password incorrect");
}
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login() Using '%s' as the username.", $username));
return $username;
}
/**
* Creates the XML response.
*
* @param string $email
* @param string $userFullname
* @access private
*
* @return string
*/
private function createResponse($email, $userFullname) {
$xml = file_get_contents('response.xml');
$response = new SimpleXMLElement($xml);
$response->Response->User->DisplayName = $userFullname;
$response->Response->User->EMailAddress = $email;
$response->Response->Action->Settings->Server->Url = SERVERURL;
$response->Response->Action->Settings->Server->Name = SERVERURL;
$response = $response->asXML();
ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->createResponse() XML response:%s%s", PHP_EOL, $response));
return $response;
}
/**
* Sends the response to the device.
* @param string $response
* @access private
*
* @return void
*/
private function sendResponse($response) {
ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() sending response...");
header("Content-type: text/html");
$output = fopen("php://output", "w+");
fwrite($output, $response);
fclose($output);
ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() response sent.");
}
}
ZPushAutodiscover::DoZPushAutodiscover();
?>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
<Response>
<Culture>en:us</Culture>
<User>
<DisplayName></DisplayName>
<EMailAddress></EMailAddress>
</User>
<Action>
<Settings>
<Server>
<Type>MobileSync</Type>
<Url></Url>
<Name></Name>
</Server>
</Settings>
</Action>
</Response>
</Autodiscover>
\ No newline at end of file
......@@ -1180,6 +1180,20 @@ class BackendZarafa implements IBackend, ISearchProvider {
}
}
/**
* Returns the display name of the user. Used by autodiscover.
*
* @param string $username The username
*
* @access public
* @return string
*/
public function GetUserFullname($username) {
ZLog::Write(LOGLEVEL_WBXML, sprintf("ZarafaBackend->GetUserFullname for '%s'.", $username));
$zarafauserinfo = @mapi_zarafa_getuser_by_name($this->defaultstore, $username);
return (isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname']) ? $zarafauserinfo['fullname'] : $username;
}
/**----------------------------------------------------------------------------------------------------------
* Private methods
......
......@@ -60,6 +60,9 @@
// This setting specifies the owner parameter in the certificate to look at.
define("CERTIFICATE_OWNER_PARAMETER", "SSL_CLIENT_S_DN_CN");
// The Z-Push server location for the autodiscover response
define('SERVERURL', 'https://localhost/Microsoft-Server-ActiveSync');
/**********************************************************************************
* Default FileStateMachine settings
*/
......
......@@ -203,6 +203,17 @@ abstract class Backend implements IBackend {
return $r;
}
/**
* Returns the display name of the user. Used by autodiscover.
*
* @param string $username The username
*
* @access public
* @return string
*/
public function GetUserFullname($username) {
return $username;
}
/**----------------------------------------------------------------------------------------------------------
* Protected methods for BackendStorage
......
......@@ -917,6 +917,22 @@ class Utils {
return 0;
}
/**
* Returns the local part from email address.
*
* @param string $email
*
* @access public
* @return string
*/
public static function GetLocalPartFromEmail($email) {
$pos = strpos($email, '@');
if ($pos === false) {
return $email;
}
return substr($email, 0, $pos);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment