Reworked SyncML authentication/session handling with multi-domain support

This commit is contained in:
Jörg Lehrke 2010-02-05 20:52:55 +00:00
parent 312c782961
commit d843300394
8 changed files with 193 additions and 90 deletions

View File

@ -19,7 +19,7 @@
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
require_once(EGW_API_INC.'/class.egw_db.inc.php');
include_once 'Horde/SyncML/Command.php';
include_once 'Horde/SyncML/Command/Status.php';
include_once 'Horde/SyncML/Command/Alert.php';
@ -646,11 +646,12 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
switch ($element) {
case 'Final':
$this->_actionCommands = false;
$deviceInfo = $state->getClientDeviceInfo();
if ($state->getSyncStatus() == CLIENT_SYNC_STARTED) {
if (strtolower($deviceInfo['manufacturer']) == 'funambol'
&& isset($deviceInfo['softwareVersion'])) {
if ($state->isAuthorized() &&
($deviceInfo = $state->getClientDeviceInfo()) &&
strtolower($deviceInfo['manufacturer']) == 'funambol'
&& isset($deviceInfo['softwareVersion'])) {
$swversion = $deviceInfo['softwareVersion'];
if ($swversion < 1.0) {
// e.g. Mozilla plugin uses this range
@ -668,9 +669,8 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
} else {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
}
}
if ($state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
} elseif ($state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
$state->setSyncStatus(SERVER_SYNC_ACKNOWLEDGED);
}

View File

@ -203,6 +203,11 @@ class Horde_SyncML_State {
var $_password;
/*
* integer 0 authorization pending
* -1 authorization failed
* 1 session is authorized
*/
var $_isAuthorized;
var $_AuthConfirmed;
@ -277,7 +282,7 @@ class Horde_SyncML_State {
$this->setPassword($password);
}
$this->_isAuthorized = false;
$this->_isAuthorized = 0;
$this->_isAuthConfirmed = false;
}
@ -560,12 +565,12 @@ class Horde_SyncML_State {
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text','u'))
{
$this->_isAuthorized = true;
$this->_isAuthorized = 1;
#Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
else
{
$this->_isAuthorized = false;
$this->_isAuthorized = -1;
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
}
@ -577,7 +582,7 @@ class Horde_SyncML_State {
Horde::logMessage('SyncML_EGW: egw session('.$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $this->_isAuthorized;
return ($this->_isAuthorized > 0);
}
function isAuthConfirmed()

View File

@ -13,7 +13,7 @@
* @author Joerg Lehrke <jlehrke@noc.de>
* @version $Id$
*/
require_once(EGW_API_INC.'/class.egw_db.inc.php');
include_once dirname(__FILE__).'/State.php';
/**
@ -60,6 +60,12 @@ class EGW_SyncML_State extends Horde_SyncML_State
$ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action);
if (strstr($_id, ':')) {
// pseudo entries are related to parent entry
$parentId = array_shift(explode(':', $_id));
$pts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $parentId, $_action);
if ($pts > $ts) $ts = $pts; // We have changed the parent
}
return $ts;
}
@ -295,71 +301,99 @@ class EGW_SyncML_State extends Horde_SyncML_State
}
function isAuthorized() {
if (!$this->_isAuthorized) {
if(!isset($this->_locName) && !isset($this->_password)) {
Horde::logMessage('SyncML: Authentication not yet possible currently. Username and password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
}
if (!isset($this->_password)) {
Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
}
if(!isset($this->_locName))
{
Horde::logMessage('SyncML: Authentication not yet possible. Username not available',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
}
if (strpos($this->_locName,'@') === False) {
$this->_account_domain = $GLOBALS['egw_info']['server']['default_domain'];
$this->_locName .= '@'. $this->_account_domain;
} else {
$parts = explode('@',$this->_locName);
$this->_account_domain = array_pop($parts);
}
// store sessionID in a variable, because create() and verify() reset this value
$sessionID = session_id();
if (strpos($this->_locName,'@') === False)
{
$this->_account_domain = $GLOBALS['egw_info']['server']['default_domain'];
$this->_locName .= '@'. $this->_account_domain;
}
else
{
$parts = explode('@',$this->_locName);
$this->_account_domain = array_pop($parts);
}
if (!is_object($GLOBALS['egw']))
{
// Let the EGw core create the infrastructure classes
$_POST['login'] = $this->_locName;
$_REQUEST['domain'] = $this->_account_domain;
$GLOBALS['egw_info']['server']['default_domain'] = $this->_account_domain;
$GLOBALS['egw_info']['user']['domain'] = $this->_account_domain;
if (is_array($GLOBALS['egw_domain'][$this->_account_domain]))
$GLOBALS['egw_info']['flags']['currentapp'] = 'login';
$GLOBALS['egw_info']['flags']['noapi'] = false;
require_once(EGW_API_INC . '/functions.inc.php');
}
$GLOBALS['egw_info']['flags']['currentapp'] = 'syncml';
if (!$this->_isAuthorized)
{
if (!isset($this->_password))
{
Horde::logMessage('SyncML: Authentication not yet possible. Credetials missing',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
}
if ($GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))
{
if ($GLOBALS['egw_info']['user']['apps']['syncml'])
{
$this->_isAuthorized = 1;
// restore the original sessionID
session_regenerate_id();
session_id($sessionID);
$GLOBALS['sessionid'] = $sessionID;
@session_start();
Horde::logMessage('SyncML_EGW[' . $GLOBALS['sessionid']
.']: Authentication of ' . $this->_locName . ' succeded',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$config =& CreateObject('phpgwapi.config','syncml');
$config->read_repository();
$GLOBALS['config_syncml'] =& $config->config_data;
unset($config);
$GLOBALS['egw_info']['server']['db_host'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_host'];
$GLOBALS['egw_info']['server']['db_port'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_port'];
$GLOBALS['egw_info']['server']['db_name'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_name'];
$GLOBALS['egw_info']['server']['db_user'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_user'];
$GLOBALS['egw_info']['server']['db_pass'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_pass'];
$GLOBALS['egw_info']['server']['db_type'] = $GLOBALS['egw_domain'][$this->_account_domain]['db_type'];
// It works -- don't ask me why.
$this->db = new egw_db($GLOBALS['egw_info']['server']);
if (!$this->db->connect()) {
Horde::logMessage('SyncML_EGW: Can not connect to database for user ' . $this->_locName,
__FILE__, __LINE__, PEAR_LOG_ERROR);
return false;
}
#Horde::logMessage('SyncML: authenticate with username: ' . $this->_locName . ' and password: ' . $this->_password, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))) {
if ($GLOBALS['egw_info']['user']['apps']['syncml']) {
$this->_isAuthorized = true;
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
$this->_isAuthorized = false;
Horde::logMessage('SyncML is not enabled for user ' . $this->_locName,
__FILE__, __LINE__, PEAR_LOG_ERROR);
}
return $this->_isAuthorized;
else
{
$this->_isAuthorized = -1; // Authorization failed!
Horde::logMessage('SyncML is not enabled for user '
. $this->_locName, __FILE__, __LINE__, PEAR_LOG_ERROR);
}
}
$this->_isAuthorized = false;
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' ,
__FILE__, __LINE__, PEAR_LOG_INFO);
} else {
// store sessionID in a variable, because verify() may reset this value
$sessionID = session_id();
$GLOBALS['egw_info']['user']['domain'] = $this->_account_domain;
if (!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3')) {
Horde::logMessage('SyncML_EGW: egw session(' .$sessionID. ') not verified' ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
else
{
$this->_isAuthorized = -1;
Horde::logMessage('SyncML: Authentication of ' . $this->_locName
. ' failed', __FILE__, __LINE__, PEAR_LOG_INFO);
}
}
elseif ($this->_isAuthorized > 0)
{
if (!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3'))
{
Horde::logMessage('SyncML_EGW: egw session(' . $sessionID
. ') could not be not verified' ,
__FILE__, __LINE__, PEAR_LOG_ERROR);
}
}
return $this->_isAuthorized;
return ($this->_isAuthorized > 0);
}
/**

View File

@ -89,6 +89,9 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
if ($c === false) continue; // no content to export
if (is_a($c, 'PEAR_Error')) {
Horde::logMessage("SyncML: refresh failed to export guid $guid:\n" . print_r($c, true),
__FILE__, __LINE__, PEAR_LOG_WARNING);

View File

@ -96,6 +96,9 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
if ($c === false) continue; // no content to export
if (is_a($c, 'PEAR_Error')) {
Horde::logMessage("SyncML: slowsync failed to export guid $guid:\n" . print_r($c, true),
__FILE__, __LINE__, PEAR_LOG_WARNING);
@ -235,7 +238,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$guid = false;
$guid = $registry->call($hordeType . '/search',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()) ));
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()), $type));
if ($guid) {
// Check if the found entry came from the client

View File

@ -135,6 +135,9 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
'guid' => $guid,
'contentType' => $contentType
));
if ($c === false) continue; // no content to export
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen.
Horde :: logMessage("SyncML: change: export of guid $guid failed:\n" . print_r($c, true),
@ -343,13 +346,14 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
// Create an Add request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType . '/export', array (
'guid' => $guid,
'contentType' => $contentType,
));
if ($c === false) continue; // no content to export
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen.
Horde :: logMessage("SyncML: add: export of guid $guid failed:\n" . print_r($c, true),
@ -406,7 +410,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
function loadData() {
global $registry;
$state = & $_SESSION['SyncML.state'];
$state =& $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType);
@ -421,15 +425,29 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
$state->setAddedItems($syncType, $addedItems);
$state->setDeletedItems($syncType, $registry->call($hordeType . '/listBy', array (
$changedItems =& $state->getChangedItems($syncType);
$deletedItems =& $registry->call($hordeType . '/listBy', array (
'action' => 'delete',
'timestamp' => $refts,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
));
foreach ($deletedItems as $guid)
{
if (strstr($guid, ':'))
{
$parentGUID = array_shift(explode(':', $guid));
if (!in_array($parentGUID, $changedItems))
{
$changedItems[] = $parentGUID;
}
}
}
$state->setDeletedItems($syncType, $deletedItems);
$this->_syncDataLoaded = TRUE;
$this->_syncDataLoaded = true;
return count($state->getChangedItems($syncType)) + count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) + count($state->getConflictItems($syncType));
return count($changedItems) + count($deletedItems) + count($addedItems) + count($state->getConflictItems($syncType));
}
}

View File

@ -735,9 +735,9 @@ class Horde_iCalendar {
foreach ($values[1] as $value) {
if ((isset($params['VALUE'])
&& $params['VALUE'] == 'DATE') || (!isset($params['VALUE']) && $isDate)) {
$dates[] = $this->_parseDate($value);
$dates[] = $this->_parseDate(trim($value));
} else {
$dates[] = $this->_parseDateTime($value, $tzid);
$dates[] = $this->_parseDateTime(trim($value), $tzid);
}
}
$this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates);
@ -939,14 +939,54 @@ class Horde_iCalendar {
case 'DCREATED':
case 'LAST-MODIFIED':
$value = $this->_exportDateTime($value);
break;
// Support additional fields after date.
case 'AALARM':
case 'DALARM':
if (isset($params['VALUE'])) {
if ($params['VALUE'] == 'DATE') {
// VCALENDAR 1.0 uses T000000 - T235959 for all day events:
if ($this->isOldFormat() && $name == 'DTEND') {
$d = new Horde_Date($value);
$value = new Horde_Date(array(
'year' => $d->year,
'month' => $d->month,
'mday' => $d->mday - 1));
$value->correct();
$value = $this->_exportDate($value, '235959');
} else {
$value = $this->_exportDate($value, '000000');
}
} else {
$value = $this->_exportDateTime($value);
}
} else {
$value = $this->_exportDateTime($value);
}
if (is_array($attribute['values']) &&
count($attribute['values']) > 0) {
$values = $attribute['values'];
if ($this->isOldFormat()) {
$values = str_replace(';', '\\;', $values);
} else {
// As of rfc 2426 2.5 semicolon and comma must be
// escaped.
$values = str_replace(array('\\', ';', ','),
array('\\\\', '\\;', '\\,'),
$values);
}
$value .= ';' . implode(';', $values);
}
break;
case 'DTEND':
case 'DTSTART':
case 'DTSTAMP':
case 'DUE':
case 'AALARM':
case 'DALARM':
case 'RECURRENCE-ID':
case 'X-RECURRENCE-ID':
if (isset($params['VALUE'])) {

28
rpc.php
View File

@ -10,22 +10,27 @@
error_reporting(E_ALL & ~E_NOTICE);
@define('AUTH_HANDLER', true);
@define('HORDE_BASE', dirname(__FILE__).'/phpgwapi/inc/horde/');
@define('EGW_API_INC', dirname(__FILE__) . '/phpgwapi/inc/');
@define('HORDE_BASE', EGW_API_INC . '/horde/');
require_once HORDE_BASE . '/lib/core.php';
require_once 'Horde/RPC.php';
//require_once EGW_API_INC . '/common_functions.inc.php';
$GLOBALS['egw_info'] = array(
'flags' => array(
'currentapp' => 'login',
'noheader' => True,
'nonavbar' => True,
'disable_Template_class' => True
)
'currentapp' => 'syncml',
'noheader' => true,
'nonavbar' => true,
'noapi' => true,
'disable_Template_class' => true,
),
'server' => array(
'show_domain_selectbox' => true,
),
);
include('./header.inc.php');
// allow to use an authentication specific for SyncML
$GLOBALS['egw_info']['flags']['currentapp'] = 'syncml';
include('./header.inc.php');
$errors = array();
@ -45,11 +50,6 @@ if(version_compare(PHP_VERSION, '5.0.0') < 0) {
$errors[] = 'eGroupWare\'s SyncML server requires PHP5. Please update to PHP 5.0.x if you want to use SyncML.';
}
$config =& CreateObject('phpgwapi.config','syncml');
$config->read_repository();
$GLOBALS['config_syncml'] =& $config->config_data;
unset($config);
/* Look at the Content-type of the request, if it is available, to try
* and determine what kind of request this is. */
$input = null;