- merged SyncML-1.2 branch with trunk:

svn merge ^/trunk/phpgwapi@27377 ^/branches/SyncML-1.2/phpgwapi .
This commit is contained in:
Ralf Becker 2009-07-15 19:31:25 +00:00
parent ab20b324e0
commit e667e168b4
48 changed files with 5959 additions and 3461 deletions

View File

@ -39,7 +39,7 @@ class contenthistory
* *
* @param string $_appName the appname example: infolog_notes * @param string $_appName the appname example: infolog_notes
* @param int $_id the internal egwapp content id * @param int $_id the internal egwapp content id
* @return bool * @return boolean
*/ */
function expireMapping($_appName, $_id) function expireMapping($_appName, $_id)
{ {
@ -163,7 +163,7 @@ class contenthistory
// now update the time stamp // now update the time stamp
$newData = array ( $newData = array (
'sync_changedby' => $GLOBALS['egw_info']['user']['account_id'], 'sync_changedby' => $GLOBALS['egw_info']['user']['account_id'],
$_action == 'modify' ? 'sync_modified' : 'sync_deleted' => $_ts , $_action == 'delete' ? 'sync_deleted' : 'sync_modified' => $this->db->to_timestamp($_ts),
); );
$this->db->update(self::TABLE, $newData, $where,__LINE__,__FILE__); $this->db->update(self::TABLE, $newData, $where,__LINE__,__FILE__);
break; break;

View File

@ -0,0 +1,769 @@
<?php
define('HORDE_DATE_SUNDAY', 0);
define('HORDE_DATE_MONDAY', 1);
define('HORDE_DATE_TUESDAY', 2);
define('HORDE_DATE_WEDNESDAY', 3);
define('HORDE_DATE_THURSDAY', 4);
define('HORDE_DATE_FRIDAY', 5);
define('HORDE_DATE_SATURDAY', 6);
define('HORDE_DATE_MASK_SUNDAY', 1);
define('HORDE_DATE_MASK_MONDAY', 2);
define('HORDE_DATE_MASK_TUESDAY', 4);
define('HORDE_DATE_MASK_WEDNESDAY', 8);
define('HORDE_DATE_MASK_THURSDAY', 16);
define('HORDE_DATE_MASK_FRIDAY', 32);
define('HORDE_DATE_MASK_SATURDAY', 64);
define('HORDE_DATE_MASK_WEEKDAYS', 62);
define('HORDE_DATE_MASK_WEEKEND', 65);
define('HORDE_DATE_MASK_ALLDAYS', 127);
define('HORDE_DATE_MASK_SECOND', 1);
define('HORDE_DATE_MASK_MINUTE', 2);
define('HORDE_DATE_MASK_HOUR', 4);
define('HORDE_DATE_MASK_DAY', 8);
define('HORDE_DATE_MASK_MONTH', 16);
define('HORDE_DATE_MASK_YEAR', 32);
define('HORDE_DATE_MASK_ALLPARTS', 63);
/**
* Horde Date wrapper/logic class, including some calculation
* functions.
*
* $Horde: framework/Date/Date.php,v 1.8.10.18 2008/09/17 08:46:04 jan Exp $
*
* @package Horde_Date
*/
class Horde_Date {
/**
* Year
*
* @var integer
*/
var $year;
/**
* Month
*
* @var integer
*/
var $month;
/**
* Day
*
* @var integer
*/
var $mday;
/**
* Hour
*
* @var integer
*/
var $hour = 0;
/**
* Minute
*
* @var integer
*/
var $min = 0;
/**
* Second
*
* @var integer
*/
var $sec = 0;
/**
* Internally supported strftime() specifiers.
*
* @var string
*/
var $_supportedSpecs = '%CdDeHImMnRStTyY';
/**
* Build a new date object. If $date contains date parts, use them to
* initialize the object.
*
* Recognized formats:
* - arrays with keys 'year', 'month', 'mday', 'day' (since Horde 3.2),
* 'hour', 'min', 'minute' (since Horde 3.2), 'sec'
* - objects with properties 'year', 'month', 'mday', 'hour', 'min', 'sec'
* - yyyy-mm-dd hh:mm:ss (since Horde 3.1)
* - yyyymmddhhmmss (since Horde 3.1)
* - yyyymmddThhmmssZ (since Horde 3.1.4)
* - unix timestamps
*/
function Horde_Date($date = null)
{
if (function_exists('nl_langinfo')) {
$this->_supportedSpecs .= 'bBpxX';
}
if (is_array($date) || is_object($date)) {
foreach ($date as $key => $val) {
if (in_array($key, array('year', 'month', 'mday', 'hour', 'min', 'sec'))) {
$this->$key = (int)$val;
}
}
// If $date['day'] is present and numeric we may have been passed
// a Horde_Form_datetime array.
if (is_array($date) && isset($date['day']) &&
is_numeric($date['day'])) {
$this->mday = (int)$date['day'];
}
// 'minute' key also from Horde_Form_datetime
if (is_array($date) && isset($date['minute'])) {
$this->min = $date['minute'];
}
} elseif (!is_null($date)) {
// Match YYYY-MM-DD HH:MM:SS, YYYYMMDDHHMMSS and YYYYMMDD'T'HHMMSS'Z'.
if (preg_match('/(\d{4})-?(\d{2})-?(\d{2})T? ?(\d{2}):?(\d{2}):?(\d{2})Z?/', $date, $parts)) {
$this->year = (int)$parts[1];
$this->month = (int)$parts[2];
$this->mday = (int)$parts[3];
$this->hour = (int)$parts[4];
$this->min = (int)$parts[5];
$this->sec = (int)$parts[6];
} else {
// Try as a timestamp.
$parts = @getdate($date);
if ($parts) {
$this->year = $parts['year'];
$this->month = $parts['mon'];
$this->mday = $parts['mday'];
$this->hour = $parts['hours'];
$this->min = $parts['minutes'];
$this->sec = $parts['seconds'];
}
}
}
}
/**
* @static
*/
function isLeapYear($year)
{
if (strlen($year) != 4 || preg_match('/\D/', $year)) {
return false;
}
return (($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0);
}
/**
* Returns the day of the year (1-366) that corresponds to the
* first day of the given week.
*
* TODO: with PHP 5.1+, see http://derickrethans.nl/calculating_start_and_end_dates_of_a_week.php
*
* @param integer $week The week of the year to find the first day of.
* @param integer $year The year to calculate for.
*
* @return integer The day of the year of the first day of the given week.
*/
function firstDayOfWeek($week, $year)
{
$jan1 = new Horde_Date(array('year' => $year, 'month' => 1, 'mday' => 1));
$start = $jan1->dayOfWeek();
if ($start > HORDE_DATE_THURSDAY) {
$start -= 7;
}
return (($week * 7) - (7 + $start)) + 1;
}
/**
* @static
*/
function daysInMonth($month, $year)
{
if ($month == 2) {
if (Horde_Date::isLeapYear($year)) {
return 29;
} else {
return 28;
}
} elseif ($month == 4 || $month == 6 || $month == 9 || $month == 11) {
return 30;
} else {
return 31;
}
}
/**
* Return the day of the week (0 = Sunday, 6 = Saturday) of this
* object's date.
*
* @return integer The day of the week.
*/
function dayOfWeek()
{
if ($this->month > 2) {
$month = $this->month - 2;
$year = $this->year;
} else {
$month = $this->month + 10;
$year = $this->year - 1;
}
$day = (floor((13 * $month - 1) / 5) +
$this->mday + ($year % 100) +
floor(($year % 100) / 4) +
floor(($year / 100) / 4) - 2 *
floor($year / 100) + 77);
return (int)($day - 7 * floor($day / 7));
}
/**
* Returns the day number of the year (1 to 365/366).
*
* @return integer The day of the year.
*/
function dayOfYear()
{
$monthTotals = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
$dayOfYear = $this->mday + $monthTotals[$this->month - 1];
if (Horde_Date::isLeapYear($this->year) && $this->month > 2) {
++$dayOfYear;
}
return $dayOfYear;
}
/**
* Returns the week of the month.
*
* @since Horde 3.2
*
* @return integer The week number.
*/
function weekOfMonth()
{
return ceil($this->mday / 7);
}
/**
* Returns the week of the year, first Monday is first day of first week.
*
* @return integer The week number.
*/
function weekOfYear()
{
return $this->format('W');
}
/**
* Return the number of weeks in the given year (52 or 53).
*
* @static
*
* @param integer $year The year to count the number of weeks in.
*
* @return integer $numWeeks The number of weeks in $year.
*/
function weeksInYear($year)
{
// Find the last Thursday of the year.
$day = 31;
$date = new Horde_Date(array('year' => $year, 'month' => 12, 'mday' => $day, 'hour' => 0, 'min' => 0, 'sec' => 0));
while ($date->dayOfWeek() != HORDE_DATE_THURSDAY) {
--$date->mday;
}
return $date->weekOfYear();
}
/**
* Set the date of this object to the $nth weekday of $weekday.
*
* @param integer $weekday The day of the week (0 = Sunday, etc).
* @param integer $nth The $nth $weekday to set to (defaults to 1).
*/
function setNthWeekday($weekday, $nth = 1)
{
if ($weekday < HORDE_DATE_SUNDAY || $weekday > HORDE_DATE_SATURDAY) {
return false;
}
$this->mday = 1;
$first = $this->dayOfWeek();
if ($weekday < $first) {
$this->mday = 8 + $weekday - $first;
} else {
$this->mday = $weekday - $first + 1;
}
$this->mday += 7 * $nth - 7;
$this->correct();
return true;
}
function dump($prefix = '')
{
echo ($prefix ? $prefix . ': ' : '') . $this->year . '-' . $this->month . '-' . $this->mday . "<br />\n";
}
/**
* Is the date currently represented by this object a valid date?
*
* @return boolean Validity, counting leap years, etc.
*/
function isValid()
{
if ($this->year < 0 || $this->year > 9999) {
return false;
}
return checkdate($this->month, $this->mday, $this->year);
}
/**
* Correct any over- or underflows in any of the date's members.
*
* @param integer $mask We may not want to correct some overflows.
*/
function correct($mask = HORDE_DATE_MASK_ALLPARTS)
{
if ($mask & HORDE_DATE_MASK_SECOND) {
while ($this->sec < 0) {
--$this->min;
$this->sec += 60;
}
while ($this->sec > 59) {
++$this->min;
$this->sec -= 60;
}
}
if ($mask & HORDE_DATE_MASK_MINUTE) {
while ($this->min < 0) {
--$this->hour;
$this->min += 60;
}
while ($this->min > 59) {
++$this->hour;
$this->min -= 60;
}
}
if ($mask & HORDE_DATE_MASK_HOUR) {
while ($this->hour < 0) {
--$this->mday;
$this->hour += 24;
}
while ($this->hour > 23) {
++$this->mday;
$this->hour -= 24;
}
}
if ($mask & HORDE_DATE_MASK_MONTH) {
while ($this->month > 12) {
++$this->year;
$this->month -= 12;
}
while ($this->month < 1) {
--$this->year;
$this->month += 12;
}
}
if ($mask & HORDE_DATE_MASK_DAY) {
while ($this->mday > Horde_Date::daysInMonth($this->month, $this->year)) {
$this->mday -= Horde_Date::daysInMonth($this->month, $this->year);
++$this->month;
$this->correct(HORDE_DATE_MASK_MONTH);
}
while ($this->mday < 1) {
--$this->month;
$this->correct(HORDE_DATE_MASK_MONTH);
$this->mday += Horde_Date::daysInMonth($this->month, $this->year);
}
}
}
/**
* Compare this date to another date object to see which one is
* greater (later). Assumes that the dates are in the same
* timezone.
*
* @param mixed $date The date to compare to.
*
* @return integer == 0 if the dates are equal
* >= 1 if this date is greater (later)
* <= -1 if the other date is greater (later)
*/
function compareDate($date)
{
if (!is_a($date, 'Horde_Date')) {
$date = new Horde_Date($date);
}
if ($this->year != $date->year) {
return $this->year - $date->year;
}
if ($this->month != $date->month) {
return $this->month - $date->month;
}
return $this->mday - $date->mday;
}
/**
* Compare this to another date object by time, to see which one
* is greater (later). Assumes that the dates are in the same
* timezone.
*
* @param mixed $date The date to compare to.
*
* @return integer == 0 if the dates are equal
* >= 1 if this date is greater (later)
* <= -1 if the other date is greater (later)
*/
function compareTime($date)
{
if (!is_a($date, 'Horde_Date')) {
$date = new Horde_Date($date);
}
if ($this->hour != $date->hour) {
return $this->hour - $date->hour;
}
if ($this->min != $date->min) {
return $this->min - $date->min;
}
return $this->sec - $date->sec;
}
/**
* Compare this to another date object, including times, to see
* which one is greater (later). Assumes that the dates are in the
* same timezone.
*
* @param mixed $date The date to compare to.
*
* @return integer == 0 if the dates are equal
* >= 1 if this date is greater (later)
* <= -1 if the other date is greater (later)
*/
function compareDateTime($date)
{
if (!is_a($date, 'Horde_Date')) {
$date = new Horde_Date($date);
}
if ($diff = $this->compareDate($date)) {
return $diff;
}
return $this->compareTime($date);
}
/**
* Get the time offset for local time zone.
*
* @param boolean $colon Place a colon between hours and minutes?
*
* @return string Timezone offset as a string in the format +HH:MM.
*/
function tzOffset($colon = true)
{
$secs = $this->format('Z');
if ($secs < 0) {
$sign = '-';
$secs = -$secs;
} else {
$sign = '+';
}
$colon = $colon ? ':' : '';
$mins = intval(($secs + 30) / 60);
return sprintf('%s%02d%s%02d',
$sign, $mins / 60, $colon, $mins % 60);
}
/**
* Return the unix timestamp representation of this date.
*
* @return integer A unix timestamp.
*/
function timestamp()
{
if (class_exists('DateTime')) {
return $this->format('U');
} else {
return Horde_Date::_mktime($this->hour, $this->min, $this->sec, $this->month, $this->mday, $this->year);
}
}
/**
* Return the unix timestamp representation of this date, 12:00am.
*
* @return integer A unix timestamp.
*/
function datestamp()
{
if (class_exists('DateTime')) {
$dt = new DateTime();
$dt->setDate($this->year, $this->month, $this->mday);
$dt->setTime(0, 0, 0);
return $dt->format('U');
} else {
return Horde_Date::_mktime(0, 0, 0, $this->month, $this->mday, $this->year);
}
}
/**
* Format time using the specifiers available in date() or in the DateTime
* class' format() method.
*
* @since Horde 3.3
*
* @param string $format
*
* @return string Formatted time.
*/
function format($format)
{
if (class_exists('DateTime')) {
$dt = new DateTime();
$dt->setDate($this->year, $this->month, $this->mday);
$dt->setTime($this->hour, $this->min, $this->sec);
return $dt->format($format);
} else {
return date($format, $this->timestamp());
}
}
/**
* Format time in ISO-8601 format. Works correctly since Horde 3.2.
*
* @return string Date and time in ISO-8601 format.
*/
function iso8601DateTime()
{
return $this->rfc3339DateTime() . $this->tzOffset();
}
/**
* Format time in RFC 2822 format.
*
* @return string Date and time in RFC 2822 format.
*/
function rfc2822DateTime()
{
return $this->format('D, j M Y H:i:s') . ' ' . $this->tzOffset(false);
}
/**
* Format time in RFC 3339 format.
*
* @since Horde 3.1
*
* @return string Date and time in RFC 3339 format. The seconds part has
* been added with Horde 3.2.
*/
function rfc3339DateTime()
{
return $this->format('Y-m-d\TH:i:s');
}
/**
* Format time to standard 'ctime' format.
*
* @return string Date and time.
*/
function cTime()
{
return $this->format('D M j H:i:s Y');
}
/**
* Format date and time using strftime() format.
*
* @since Horde 3.1
*
* @return string strftime() formatted date and time.
*/
function strftime($format)
{
if (preg_match('/%[^' . $this->_supportedSpecs . ']/', $format)) {
return strftime($format, $this->timestamp());
} else {
return $this->_strftime($format);
}
}
/**
* Format date and time using a limited set of the strftime() format.
*
* @return string strftime() formatted date and time.
*/
function _strftime($format)
{
if (preg_match('/%[bBpxX]/', $format)) {
require_once 'Horde/NLS.php';
}
return preg_replace(
array('/%b/e',
'/%B/e',
'/%C/e',
'/%d/e',
'/%D/e',
'/%e/e',
'/%H/e',
'/%I/e',
'/%m/e',
'/%M/e',
'/%n/',
'/%p/e',
'/%R/e',
'/%S/e',
'/%t/',
'/%T/e',
'/%x/e',
'/%X/e',
'/%y/e',
'/%Y/',
'/%%/'),
array('$this->_strftime(NLS::getLangInfo(constant(\'ABMON_\' . (int)$this->month)))',
'$this->_strftime(NLS::getLangInfo(constant(\'MON_\' . (int)$this->month)))',
'(int)($this->year / 100)',
'sprintf(\'%02d\', $this->mday)',
'$this->_strftime(\'%m/%d/%y\')',
'sprintf(\'%2d\', $this->mday)',
'sprintf(\'%02d\', $this->hour)',
'sprintf(\'%02d\', $this->hour == 0 ? 12 : ($this->hour > 12 ? $this->hour - 12 : $this->hour))',
'sprintf(\'%02d\', $this->month)',
'sprintf(\'%02d\', $this->min)',
"\n",
'$this->_strftime(NLS::getLangInfo($this->hour < 12 ? AM_STR : PM_STR))',
'$this->_strftime(\'%H:%M\')',
'sprintf(\'%02d\', $this->sec)',
"\t",
'$this->_strftime(\'%H:%M:%S\')',
'$this->_strftime(NLS::getLangInfo(D_FMT))',
'$this->_strftime(NLS::getLangInfo(T_FMT))',
'substr(sprintf(\'%04d\', $this->year), -2)',
(int)$this->year,
'%'),
$format);
}
/**
* mktime() implementation that supports dates outside of 1970-2038,
* from http://phplens.com/phpeverywhere/adodb_date_library.
*
* @TODO remove in Horde 4
*
* This does NOT work with pre-1970 daylight saving times.
*
* @static
*/
function _mktime($hr, $min, $sec, $mon = false, $day = false,
$year = false, $is_dst = false, $is_gmt = false)
{
if ($mon === false) {
return $is_gmt
? @gmmktime($hr, $min, $sec)
: @mktime($hr, $min, $sec);
}
if ($year > 1901 && $year < 2038 &&
($year >= 1970 || version_compare(PHP_VERSION, '5.0.0', '>='))) {
return $is_gmt
? @gmmktime($hr, $min, $sec, $mon, $day, $year)
: @mktime($hr, $min, $sec, $mon, $day, $year);
}
$gmt_different = $is_gmt
? 0
: (mktime(0, 0, 0, 1, 2, 1970, 0) - gmmktime(0, 0, 0, 1, 2, 1970, 0));
$mon = intval($mon);
$day = intval($day);
$year = intval($year);
if ($mon > 12) {
$y = floor($mon / 12);
$year += $y;
$mon -= $y * 12;
} elseif ($mon < 1) {
$y = ceil((1 - $mon) / 12);
$year -= $y;
$mon += $y * 12;
}
$_day_power = 86400;
$_hour_power = 3600;
$_min_power = 60;
$_month_table_normal = array('', 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$_month_table_leaf = array('', 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$_total_date = 0;
if ($year >= 1970) {
for ($a = 1970; $a <= $year; $a++) {
$leaf = Horde_Date::isLeapYear($a);
if ($leaf == true) {
$loop_table = $_month_table_leaf;
$_add_date = 366;
} else {
$loop_table = $_month_table_normal;
$_add_date = 365;
}
if ($a < $year) {
$_total_date += $_add_date;
} else {
for ($b = 1; $b < $mon; $b++) {
$_total_date += $loop_table[$b];
}
}
}
return ($_total_date + $day - 1) * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
}
for ($a = 1969 ; $a >= $year; $a--) {
$leaf = Horde_Date::isLeapYear($a);
if ($leaf == true) {
$loop_table = $_month_table_leaf;
$_add_date = 366;
} else {
$loop_table = $_month_table_normal;
$_add_date = 365;
}
if ($a > $year) {
$_total_date += $_add_date;
} else {
for ($b = 12; $b > $mon; $b--) {
$_total_date += $loop_table[$b];
}
}
}
$_total_date += $loop_table[$mon] - $day;
$_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
$_day_time = $_day_power - $_day_time;
$ret = -($_total_date * $_day_power + $_day_time - $gmt_different);
if ($ret < -12220185600) {
// If earlier than 5 Oct 1582 - gregorian correction.
return $ret + 10 * 86400;
} elseif ($ret < -12219321600) {
// If in limbo, reset to 15 Oct 1582.
return -12219321600;
} else {
return $ret;
}
}
}

View File

@ -113,8 +113,8 @@ class Horde_RPC_syncml extends Horde_RPC {
* that came up for later debugging. */ * that came up for later debugging. */
$errorLogging = ob_get_clean(); $errorLogging = ob_get_clean();
if (!empty($errorLogging)) { if (!empty($errorLogging)) {
#Horde::logMessage('SyncML: caught output=' . Horde::logMessage('SyncML: caught output=' .
# $errorLogging, __FILE__, __LINE__, PEAR_LOG_DEBUG); $errorLogging, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
return $response; return $response;

View File

@ -1,16 +1,14 @@
<?php <?php
require_once 'Horde/Util.php';
$GLOBALS['_HORDE_STRING_CHARSET'] = 'iso-8859-1'; $GLOBALS['_HORDE_STRING_CHARSET'] = 'iso-8859-1';
/** /**
* The String:: class provides static methods for charset and locale safe * The String:: class provides static methods for charset and locale safe
* string manipulation. * string manipulation.
* *
* $Horde: framework/Util/String.php,v 1.50 2005/02/10 17:09:44 jan Exp $ * $Horde: framework/Util/String.php,v 1.43.6.31 2008/10/23 21:28:38 jan Exp $
* *
* Copyright 2003-2005 Jan Schneider <jan@horde.org> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
@ -21,18 +19,40 @@ $GLOBALS['_HORDE_STRING_CHARSET'] = 'iso-8859-1';
*/ */
class String { class String {
/**
* Caches the result of extension_loaded() calls.
*
* @param string $ext The extension name.
*
* @return boolean Is the extension loaded?
*
* @see Util::extensionExists()
*/
function extensionExists($ext)
{
static $cache = array();
if (!isset($cache[$ext])) {
$cache[$ext] = extension_loaded($ext);
}
return $cache[$ext];
}
/** /**
* Sets a default charset that the String:: methods will use if none is * Sets a default charset that the String:: methods will use if none is
* explicitely specified. * explicitly specified.
* *
* @param string $charset The charset to use as the default one. * @param string $charset The charset to use as the default one.
*/ */
function setDefaultCharset($charset) function setDefaultCharset($charset)
{ {
$GLOBALS['_HORDE_STRING_CHARSET'] = $charset; $GLOBALS['_HORDE_STRING_CHARSET'] = $charset;
if (Util::extensionExists('mbstring') && if (String::extensionExists('mbstring') &&
function_exists('mb_regex_encoding')) { function_exists('mb_regex_encoding')) {
@mb_regex_encoding($charset); $old_error = error_reporting(0);
mb_regex_encoding(String::_mbstringCharset($charset));
error_reporting($old_error);
} }
} }
@ -44,44 +64,53 @@ class String {
* The original string is returned if conversion failed or none * The original string is returned if conversion failed or none
* of the extensions were available. * of the extensions were available.
* *
* @param mixed $input The data to be converted. If $input is an an * @param mixed $input The data to be converted. If $input is an an array,
* array, the array's values get converted * the array's values get converted recursively.
* recursively. * @param string $from The string's current charset.
* @param string $from The string's current charset. * @param string $to The charset to convert the string to. If not
* @param string $to The charset to convert the string to. If not * specified, the global variable
* specified, the global variable * $_HORDE_STRING_CHARSET will be used.
* $_HORDE_STRING_CHARSET will be used.
* @param bool $recursion Internally used.
* *
* @return string The converted string. * @return mixed The converted input data.
*/ */
function convertCharset($input, $from, $to = null, $recursion = false) function convertCharset($input, $from, $to = null)
{ {
/* Don't bother converting numbers. */
if (is_numeric($input)) {
return $input;
}
/* Get the user's default character set if none passed in. */ /* Get the user's default character set if none passed in. */
if (is_null($to)) { if (is_null($to)) {
$to = $GLOBALS['_HORDE_STRING_CHARSET']; $to = $GLOBALS['_HORDE_STRING_CHARSET'];
} }
/* If the from and to character sets are identical, return now. */ /* If the from and to character sets are identical, return now. */
if (!$recursion) { $from = String::lower($from);
$from = String::lower($from); $to = String::lower($to);
$to = String::lower($to);
}
if ($from == $to) { if ($from == $to) {
return $input; return $input;
} }
if (is_array($input)) { if (is_array($input)) {
$tmp = array(); $tmp = array();
foreach ($input as $key => $val) { while (list($key, $val) = each($input)) {
$tmp[String::convertCharset($key, $from, $to, true)] = String::convertCharset($val, $from, $to, true); $tmp[String::_convertCharset($key, $from, $to)] = String::convertCharset($val, $from, $to);
} }
return $tmp; return $tmp;
} }
if (is_object($input)) { if (is_object($input)) {
// PEAR_Error objects are almost guaranteed to contain recursion,
// which will cause a segfault in PHP. We should never reach
// this line, but add a check and a log message to help the devs
// track down and fix this issue.
if (is_a($input, 'PEAR_Error')) {
Horde::logMessage('Called convertCharset() on a PEAR_Error object. ' . print_r($input, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return '';
}
$vars = get_object_vars($input); $vars = get_object_vars($input);
foreach ($vars as $key => $val) { while (list($key, $val) = each($vars)) {
$input->$key = String::convertCharset($val, $from, $to, true); $input->$key = String::convertCharset($val, $from, $to);
} }
return $input; return $input;
} }
@ -90,43 +119,62 @@ class String {
return $input; return $input;
} }
$output = false; return String::_convertCharset($input, $from, $to);
}
/* Use utf8_[en|de]code() if possible. */ /**
* Internal function used to do charset conversion.
*
* @access private
*
* @param string $input See String::convertCharset().
* @param string $from See String::convertCharset().
* @param string $to See String::convertCharset().
*
* @return string The converted string.
*/
function _convertCharset($input, $from, $to)
{
$output = '';
$from_check = (($from == 'iso-8859-1') || ($from == 'us-ascii')); $from_check = (($from == 'iso-8859-1') || ($from == 'us-ascii'));
if ($from_check && ($to == 'utf-8')) {
return utf8_encode($input);
}
$to_check = (($to == 'iso-8859-1') || ($to == 'us-ascii')); $to_check = (($to == 'iso-8859-1') || ($to == 'us-ascii'));
if (($from == 'utf-8') && $to_check) {
return utf8_decode($input); /* Use utf8_[en|de]code() if possible and if the string isn't too
* large (less than 16 MB = 16 * 1024 * 1024 = 16777216 bytes) - these
* functions use more memory. */
if (strlen($input) < 16777216 || !(String::extensionExists('iconv') || String::extensionExists('mbstring'))) {
if ($from_check && ($to == 'utf-8')) {
return utf8_encode($input);
}
if (($from == 'utf-8') && $to_check) {
return utf8_decode($input);
}
} }
/* First try iconv with transliteration. */ /* First try iconv with transliteration. */
if ($from != 'utf7-imap' && if (($from != 'utf7-imap') &&
$to != 'utf7-imap' && ($to != 'utf7-imap') &&
Util::extensionExists('iconv')) { String::extensionExists('iconv')) {
ini_set('track_errors', 1); /* We need to tack an extra character temporarily because of a bug
/* We need to tack an extra character temporarily because * in iconv() if the last character is not a 7 bit ASCII
* of a bug in iconv() if the last character is not a 7 * character. */
* bit ASCII character. */ $oldTrackErrors = ini_set('track_errors', 1);
unset($php_errormsg);
$output = @iconv($from, $to . '//TRANSLIT', $input . 'x'); $output = @iconv($from, $to . '//TRANSLIT', $input . 'x');
if (isset($php_errormsg)) { $output = (isset($php_errormsg)) ? false : String::substr($output, 0, -1, $to);
$output = false; ini_set('track_errors', $oldTrackErrors);
} else {
$output = String::substr($output, 0, -1, $to);
}
ini_restore('track_errors');
} }
/* Next try mbstring. */ /* Next try mbstring. */
if (!$output && Util::extensionExists('mbstring')) { if (!$output && String::extensionExists('mbstring')) {
$output = @mb_convert_encoding($input, $to, $from); $old_error = error_reporting(0);
$output = mb_convert_encoding($input, $to, String::_mbstringCharset($from));
error_reporting($old_error);
} }
/* At last try imap_utf7_[en|de]code if appropriate. */ /* At last try imap_utf7_[en|de]code if appropriate. */
if (!$output && Util::extensionExists('imap')) { if (!$output && String::extensionExists('imap')) {
if ($from_check && ($to == 'utf7-imap')) { if ($from_check && ($to == 'utf7-imap')) {
return @imap_utf7_encode($input); return @imap_utf7_encode($input);
} }
@ -135,7 +183,7 @@ class String {
} }
} }
return !$output ? $input : $output; return (!$output) ? $input : $output;
} }
/** /**
@ -155,12 +203,14 @@ class String {
if ($locale) { if ($locale) {
/* The existence of mb_strtolower() depends on the platform. */ /* The existence of mb_strtolower() depends on the platform. */
if (Util::extensionExists('mbstring') && if (String::extensionExists('mbstring') &&
function_exists('mb_strtolower')) { function_exists('mb_strtolower')) {
if (is_null($charset)) { if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET']; $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
} }
$ret = @mb_strtolower($string, $charset); $old_error = error_reporting(0);
$ret = mb_strtolower($string, String::_mbstringCharset($charset));
error_reporting($old_error);
if (!empty($ret)) { if (!empty($ret)) {
return $ret; return $ret;
} }
@ -173,7 +223,7 @@ class String {
} }
if (!isset($lowers[$string])) { if (!isset($lowers[$string])) {
$language = setlocale(LC_CTYPE, 0); $language = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'en_US'); setlocale(LC_CTYPE, 'C');
$lowers[$string] = strtolower($string); $lowers[$string] = strtolower($string);
setlocale(LC_CTYPE, $language); setlocale(LC_CTYPE, $language);
} }
@ -203,7 +253,9 @@ class String {
if (is_null($charset)) { if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET']; $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
} }
$ret = @mb_strtoupper($string, $charset); $old_error = error_reporting(0);
$ret = mb_strtoupper($string, String::_mbstringCharset($charset));
error_reporting($old_error);
if (!empty($ret)) { if (!empty($ret)) {
return $ret; return $ret;
} }
@ -216,7 +268,7 @@ class String {
} }
if (!isset($uppers[$string])) { if (!isset($uppers[$string])) {
$language = setlocale(LC_CTYPE, 0); $language = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'en_US'); setlocale(LC_CTYPE, 'C');
$uppers[$string] = strtoupper($string); $uppers[$string] = strtoupper($string);
setlocale(LC_CTYPE, $language); setlocale(LC_CTYPE, $language);
} }
@ -251,31 +303,34 @@ class String {
/** /**
* Returns part of a string. * Returns part of a string.
* *
* @param string $string The string to be converted. * @param string $string The string to be converted.
* @param int $start The part's start position, zero based. * @param integer $start The part's start position, zero based.
* @param int $length The part's length. * @param integer $length The part's length.
* @param string $charset The charset to use when calculating the part's * @param string $charset The charset to use when calculating the part's
* position and length, defaults to current charset. * position and length, defaults to current
* charset.
* *
* @return string The string's part. * @return string The string's part.
*/ */
function substr($string, $start, $length = null, $charset = null) function substr($string, $start, $length = null, $charset = null)
{ {
if (Util::extensionExists('mbstring')) { if (is_null($length)) {
$length = String::length($string, $charset) - $start;
}
if ($length == 0) {
return '';
}
if (String::extensionExists('mbstring')) {
if (is_null($charset)) { if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET']; $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
} }
if (is_null($length)) { $old_error = error_reporting(0);
$length = String::length($string, $charset); $ret = mb_substr($string, $start, $length, String::_mbstringCharset($charset));
} error_reporting($old_error);
$ret = @mb_substr($string, $start, $length, $charset);
if (!empty($ret)) { if (!empty($ret)) {
return $ret; return $ret;
} }
} }
if (is_null($length)) {
$length = String::length($string);
}
return substr($string, $start, $length); return substr($string, $start, $length);
} }
@ -290,11 +345,17 @@ class String {
*/ */
function length($string, $charset = null) function length($string, $charset = null)
{ {
if (Util::extensionExists('mbstring')) { if (is_null($charset)) {
if (is_null($charset)) { $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
$charset = $GLOBALS['_HORDE_STRING_CHARSET']; }
} $charset = String::lower($charset);
$ret = @mb_strlen($string, $charset); if ($charset == 'utf-8' || $charset == 'utf8') {
return strlen(utf8_decode($string));
}
if (String::extensionExists('mbstring')) {
$old_error = error_reporting(0);
$ret = mb_strlen($string, String::_mbstringCharset($charset));
error_reporting($old_error);
if (!empty($ret)) { if (!empty($ret)) {
return $ret; return $ret;
} }
@ -308,22 +369,24 @@ class String {
* *
* @param string $haystack The string to search through. * @param string $haystack The string to search through.
* @param string $needle The string to search for. * @param string $needle The string to search for.
* @param int $offset Allows to specify which character in haystack * @param integer $offset Allows to specify which character in haystack
* to start searching. * to start searching.
* @param string $charset The charset to use when searching for the * @param string $charset The charset to use when searching for the
* $needle string. * $needle string.
* *
* @return int The position of first occurrence. * @return integer The position of first occurrence.
*/ */
function pos($haystack, $needle, $offset = 0, $charset = null) function pos($haystack, $needle, $offset = 0, $charset = null)
{ {
if (Util::extensionExists('mbstring')) { if (String::extensionExists('mbstring')) {
if (is_null($charset)) { if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET']; $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
} }
ini_set('track_errors', 1); $track_errors = ini_set('track_errors', 1);
$ret = @mb_strpos($haystack, $needle, $offset, $charset); $old_error = error_reporting(0);
ini_restore('track_errors'); $ret = mb_strpos($haystack, $needle, $offset, String::_mbstringCharset($charset));
error_reporting($old_error);
ini_set('track_errors', $track_errors);
if (!isset($php_errormsg)) { if (!isset($php_errormsg)) {
return $ret; return $ret;
} }
@ -337,7 +400,7 @@ class String {
* This method behaves exactly like str_pad but is multibyte safe. * This method behaves exactly like str_pad but is multibyte safe.
* *
* @param string $input The string to be padded. * @param string $input The string to be padded.
* @param int $length The length of the resulting string. * @param integer $length The length of the resulting string.
* @param string $pad The string to pad the input string with. Must * @param string $pad The string to pad the input string with. Must
* be in the same charset like the input string. * be in the same charset like the input string.
* @param const $type The padding type. One of STR_PAD_LEFT, * @param const $type The padding type. One of STR_PAD_LEFT,
@ -388,22 +451,89 @@ class String {
/** /**
* Wraps the text of a message. * Wraps the text of a message.
* *
* @todo Make multibyte-save. * @since Horde 3.2
* *
* @access public * @param string $string String containing the text to wrap.
* @param integer $width Wrap the string at this number of
* characters.
* @param string $break Character(s) to use when breaking lines.
* @param boolean $cut Whether to cut inside words if a line
* can't be wrapped.
* @param string $charset Character set to use when breaking lines.
* @param boolean $line_folding Whether to apply line folding rules per
* RFC 822 or similar. The correct break
* characters including leading whitespace
* have to be specified too.
* *
* @param string $text String containing the text to wrap. * @return string String containing the wrapped text.
* @param optional integer $length Wrap $text at this number of */
* characters. function wordwrap($string, $width = 75, $break = "\n", $cut = false,
* @param optional string $break_char Character(s) to use when breaking $charset = null, $line_folding = false)
* lines. {
* @param optional string $charset Character set to use when breaking /* Get the user's default character set if none passed in. */
* lines. if (is_null($charset)) {
* @param optional boolean $quote Ignore lines that are wrapped with $charset = $GLOBALS['_HORDE_STRING_CHARSET'];
* the '>' character (RFC 2646)? If }
* true, we don't remove any padding $charset = String::_mbstringCharset($charset);
* whitespace at the end of the $string = String::convertCharset($string, $charset, 'utf-8');
* string. $wrapped = '';
while (String::length($string, 'utf-8') > $width) {
$line = String::substr($string, 0, $width, 'utf-8');
$string = String::substr($string, String::length($line, 'utf-8'), null, 'utf-8');
// Make sure didn't cut a word, unless we want hard breaks anyway.
if (!$cut && preg_match('/^(.+?)(\s|\r?\n)/u', $string, $match)) {
$line .= $match[1];
$string = String::substr($string, String::length($match[1], 'utf-8'), null, 'utf-8');
}
// Wrap at existing line breaks.
if (preg_match('/^(.*?)(\r?\n)(.*)$/u', $line, $match)) {
$wrapped .= $match[1] . $match[2];
$string = $match[3] . $string;
continue;
}
// Wrap at the last colon or semicolon followed by a whitespace if
// doing line folding.
if ($line_folding &&
preg_match('/^(.*?)(;|:)(\s+.*)$/u', $line, $match)) {
$wrapped .= $match[1] . $match[2] . $break;
$string = $match[3] . $string;
continue;
}
// Wrap at the last whitespace of $line.
if ($line_folding) {
$sub = '(.+[^\s])';
} else {
$sub = '(.*)';
}
if (preg_match('/^' . $sub . '(\s+)(.*)$/u', $line, $match)) {
$wrapped .= $match[1] . $break;
$string = ($line_folding ? $match[2] : '') . $match[3] . $string;
continue;
}
// Hard wrap if necessary.
if ($cut) {
$wrapped .= String::substr($line, 0, $width, 'utf-8') . $break;
$string = String::substr($line, $width, null, 'utf-8') . $string;
continue;
}
$wrapped .= $line;
}
return String::convertCharset($wrapped . $string, 'utf-8', $charset);
}
/**
* Wraps the text of a message.
*
* @param string $text String containing the text to wrap.
* @param integer $length Wrap $text at this number of characters.
* @param string $break_char Character(s) to use when breaking lines.
* @param string $charset Character set to use when breaking lines.
* @param boolean $quote Ignore lines that are wrapped with the '>'
* character (RFC 2646)? If true, we don't
* remove any padding whitespace at the end of
* the string.
* *
* @return string String containing the wrapped text. * @return string String containing the wrapped text.
*/ */
@ -422,7 +552,7 @@ class String {
if ($input != '-- ') { if ($input != '-- ') {
$input = rtrim($input); $input = rtrim($input);
} }
$line = wordwrap($input, $length, $break_char); $line = String::wordwrap($input, $length, $break_char, false, $charset);
} }
$paragraphs[] = $line; $paragraphs[] = $line;
@ -432,9 +562,8 @@ class String {
} }
/** /**
* Returns true if the every character in the parameter is an * Returns true if the every character in the parameter is an alphabetic
* alphabetic character. This method doesn't work with any charset * character.
* other than the current charset yet.
* *
* @param $string The string to test. * @param $string The string to test.
* @param $charset The charset to use when testing the string. * @param $charset The charset to use when testing the string.
@ -443,26 +572,30 @@ class String {
*/ */
function isAlpha($string, $charset = null) function isAlpha($string, $charset = null)
{ {
if (Util::extensionExists('mbstring')) { if (!String::extensionExists('mbstring')) {
$old_charset = mb_regex_encoding(); return ctype_alpha($string);
if ($charset != $old_charset) {
@mb_regex_encoding($charset);
}
$alpha = !mb_ereg_match('[^[:alpha:]]', $string);
if ($charset != $old_charset) {
@mb_regex_encoding($old_charset);
}
return $alpha;
} }
return ctype_alpha($string); $charset = String::_mbstringCharset($charset);
$old_charset = mb_regex_encoding();
$old_error = error_reporting(0);
if ($charset != $old_charset) {
mb_regex_encoding($charset);
}
$alpha = !mb_ereg_match('[^[:alpha:]]', $string);
if ($charset != $old_charset) {
mb_regex_encoding($old_charset);
}
error_reporting($old_error);
return $alpha;
} }
/** /**
* Returns true if every character in the parameter is a lowercase * Returns true if ever character in the parameter is a lowercase letter in
* letter in the current locale. * the current locale.
*
* @access public
* *
* @param $string The string to test. * @param $string The string to test.
* @param $charset The charset to use when testing the string. * @param $charset The charset to use when testing the string.
@ -476,10 +609,8 @@ class String {
} }
/** /**
* Returns true if every character in the parameter is an * Returns true if every character in the parameter is an uppercase letter
* uppercase letter in the current locale. * in the current locale.
*
* @access public
* *
* @param string $string The string to test. * @param string $string The string to test.
* @param string $charset The charset to use when testing the string. * @param string $charset The charset to use when testing the string.
@ -493,63 +624,59 @@ class String {
} }
/** /**
* Performs a regex match search on the text provided. Will correctly * Performs a multibyte safe regex match search on the text provided.
* handle text with multibyte characters if the mbstring extensions and
* the mbregex functions are available. Will use the preg_match()
* function if possible or if the mbregex ereg function is not available.
* *
* @access public
* @since Horde 3.1 * @since Horde 3.1
* *
* @param string $text The text to search. * @param string $text The text to search.
* @param array $regex The regular expressions to use. These * @param array $regex The regular expressions to use, without perl
* expressions should conform to ereg() rules - * regex delimiters (e.g. '/' or '|').
* extended perl rules are NOT supported.
* Additionally, do NOT add perl regex delimiters
* (e.g. '/' or '|') to the beginning/end.
* @param string $charset The character set of the text. * @param string $charset The character set of the text.
* *
* @return array The matches array from the first regex that matches. * @return array The matches array from the first regex that matches.
*/ */
function regexMatch($text, $regex, $charset = null) function regexMatch($text, $regex, $charset = null)
{ {
static $mbregex; if (!empty($charset)) {
if (!isset($mbregex)) { $regex = String::convertCharset($regex, $charset, 'utf-8');
$mbregex = function_exists('mb_ereg'); $text = String::convertCharset($text, $charset, 'utf-8');
}
$use_mb = false;
if ($mbregex && !is_null($charset) &&
(String::lower($charset) != 'utf-8')) {
$old_charset = mb_regex_encoding();
if ($charset != $old_charset) {
@mb_regex_encoding($charset);
} else {
unset($old_charset);
}
$use_mb = true;
} }
$matches = array(); $matches = array();
foreach ($regex as $val) { foreach ($regex as $val) {
if ($use_mb) { if (preg_match('/' . $val . '/u', $text, $matches)) {
if (mb_ereg($val, $text, $matches)) { break;
break;
}
} else {
if (preg_match('/' . $val . '/u', $text, $matches)) {
break;
}
} }
} }
if (isset($old_charset)) { if (!empty($charset)) {
@mb_regex_encoding($old_charset); $matches = String::convertCharset($matches, 'utf-8', $charset);
} }
return $matches; return $matches;
} }
/**
* Workaround charsets that don't work with mbstring functions.
*
* @access private
*
* @param string $charset The original charset.
*
* @return string The charset to use with mbstring functions.
*/
function _mbstringCharset($charset)
{
/* mbstring functions do not handle the 'ks_c_5601-1987' &
* 'ks_c_5601-1989' charsets. However, these charsets are used, for
* example, by various versions of Outlook to send Korean characters.
* Use UHC (CP949) encoding instead. See, e.g.,
* http://lists.w3.org/Archives/Public/ietf-charsets/2001AprJun/0030.html */
if (in_array(String::lower($charset), array('ks_c_5601-1987', 'ks_c_5601-1989'))) {
$charset = 'UHC';
}
return $charset;
}
} }

View File

@ -1,32 +1,32 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3.0
*
* The Horde_SyncML_SyncHdr and Horde_SyncML_SyncBody classes provides
* a SyncHdr and SyncBody in SyncML Representation Protocol, version
* 1.1 5.2.2 and 5.2.3. Most of the work is passed on to
* Horde_SyncML_Command_Alert and Horde_SyncML_Command_Sync.
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
include_once 'Horde/SyncML/Command/Status.php'; include_once 'Horde/SyncML/Command/Status.php';
include_once 'Horde/SyncML/Command/Alert.php'; include_once 'Horde/SyncML/Command/Alert.php';
include_once 'Horde/SyncML/Command/Final.php'; include_once 'Horde/SyncML/Command/Final.php';
include_once 'Horde/SyncML/Command/Sync.php'; include_once 'Horde/SyncML/Command/Sync.php';
include_once 'Horde/SyncML/Sync.php'; include_once 'Horde/SyncML/Sync.php';
include_once 'Horde/SyncML/Command/Sync/SyncElementItem.php';
/**
* The Horde_SyncML_SyncHdr and Horde_SyncML_SyncBody classes provides
* a SyncHdr and SyncBody in SyncML Representation Protocol, version
* 1.1 5.2.2 and 5.2.3. Most of the work is passed on to
* Horde_SyncML_Command_Alert and Horde_SyncML_Command_Sync.
*
* $Horde: framework/SyncML/SyncML.php,v 1.21 2004/07/21 19:26:36 karsten Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
*
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_ContentHandler { class Horde_SyncML_ContentHandler {
/** /**
@ -144,41 +144,52 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
var $_credType; var $_credType;
var $_maxMsgSize;
function getStateFromSession($sourceURI, $locName, $sessionID) function getStateFromSession($sourceURI, $locName, $sessionID)
{ {
// Remove any existing session since we'll be contructing a // Remove any existing session since we'll be contructing a
// custom session id. // custom session id.
session_regenerate_id();
session_destroy(); session_destroy();
// we need to (re-)load the eGW session-handler, as session_destroy unloads custom session-handlers // we need to (re-)load the eGW session-handler, as session_destroy unloads custom session-handlers
if (function_exists('init_session_handler')) if (function_exists('init_session_handler')) {
{ init_session_handler();
init_session_handler();
} }
// Reload the Horde SessionHandler if necessary.
// Reload the Horde SessionHandler if necessary.
Horde::setupSessionHandler(); Horde::setupSessionHandler();
// It would seem multisync does not send the user name once it // It would seem multisync does not send the user name once it
// has been authorized. Make sure we have a valid session id. // has been authorized. Make sure we have a valid session id.
if(!empty($_GET['syncml_sessionid'])) { if(!empty($_GET['syncml_sessionid'])) {
session_id($_GET['syncml_sessionid']); session_id($_GET['syncml_sessionid']);
Horde::logMessage('SyncML['. session_id() .']: reusing existing session', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() .']: reusing existing session',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
} else { } else {
#session_id('syncml' . preg_replace('/[^a-zA-Z0-9]/', '', $sourceURI . $sessionID)); #session_id('syncml' . preg_replace('/[^a-zA-Z0-9]/', '', $sourceURI . $sessionID));
session_id('syncml-' . md5(uniqid(rand(), true))); session_id('syncml-' . md5(uniqid(rand(), true)));
Horde::logMessage('SyncML['. session_id() .']: starting new session for '.$this->_locName, __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: starting new session for '
. $this->_locName, __FILE__, __LINE__, PEAR_LOG_INFO);
} }
@session_start(); @session_start();
if (!isset($_SESSION['SyncML.state'])) { if (!isset($_SESSION['SyncML.state'])) {
// Create a new state if one does not already exist. // Create a new state if one does not already exist.
Horde::logMessage('SyncML['. session_id() .']: create new session state variable', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() .']: create new session state variable for '
. $sourceURI, __FILE__, __LINE__, PEAR_LOG_DEBUG);
# LK $_SESSION['SyncML.state'] = new Horde_SyncML_State($sourceURI, $locName, $sessionID);
$_SESSION['SyncML.state'] = new EGW_SyncML_State($sourceURI, $locName, $sessionID); $_SESSION['SyncML.state'] = new EGW_SyncML_State($sourceURI, $locName, $sessionID);
} }
#if($_SESSION['SyncML.state']->_isAuthorized)
# Horde::logMessage('SyncML['. session_id() .']: is session authorized', __FILE__, __LINE__, PEAR_LOG_DEBUG);
if($_SESSION['SyncML.state']->isAuthorized()) {
Horde::logMessage('SyncML['. session_id() .']: session is authorized',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
}
#Horde::logMessage('SyncML['. session_id() . "]:\n" . print_r($_SESSION['SyncML.state'], true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $_SESSION['SyncML.state']; return $_SESSION['SyncML.state'];
} }
@ -216,18 +227,26 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
#Horde::logMessage('SymcML: SyncHdr done. Try to load state from session.', __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage('SymcML: SyncHdr done. Try to load state from session.', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state = $this->getStateFromSession($this->_sourceURI, $this->_locName, $this->_sessionID); $state = $this->getStateFromSession($this->_sourceURI, $this->_locName, $this->_sessionID);
Horde::logMessage('SyncML['. session_id() .']: package '.$this->_msgID.' +++++++++++++++++++++ started', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() .']: package '
. $this->_msgID.' +++++++++++++++++++++ started',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setVersion($this->_version); $state->setVersion($this->_version);
$state->setMsgID($this->_msgID); $state->setMsgID($this->_msgID);
$state->setTargetURI($this->_targetURI); $state->setTargetURI($this->_targetURI);
$state->setWBXML(is_a($this->_output, 'XML_WBXML_Encoder')); $state->setWBXML(is_a($this->_output, 'XML_WBXML_Encoder'));
if(isset($this->_credData) && isset($this->_locName) && !$state->isAuthorized())
{ if(isset($this->_credData)
&& isset($this->_locName)
&& !$state->isAuthorized()) {
$state->setPassword($this->_credData); $state->setPassword($this->_credData);
$state->setLocName($this->_locName); $state->setLocName($this->_locName);
} }
if (isset($this->_maxMsgSize)) {
$state->setMaxMsgSize($this->_maxMsgSize);
}
#$str = 'authorized=' . $state->isAuthorized(); #$str = 'authorized=' . $state->isAuthorized();
#$str .= ' version=' . $state->getVersion(); #$str .= ' version=' . $state->getVersion();
#$str .= ' msgid=' . $state->getMsgID(); #$str .= ' msgid=' . $state->getMsgID();
@ -246,11 +265,17 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
case 3: case 3:
if ($element == 'VerProto') { if ($element == 'VerProto') {
// </VerProto></SyncHdr></SyncML> // </VerProto></SyncHdr></SyncML>
if (trim($this->_chars) == 'SyncML/1.1') { switch (strtolower(trim($this->_chars))) {
$this->_version = 1; case 'syncml/1.2':
} else { $this->_version = 2;
$this->_version = 0; break;
} case 'syncml/1.1':
$this->_version = 1;
break;
default:
$this->_version = 0;
break;
}
} elseif ($element == 'SessionID') { } elseif ($element == 'SessionID') {
// </SessionID></SyncHdr></SyncML> // </SessionID></SyncHdr></SyncML>
$this->_sessionID = trim($this->_chars); $this->_sessionID = trim($this->_chars);
@ -271,8 +296,7 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
$tmp = explode(':', $this->_credData, 2); $tmp = explode(':', $this->_credData, 2);
// set only if not set by LocName already // set only if not set by LocName already
if(!isset($this->_locName)) if(!isset($this->_locName)) {
{
$this->_locName = $tmp[0]; $this->_locName = $tmp[0];
} }
$this->_credData = $tmp[1]; $this->_credData = $tmp[1];
@ -282,26 +306,34 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
break; break;
case 4: case 4:
if ($element == 'LocURI') { switch ($element) {
if ($this->_isSource) { case 'LocURI':
// </LocURI></Source></SyncHdr></SyncML> if ($this->_isSource) {
$this->_sourceURI = trim($this->_chars); // </LocURI></Source></SyncHdr></SyncML>
} else { $this->_sourceURI = trim($this->_chars);
// </LocURI></Target></SyncHdr></SyncML> } else {
$this->_targetURI = trim($this->_chars); // </LocURI></Target></SyncHdr></SyncML>
} $this->_targetURI = trim($this->_chars);
} elseif ($element == 'LocName') { }
if ($this->_isSource) { break;
// </LocName></Source></SyncHdr></SyncML> case 'LocName':
$this->_locName = trim($this->_chars); if ($this->_isSource) {
} // </LocName></Source></SyncHdr></SyncML>
} elseif ($element == 'Data') { $this->_locName = trim($this->_chars);
// </Data></Cred></SyncHdr></SyncML> }
if ($this->_isCred) { break;
$this->_credData = trim($this->_chars); case 'Data':
} // </Data></Cred></SyncHdr></SyncML>
} if ($this->_isCred) {
break; $this->_credData = trim($this->_chars);
}
break;
case 'MaxMsgSize':
//</MaxMsgSize></Meta></SyncHdr></SyncML>
$this->_maxMsgSize = trim($this->_chars);
break;
}
break;
case 5: case 5:
if ($this->_isCred) { if ($this->_isCred) {
@ -323,19 +355,31 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
{ {
$attrs = array(); $attrs = array();
$state = $_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$uri = $state->getURI(); $uri = $state->getURI();
$uriMeta = $state->getURIMeta(); $uriMeta = $state->getURIMeta();
$output->startElement($uri, 'SyncHdr', $attrs); $output->startElement($uri, 'SyncHdr', $attrs);
$output->startElement($uri, 'VerDTD', $attrs); $output->startElement($uri, 'VerDTD', $attrs);
$chars = ($this->_version == 1) ? '1.1' : '1.0'; if ($this->_version == 2) {
$chars = '1.2';
} elseif ($this->_version == 1) {
$chars = '1.1';
} else {
$chars = '1.0';
}
$output->characters($chars); $output->characters($chars);
$output->endElement($uri, 'VerDTD'); $output->endElement($uri, 'VerDTD');
$output->startElement($uri, 'VerProto', $attrs); $output->startElement($uri, 'VerProto', $attrs);
$chars = ($this->_version == 1) ? 'SyncML/1.1' : 'SyncML/1.0'; if ($this->_version == 2) {
$chars = 'SyncML/1.2';
} elseif ($this->_version == 1) {
$chars = 'SyncML/1.1';
} else {
$chars = 'SyncML/1.0';
}
$output->characters($chars); $output->characters($chars);
$output->endElement($uri, 'VerProto'); $output->endElement($uri, 'VerProto');
@ -359,37 +403,40 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
$output->endElement($uri, 'LocURI'); $output->endElement($uri, 'LocURI');
$output->endElement($uri, 'Source'); $output->endElement($uri, 'Source');
if(session_id() != '' && !strpos($this->_targetURI,'syncml_sessionid')) { if (session_id() != '' && !strpos($this->_targetURI,'syncml_sessionid')) {
$output->startElement($uri, 'RespURI', $attrs); $output->startElement($uri, 'RespURI', $attrs);
// some clients don't send the whole URL as targetURI // some clients don't send the whole URL as targetURI
if (strpos($this->_targetURI,$_SERVER['PHP_SELF']) === false) { if (strpos($this->_targetURI,$_SERVER['PHP_SELF']) === false) {
$output->characters($this->_targetURI . $_SERVER['PHP_SELF'] . '?syncml_sessionid=' . session_id()); $output->characters($this->_targetURI . $_SERVER['PHP_SELF'] . '?syncml_sessionid=' . session_id());
} else { } else {
$output->characters($this->_targetURI . '?syncml_sessionid=' . session_id()); $output->characters($this->_targetURI . '?syncml_sessionid=' . session_id());
}
$output->endElement($uri, 'RespURI');
} }
$output->endElement($uri, 'RespURI');
}
/*
$output->startElement($uri, 'Meta', $attrs); $output->startElement($uri, 'Meta', $attrs);
// Dummy Max MsqSize, this is just put in to make the packet if (!($maxMsgSize = $state->getMaxMsgSizeClient())) {
// work, it is not a real value. // Dummy MaxMsqSize, this is just put in to make the packet
// work, it is not our real value.
$maxMsgSize = 50000;
}
$output->startElement($uriMeta, 'MaxMsgSize', $attrs); $output->startElement($uriMeta, 'MaxMsgSize', $attrs);
$chars = '50000'; $output->characters($maxMsgSize);
$output->characters($chars);
$output->endElement($uriMeta, 'MaxMsgSize'); $output->endElement($uriMeta, 'MaxMsgSize');
// Dummy MaxObjSize, this is just put in to make the packet #// Dummy MaxObjSize, this is just put in to make the packet
// work, it is not a real value. #// work, it is not our real value.
$output->startElement($uriMeta, 'MaxObjSize', $attrs); #if ($this->_version > 0) {
$chars = '4000000'; # // Don't send this to old devices
$output->characters($chars); # $output->startElement($uriMeta, 'MaxObjSize', $attrs);
$output->endElement($uriMeta, 'MaxObjSize'); # $output->characters('4000000');
# $output->endElement($uriMeta, 'MaxObjSize');
#}
$output->endElement($uri, 'Meta'); $output->endElement($uri, 'Meta');
*/
$output->endElement($uri, 'SyncHdr'); $output->endElement($uri, 'SyncHdr');
} }
@ -448,26 +495,27 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
function startElement($uri, $element, $attrs) function startElement($uri, $element, $attrs)
{ {
parent::startElement($uri, $element, $attrs); parent::startElement($uri, $element, $attrs);
$state = &$_SESSION['SyncML.state'];
switch ($this->_xmlStack) { switch ($this->_xmlStack) {
case 2: case 2:
$state = & $_SESSION['SyncML.state'];
$this->_actionCommands = false; // so far, we have not seen commands that require action from our side $this->_actionCommands = false; // so far, we have not seen commands that require action from our side
$state->_sendFinal = false; $state->_sendFinal = false;
// <SyncML><SyncBody> // <SyncML><SyncBody>
$this->_output->startElement($uri, $element, $attrs); $this->_output->startElement($uri, $element, $attrs);
if($state->getLocName()) if($state->getLocName()) {
{ if($state->isAuthConfirmed()) {
// Right our status about the header. // Right our status about the header
$status = new Horde_SyncML_Command_Status(($state->isAuthorized()) ? $status = new Horde_SyncML_Command_Status(($state->isAuthorized()) ?
RESPONSE_AUTHENTICATION_ACCEPTED : RESPONSE_INVALID_CREDENTIALS, 'SyncHdr'); RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS, 'SyncHdr');
} } else {
else // Right our status about the header.
{ $status = new Horde_SyncML_Command_Status(($state->isAuthorized()) ?
RESPONSE_AUTHENTICATION_ACCEPTED : RESPONSE_INVALID_CREDENTIALS, 'SyncHdr');
}
} else {
// Request credentials if not sent so far // Request credentials if not sent so far
$status = new Horde_SyncML_Command_Status(RESPONSE_MISSING_CREDENTIALS, 'SyncHdr'); $status = new Horde_SyncML_Command_Status(RESPONSE_MISSING_CREDENTIALS, 'SyncHdr');
} }
@ -475,6 +523,7 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
$status->setSourceRef($state->getSourceURI()); $status->setSourceRef($state->getSourceURI());
$status->setTargetRef($state->getTargetURI()); $status->setTargetRef($state->getTargetURI());
$status->setCmdRef(0); $status->setCmdRef(0);
$state->clearNumberOfElements();
/*$str = 'authorized=' . $state->isAuthorized(); /*$str = 'authorized=' . $state->isAuthorized();
$str .= ' version=' . $state->getVersion(); $str .= ' version=' . $state->getVersion();
@ -483,11 +532,12 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
$str .= ' target=' . $state->getTargetURI(); $str .= ' target=' . $state->getTargetURI();
*/ */
$this->_currentCmdID = $status->output($this->_currentCmdID, $this->_output); $this->_currentCmdID = $status->output($this->_currentCmdID, $this->_output);
if ($state->isAuthorized()) {
$state->AuthConfirmed();
}
break; break;
case 3: case 3:
$state = & $_SESSION['SyncML.state'];
// <SyncML><SyncBody><[Command]> // <SyncML><SyncBody><[Command]>
#Horde::logMessage('SyncML['. session_id() ."]: found command $element ", __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage('SyncML['. session_id() ."]: found command $element ", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->_currentCommand = Horde_SyncML_Command::factory($element); $this->_currentCommand = Horde_SyncML_Command::factory($element);
@ -497,7 +547,7 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
// We've got to do something! This can't be the last // We've got to do something! This can't be the last
// packet. // packet.
$this->_actionCommands = true; $this->_actionCommands = true;
Horde::logMessage('SyncML['. session_id() ."]: found action commands <$element> " . $this->_actionCommands, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() ."]: found action commands <$element> ", __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
switch($element) switch($element)
@ -516,23 +566,29 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
} }
} }
function endElement($uri, $element) { function endElement($uri, $element)
{
$state = &$_SESSION['SyncML.state'];
switch ($this->_xmlStack) { switch ($this->_xmlStack) {
case 2: case 2:
// </SyncBody></SyncML> // </SyncBody></SyncML>
$state = & $_SESSION['SyncML.state'];
Horde::logMessage('SyncML['. session_id() .']: package ----------------------- done', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() .']: package ----------------------- done', __FILE__, __LINE__, PEAR_LOG_DEBUG);
if($state->getSyncStatus() == CLIENT_SYNC_FINNISHED && $state->getAlert222Received() == true) { if ($state->getAlert222Received() == true) {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED); // the Funambol specialty
if ($state->getSyncStatus() == CLIENT_SYNC_FINNISHED) {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
}
$state->setAlert222Received(false); $state->setAlert222Received(false);
} }
// send the sync reply // send the sync reply
// we do still have some data to send OR // we do still have some data to send OR
// we should reply to the Sync command // we should reply to the Sync command
if($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED) { if($state->getSyncStatus() > CLIENT_SYNC_FINNISHED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED) {
$sync = new Horde_SyncML_Command_Sync(); $sync = new Horde_SyncML_Command_Sync();
$this->_currentCmdID = $sync->syncToClient($this->_currentCmdID, $this->_output); $this->_currentCmdID = $sync->syncToClient($this->_currentCmdID, $this->_output);
} }
@ -546,13 +602,13 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
$this->_output->endElement($uri, $element); $this->_output->endElement($uri, $element);
Horde::logMessage('SyncML['. session_id() .']: syncStatus ' . $state->getSyncStatus() .'actionCommands: '.$this->_actionCommands, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML['. session_id() .']: syncStatus = '. $state->getSyncStatus() .', actionCommands = '.
($this->_actionCommands ? 'True' : 'False'), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!$this->_actionCommands && $state->getSyncStatus() == SERVER_SYNC_FINNISHED) { if (!$this->_actionCommands && $state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
// this packet did not contain any real actions, just status and map. // this packet did not contain any real actions, just status and map.
// This means, we're through! The session can be closed and // This means, we're through! The session can be closed and
// the Anchors saved for the next Sync // the Anchors saved for the next Sync
$state = & $_SESSION['SyncML.state'];
Horde::logMessage('SyncML['. session_id() .']: sync' . session_id() . ' completed successfully!', __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: sync' . session_id() . ' completed successfully!', __FILE__, __LINE__, PEAR_LOG_INFO);
$state->writeSyncSummary(); $state->writeSyncSummary();
$log = $state->getLog(); $log = $state->getLog();
@ -560,6 +616,9 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
foreach($log as $k => $v) { foreach($log as $k => $v) {
$s .= " $k=$v"; $s .= " $k=$v";
} }
if (strlen(trim($s)) == 0) {
$s = ' Both parties were already in sync';
}
Horde::logMessage('SyncML['. session_id() .']: summary:' . $s, __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: summary:' . $s, __FILE__, __LINE__, PEAR_LOG_INFO);
# Horde::logMessage('SyncML['. session_id() .']: destroying sync session '.session_id(), __FILE__, __LINE__, PEAR_LOG_INFO); # Horde::logMessage('SyncML['. session_id() .']: destroying sync session '.session_id(), __FILE__, __LINE__, PEAR_LOG_INFO);
# // session can be closed here! # // session can be closed here!
@ -571,7 +630,6 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
// this packet did not contain any real actions, just status and map. // this packet did not contain any real actions, just status and map.
// This means, we're through! The session can be closed and // This means, we're through! The session can be closed and
// the Anchors saved for the next Sync // the Anchors saved for the next Sync
$state = & $_SESSION['SyncML.state'];
Horde::logMessage('SyncML['. session_id() .']: sync' . session_id() . ' completed successfully!', __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: sync' . session_id() . ' completed successfully!', __FILE__, __LINE__, PEAR_LOG_INFO);
$state->writeSyncSummary(); $state->writeSyncSummary();
$log = $state->getLog(); $log = $state->getLog();
@ -579,6 +637,9 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
foreach($log as $k => $v) { foreach($log as $k => $v) {
$s .= " $k=$v"; $s .= " $k=$v";
} }
if (strlen(trim($s)) == 0) {
$s = ' Both parties were already in sync';
}
Horde::logMessage('SyncML['. session_id() .']: summary:' . $s, __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: summary:' . $s, __FILE__, __LINE__, PEAR_LOG_INFO);
Horde::logMessage('SyncML['. session_id() .']: destroying sync session '.session_id(), __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML['. session_id() .']: destroying sync session '.session_id(), __FILE__, __LINE__, PEAR_LOG_INFO);
@ -586,75 +647,53 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
session_unset(); session_unset();
session_destroy(); session_destroy();
} }
if($state->getSyncStatus() == CLIENT_SYNC_FINNISHED) {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
Horde::logMessage('SyncML['. session_id() .']: syncStatus(client sync acknowledged) '.$state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
break; break;
case 3: case 3:
// </[Command]></SyncBody></SyncML> // </[Command]></SyncBody></SyncML>
$state = & $_SESSION['SyncML.state'];
// this should be moved to case 2:
if($element == 'Final')
{
// make sure that we request devinfo, if we not have them already
/* if(!$state->getClientDeviceInfo())
{
$attrs = array();
$this->_output->startElement($state->getURI(), 'Get', $attrs);
$this->_output->startElement($state->getURI(), 'CmdID', $attrs);
$this->_output->characters($this->_currentCmdID);
$this->_currentCmdID++;
$this->_output->endElement($state->getURI(), 'CmdID');
$this->_output->startElement($state->getURI(), 'Meta', $attrs);
$this->_output->startElement($state->getURIMeta(), 'Type', $attrs);
if(is_a($this->_output, 'XML_WBXML_Encoder'))
$this->_output->characters('application/vnd.syncml-devinf+wbxml');
else
$this->_output->characters('application/vnd.syncml-devinf+xml');
$this->_output->endElement($state->getURIMeta(), 'Type');
$this->_output->endElement($state->getURI(), 'Meta');
$this->_output->startElement($state->getURI(), 'Item', $attrs);
$this->_output->startElement($state->getURI(), 'Target', $attrs);
$this->_output->startElement($state->getURI(), 'LocURI', $attrs);
$this->_output->characters(($state->getVersion() == 0) ? './devinf10' : './devinf11');
$this->_output->endElement($state->getURI(), 'LocURI');
$this->_output->endElement($state->getURI(), 'Target');
$this->_output->endElement($state->getURI(), 'Item');
$this->_output->endElement($state->getURI(), 'Get');
} */
}
$this->_currentCommand->endElement($uri, $element); $this->_currentCommand->endElement($uri, $element);
switch($element) { switch($element) {
case 'Final': case 'Final':
if($state->getSyncStatus() == CLIENT_SYNC_STARTED) { $this->_actionCommands = false;
$state->setSyncStatus(CLIENT_SYNC_FINNISHED); $deviceInfo = $state->getClientDeviceInfo();
Horde::logMessage('SyncML['. session_id() .']: syncStatus(client sync finnished) ' . $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
if($state->getSyncStatus() == SERVER_SYNC_FINNISHED) { if ($state->getSyncStatus() == CLIENT_SYNC_STARTED) {
$state->setSyncStatus(SERVER_SYNC_ACKNOWLEDGED); if (strtolower($deviceInfo['manufacturer']) == 'funambol'
Horde::logMessage('SyncML['. session_id() .']: syncStatus(server sync acknowledged) ' . $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG); && isset($deviceInfo['softwareVersion'])) {
} $swversion = $deviceInfo['softwareVersion'];
if ($swversion < 1.0) {
// e.g. Mozilla plugin uses this range
$swversion = $swversion * 10;
}
if($swversion < 7.0) {
// We wait for a ALERT_NEXT_MESSAGE from Funambol clients
Horde::logMessage('SyncML['. session_id()
. "]: Special treatment for Funambol version $swversion activated",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setSyncStatus(CLIENT_SYNC_FINNISHED);
} else {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
}
} else {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
}
}
$this->_clientSentFinal = true; if ($state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
#Horde::logMessage('SyncML['. session_id() .']: Sync _syncTag = '. $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_INFO); $state->setSyncStatus(SERVER_SYNC_ACKNOWLEDGED);
}
break; $this->_clientSentFinal = true;
Horde::logMessage('SyncML['. session_id() .']: syncStatus(server sync acknowledged) '
. $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
break;
default: default:
$this->_currentCmdID = $this->_currentCommand->output($this->_currentCmdID, $this->_output); $this->_currentCmdID = $this->_currentCommand->output($this->_currentCmdID, $this->_output);
break; break;
} }
unset($this->_currentCommand); unset($this->_currentCommand);
break; break;

View File

@ -1,80 +1,166 @@
<?php <?php
include_once 'Horde/SyncML/State.php';
/** /**
* The Horde_SyncML_Command class provides a super class fo SyncBody commands. * eGroupWare - SyncML based on Horde 3
* *
* $Horde: framework/SyncML/SyncML/Command.php,v 1.4 2004/07/03 15:26:46 chuck Exp $ * The SyncML_Command class provides a base class for handling all <SyncBody>
* commands.
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com> * A SyncML command is a protocol primitive. Each SyncML command specifies to
* a recipient an individual operation that is to be performed.
* *
* See the enclosed file COPYING for license information (LGPL). If you * The SyncML_Command objects are hooked into the XML parser of the
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * SyncML_ContentHandler class and are reponsible for parsing a single command
* inside the SyncBody section of a SyncML message. All actions that must be
* executed for a single SyncML command are handled by these objects, by means
* of the handleCommand() method.
* *
* @author Anthony Mills <amills@pyramid6.com> *
* @version $Revision$ * Using the PEAR Log class (which need to be installed!)
* @since Horde 3.0 *
* @package Horde_SyncML * @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Jan Schneider <jan@horde.org>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
include_once 'Horde/SyncML/State_egw.php';
class Horde_SyncML_Command { class Horde_SyncML_Command {
/**
* Name of the command, like 'Put'.
*
* Must be overwritten by a sub class.
*
* @var string
*/
var $_cmdName;
/**
* The command ID (<CmdID>).
*
* @var integer
*/
var $_cmdID; var $_cmdID;
var $_xmlStack; /**
* Stack for holding the XML elements during creation of the object from
* the XML event flow.
*
* @var array
*/
var $_stack = array();
var $_chars; /**
* Buffer for the parsed character data.
*
* @var string
*/
var $_chars = '';
function &factory($command, $params = null) /**
* Start element handler for the XML parser, delegated from
* SyncML_ContentHandler::startElement().
*
* @param string $uri The namespace URI of the element.
* @param string $element The element tag name.
* @param array $attrs A hash with the element's attributes.
*/
function startElement($uri, $element, $attrs)
{ {
include_once 'Horde/SyncML/Command/' . $command . '.php'; $this->_stack[] = $element;
$class = 'Horde_SyncML_Command_' . $command;
if (class_exists($class)) {
return $cmd = new $class($params);
} else {
Horde::logMessage('SyncML: Class definition of ' . $class . ' not found.', __FILE__, __LINE__, PEAR_LOG_ERR);
require_once 'PEAR.php';
return PEAR::raiseError('Class definition of ' . $class . ' not found.');
}
}
function output($currentCmdID, $output)
{
}
function startElement($uri, $localName, $attrs)
{
$this->_xmlStack++;
} }
/**
* End element handler for the XML parser, delegated from
* SyncML_ContentHandler::endElement().
*
* @param string $uri The namespace URI of the element.
* @param string $element The element tag name.
*/
function endElement($uri, $element) function endElement($uri, $element)
{ {
switch ($this->_xmlStack) { if (count($this->_stack) == 2 &&
case 2: $element == 'CmdID') {
if ($element == 'CmdID') { $this->_cmdID = intval(trim($this->_chars));
$this->_cmdID = intval(trim($this->_chars));
}
break;
} }
if (isset($this->_chars)) { if (strlen($this->_chars)) {
unset($this->_chars); $this->_chars = '';
} }
$this->_xmlStack--; array_pop($this->_stack);
} }
/**
* Character data handler for the XML parser, delegated from
* SyncML_ContentHandler::characters().
*
* @param string $str The data string.
*/
function characters($str) function characters($str)
{ {
$tempValue = trim($str);
if(empty($tempValue)) return;
if (isset($this->_chars)) { if (isset($this->_chars)) {
$this->_chars = $this->_chars . $str; $this->_chars .= $str;
} else { } else {
$this->_chars = $str; $this->_chars = $str;
} }
} }
/**
* Returns the command name this instance is reponsible for.
*
* @return string The command name this object is handling.
*/
function getCommandName()
{
return $this->_cmdName;
}
/**
* This method is supposed to implement the actual business logic of the
* command once the XML parsing is complete.
*
* @abstract
*/
function output($currentCmdID, &$output)
{
}
/**
* Attempts to return a concrete Horde_SyncML_Command instance based on
* $command.
*
* @param string $command The type of the concrete
* SyncML_Comment subclass to
* return.
* @param $params Optional Parameter.
*
* @return SyncML_Command The newly created concrete SyncML_Command
* instance, or false on error.
*/
function &factory($command, $params = null)
{
$command = basename($command);
$class = 'Horde_SyncML_Command_' . $command;
if (!class_exists($class)) {
include_once 'Horde/SyncML/Command/' . $command . '.php';
}
if (class_exists($class)) {
$cmd = new $class($params);
} else {
$msg = 'SyncML: Class definition of ' . $class . ' not found.';
Horde::logMessage($msg, __FILE__, __LINE__, PEAR_LOG_ERR);
require_once 'PEAR.php';
$cmd = PEAR::raiseError($msg);
}
return $cmd;
}
} }

View File

@ -1,56 +1,87 @@
<?php <?php
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php';
/** /**
* eGroupWare - SyncML based on Horde 3
*
* The Horde_SyncML_Alert class provides a SyncML implementation of * The Horde_SyncML_Alert class provides a SyncML implementation of
* the Alert command as defined in SyncML Representation Protocol, * the Alert command as defined in SyncML Representation Protocol,
* version 1.1 5.5.2. * version 1.1 5.5.2.
* *
* $Horde: framework/SyncML/SyncML/Command/Alert.php,v 1.18 2004/07/03 15:21:14 chuck Exp $
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com> * Using the PEAR Log class (which need to be installed!)
* *
* See the enclosed file COPYING for license information (LGPL). If you * @link http://www.egroupware.org
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* * @package api
* @author Anthony Mills <amills@pyramid6.com> * @subpackage horde
* @version $Revision$ * @author Anthony Mills <amills@pyramid6.com>
* @since Horde 3.0 * @author Joerg Lehrke <jlehrke@noc.de>
* @package Horde_SyncML * @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
include_once 'Horde/SyncML/State_egw.php';
include_once 'Horde/SyncML/Command.php';
class Horde_SyncML_Command_Alert extends Horde_SyncML_Command { class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
/** /**
* @var integer $_alert * Name of the command.
*
* @var string
*/
var $_cmdName = 'Alert';
/**
* The alert type. Should be one of the ALERT_* constants.
*
* @var integer
*/ */
var $_alert; var $_alert;
/** /**
* @var string $_sourceURI * Source database of the Alert command.
*
* @var string
*/ */
var $_sourceLocURI; var $_sourceLocURI;
/** /**
* @var string $_targetURI * Target database of the Alert command.
*
* @var string
*/ */
var $_targetLocURI; var $_targetLocURI;
/** /**
* @var string $_metaAnchorNext * Optional parameter for the Target database.
*
* @var string
*/
var $_targetLocURIParameters;
/**
* The current time this synchronization happens, from the <Meta><Next>
* element.
*
* @var string
*/ */
var $_metaAnchorNext; var $_metaAnchorNext;
/** /**
* @var integer $_metaAnchorLast * The last time when synchronization happened, from the <Meta><Last>
* element.
*
* @var integer
*/ */
var $_metaAnchorLast; var $_metaAnchorLast;
/** /**
* Use in xml tag. * The filter expression the client provided
* (e.g. the time range for calendar synchronization)
*
* @var string
*/ */
var $_isInSource; var $_filterExpression = '';
/** /**
* Creates a new instance of Alert. * Creates a new instance of Alert.
@ -64,6 +95,8 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
{ {
global $registry;
$attrs = array(); $attrs = array();
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
@ -76,323 +109,389 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
return $currentCmdID; return $currentCmdID;
} }
$type = $this->_targetLocURI;
$clientAnchorNext = $this->_metaAnchorNext;
if($this->_alert < ALERT_RESULT_ALERT) { if ($this->_alert == ALERT_TWO_WAY ||
$this->_alert == ALERT_ONE_WAY_FROM_CLIENT ||
$this->_alert == ALERT_ONE_WAY_FROM_SERVER) {
// Check if we have information about previous sync.
$info = $state->getSyncSummary($this->_targetLocURI);
if (is_a($info, 'DataTreeObject')) {
$x = $info->get('ClientAnchor');
$clientlast = $x[$type];
$x = $info->get('ServerAnchor');
$serverAnchorLast = $x[$type];
} elseif (is_array($info)) {
$clientlast = $info['ClientAnchor'];
$serverAnchorLast = $info['ServerAnchor'];
} else {
$clientlast = false;
$serverAnchorLast = 0;
}
$state->setServerAnchorLast($type, $serverAnchorLast);
$type = $this->_targetLocURI; if ($clientlast !== false){
// Info about previous successful sync sessions found.
Horde::logMessage('SyncML: Previous sync found for target ' . $type
. '; client timestamp: ' . $clientlast,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
// Store client's Next Anchor in State. After successful sync // Check if anchor sent from client matches our own stored
// this is then written to persistence for negotiation of // data.
// further syncs. if ($clientlast == $this->_metaAnchorLast) {
$state->setClientAnchorNext($type, $this->_metaAnchorNext); // Last sync anchors matche, TwoWaySync will do.
$anchormatch = true;
Horde::logMessage('SyncML: Anchor timestamps match, TwoWaySync possible. Syncing data since '
. date('Y-m-d H:i:s', $serverAnchorLast),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
// Server and client have different anchors, enforce
// SlowSync/RefreshSync
Horde::logMessage('SyncML: Client requested sync with anchor timestamp '
. $this->_metaAnchorLast
. ' but server has recorded timestamp '
. $clientlast . '. Enforcing SlowSync',
__FILE__, __LINE__, PEAR_LOG_INFO);
$anchormatch = false;
$clientlast = 0;
}
} else {
// No info about previous sync, use SlowSync or RefreshSync.
Horde::logMessage('SyncML: No info about previous syncs found for device ' .
$state->getSourceURI() . ' and target ' . $type,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$clientlast = 0;
$serverAnchorLast = 0;
$anchormatch = false;
}
} else {
// SlowSync requested, no anchor check required.
$anchormatch = true;
}
$info = $state->getSyncSummary($this->_targetLocURI); // Determine sync type and status response code.
#Horde::logMessage("SyncML: Anchor match, TwoWaySync sinceee " . $clientlast, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: Alert " . $this->_alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (is_a($info, 'DataTreeObject')) { switch ($this->_alert) {
$x = $info->get('ClientAnchor'); case ALERT_NEXT_MESSAGE:
$clientlast = $x[$type]; $state->setAlert222Received(true);
$x = $info->get('ServerAnchor'); case ALERT_RESULT_ALERT:
$state->setServerAnchorLast($type, $x[$type]); case ALERT_NO_END_OF_DATA:
} elseif (is_array($info)) { // Nothing to do on our side
$clientlast = $info['ClientAnchor']; $status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert');
$state->setServerAnchorLast($type, $info['ServerAnchor']); $status->setCmdRef($this->_cmdID);
} else { if ($this->_sourceLocURI != null) {
$clientlast = false; $status->setSourceRef($this->_sourceLocURI);
$state->setServerAnchorLast($type, 0); }
} if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
Horde::logMessage('SyncML: checking anchor targetLocURI: clientlast: ' . $clientlast .' / '. $this->_metaAnchorLast, __FILE__, __LINE__, PEAR_LOG_DEBUG); }
if ($this->_alert == ALERT_NEXT_MESSAGE) {
// Set Server Anchor for this sync to current time. if ($this->_sourceLocURI != null) {
$state->setServerAnchorNext($type,time()); $status->setItemSourceLocURI($this->_sourceLocURI);
if ($clientlast !== false && $clientlast == $this->_metaAnchorLast) { }
// Last Sync Anchor matches, TwoWaySync will do. if ($this->_targetLocURI != null) {
$code = RESPONSE_OK; $status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
Horde::logMessage("SyncML: Anchor match, TwoWaySync since " . $clientlast, __FILE__, __LINE__, PEAR_LOG_DEBUG); }
} else { }
Horde::logMessage("SyncML: Anchor mismatch, enforcing SlowSync clientlast $clientlast serverlast ".$this->_metaAnchorLast, __FILE__, __LINE__, PEAR_LOG_DEBUG); $currentCmdID = $status->output($currentCmdID, $output);
// Mismatch, enforce slow sync. 508=RESPONSE_REFRESH_REQUIRED 201=ALERT_SLOW_SYNC return $currentCmdID;
$this->_alert = 201; case ALERT_TWO_WAY:
$code = 508; if ($anchormatch) {
// create new synctype $synctype = ALERT_TWO_WAY;
$sync = &Horde_SyncML_Sync::factory($this->_alert); $response = RESPONSE_OK;
$sync->_targetLocURI = $this->_targetLocURI; } else {
$sync->_sourceLocURI = $this->_sourceLocURI; $synctype = ALERT_SLOW_SYNC;
if(isset($this->_targetLocURIParameters)) $response = RESPONSE_REFRESH_REQUIRED;
$sync->_targetLocURIParameters = $this->_targetLocURIParameters; }
$state->setSync($this->_targetLocURI, $sync); break;
// PH : no longer delete entire content_map entries for client before SlowSync,
// use content_map to verify if content is mapped correctly
//$state->removeAllUID($this->_targetLocURI);
}
$status = new Horde_SyncML_Command_Status($code, 'Alert'); case ALERT_SLOW_SYNC:
$status->setCmdRef($this->_cmdID); $synctype = ALERT_SLOW_SYNC;
if ($this->_sourceLocURI != null) { $response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
$status->setSourceRef($this->_sourceLocURI); break;
}
if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
}
// Mirror Next Anchor from client back to client. case ALERT_ONE_WAY_FROM_CLIENT:
if (isset($this->_metaAnchorNext)) { if ($anchormatch) {
$status->setItemDataAnchorNext($this->_metaAnchorNext); $synctype = ALERT_ONE_WAY_FROM_CLIENT;
} $response = RESPONSE_OK;
} else {
$synctype = ALERT_REFRESH_FROM_CLIENT;
$response = RESPONSE_REFRESH_REQUIRED;
}
break;
// Mirror Last Anchor from client back to client. case ALERT_REFRESH_FROM_CLIENT:
if (isset($this->_metaAnchorLast)) { $synctype = ALERT_REFRESH_FROM_CLIENT;
$status->setItemDataAnchorLast($this->_metaAnchorLast); $response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
}
$currentCmdID = $status->output($currentCmdID, $output); // We will erase the current server content,
// then we can add the client's contents.
if ($state->isAuthorized()) { $hordeType = $state->getHordeType($this->_targetLocURI);
$output->startElement($state->getURI(), 'Alert', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs); $state->setTargetURI($this->_targetLocURI);
$chars = $currentCmdID; $deletes = $state->getClientItems();
$output->characters($chars); if (is_array($deletes)) {
$output->endElement($state->getURI(), 'CmdID'); foreach ($deletes as $delete) {
$registry->call($hordeType . '/delete', array($delete));
}
Horde::logMessage("SyncML: RefreshFromClient " . count($deletes) . " entries deleted for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
$anchormatch = false;
break;
$output->startElement($state->getURI(), 'Data', $attrs); case ALERT_ONE_WAY_FROM_SERVER:
$chars = $this->_alert; if ($anchormatch) {
$output->characters($chars); $synctype = ALERT_ONE_WAY_FROM_SERVER;
$output->endElement($state->getURI(), 'Data'); $response = RESPONSE_OK;
} else {
$synctype = ALERT_REFRESH_FROM_SERVER;
$response = RESPONSE_REFRESH_REQUIRED;
}
break;
$output->startElement($state->getURI(), 'Item', $attrs); case ALERT_REFRESH_FROM_SERVER:
$synctype = ALERT_REFRESH_FROM_SERVER;
$response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
break;
if ($this->_sourceLocURI != null) { case ALERT_RESUME:
$output->startElement($state->getURI(), 'Target', $attrs); // @TODO: Suspend and Resume is not supported yet
$output->startElement($state->getURI(), 'LocURI', $attrs); $synctype = ALERT_SLOW_SYNC;
$chars = $this->_sourceLocURI; $response = RESPONSE_REFRESH_REQUIRED;
$output->characters($chars); break;
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
}
if ($this->_targetLocURI != null) {
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = (isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
}
$output->startElement($state->getURI(), 'Meta', $attrs); default:
// We can't handle this one
Horde::logMessage('SyncML: Unknown sync type ' . $this->_alert,
__FILE__, __LINE__, PEAR_LOG_ERR);
$status = new Horde_SyncML_Command_Status(RESPONSE_BAD_REQUEST, 'Alert');
$status->setCmdRef($this->_cmdID);
if ($this->_sourceLocURI != null) {
$status->setSourceRef($this->_sourceLocURI);
}
if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
}
$currentCmdID = $status->output($currentCmdID, $output);
return $currentCmdID;
}
$output->startElement($state->getURIMeta(), 'Anchor', $attrs); // Store client's Next Anchor in State and
// set server's Next Anchor. After successful sync
// this is then written to persistence for negotiation of
// further syncs.
$state->setClientAnchorNext($type, $this->_metaAnchorNext);
$serverAnchorNext = time();
$state->setServerAnchorNext($type, $serverAnchorNext);
$output->startElement($state->getURIMeta(), 'Last', $attrs); // Now set interval to retrieve server changes from, defined by
$chars = $state->getServerAnchorLast($type); // ServerAnchor [Last,Next]
$output->characters($chars); if ($synctype != ALERT_TWO_WAY &&
$output->endElement($state->getURIMeta(), 'Last'); $synctype != ALERT_ONE_WAY_FROM_CLIENT &&
$synctype != ALERT_ONE_WAY_FROM_SERVER) {
$serverAnchorLast = 0;
#if (!$anchormatch) {
// Erase existing map:
$state->removeAllUID($this->_targetLocURI);
#}
}
// Now create the actual SyncML_Sync object, if it doesn't exist yet.
$sync = &$state->getSync($this->_targetLocURI);
if (!$sync) {
Horde::logMessage('SyncML: Creating SyncML_Sync object for target '
. $this->_targetLocURI . '; sync type ' . $synctype,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$sync = &Horde_SyncML_Sync::factory($synctype);
$state->clearConflictItems($this->_targetLocURI);
}
$sync->setTargetLocURI($this->_targetLocURI);
$sync->setSourceLocURI($this->_sourceLocURI);
$sync->setLocName($state->getLocName()); // We need it for conflict handling
$sync->setsyncType($synctype);
$sync->setFilterExpression($this->_filterExpression);
$state->setSync($this->_targetLocURI, $sync);
$output->startElement($state->getURIMeta(), 'Next', $attrs); $status = new Horde_SyncML_Command_Status($response, 'Alert');
$chars = $state->getServerAnchorNext($type); $status->setCmdRef($this->_cmdID);
$output->characters($chars); if ($this->_sourceLocURI != null) {
$output->endElement($state->getURIMeta(), 'Next'); $status->setSourceRef($this->_sourceLocURI);
}
if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
}
$output->endElement($state->getURIMeta(), 'Anchor'); // Mirror Next Anchor from client back to client.
$output->endElement($state->getURI(), 'Meta'); if (isset($this->_metaAnchorNext)) {
$output->endElement($state->getURI(), 'Item'); $status->setItemDataAnchorNext($this->_metaAnchorNext);
$output->endElement($state->getURI(), 'Alert'); }
// still needed? lars // Mirror Last Anchor from client back to client.
$state->_sendFinal = true; if (isset($this->_metaAnchorLast)) {
$status->setItemDataAnchorLast($this->_metaAnchorLast);
$currentCmdID++; }
if($state->_devinfoRequested == false && $currentCmdID = $status->output($currentCmdID, $output);
$this->_sourceLocURI != null &&
is_a($state->getPreferedContentTypeClient($this->_sourceLocURI), 'PEAR_Error')) {
$output->startElement($state->getURI(), 'Get', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs); $output->startElement($state->getURI(), 'Alert', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Type', $attrs);
if(is_a($output, 'XML_WBXML_Encoder')) {
$output->characters('application/vnd.syncml-devinf+wbxml');
} else {
$output->characters('application/vnd.syncml-devinf+xml');
}
$output->endElement($state->getURIMeta(), 'Type');
$output->endElement($state->getURI(), 'Meta');
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$output->characters(($state->getVersion() == 0) ? './devinf10' : './devinf11');
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->endElement($state->getURI(), 'Item');
$output->endElement($state->getURI(), 'Get');
$state->_devinfoRequested = true;
}
}
} elseif ($this->_alert == ALERT_NEXT_MESSAGE) { $output->startElement($state->getURI(), 'CmdID', $attrs);
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert'); $chars = $currentCmdID;
$status->setCmdRef($this->_cmdID); $output->characters($chars);
if ($this->_targetLocURI != null) { $output->endElement($state->getURI(), 'CmdID');
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
}
if ($this->_sourceLocURI != null) {
$status->setSourceRef($this->_sourceLocURI);
}
$status->setItemSourceLocURI($this->_sourceLocURI);
$status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
$currentCmdID = $status->output($currentCmdID, $output);
$state->setAlert222Received(true); $output->startElement($state->getURI(), 'Data', $attrs);
$chars = $synctype;
$output->characters($chars);
$output->endElement($state->getURI(), 'Data');
} else { $output->startElement($state->getURI(), 'Item', $attrs);
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert');
$status->setCmdRef($this->_cmdID);
if ($this->_sourceLocURI != null) {
$status->setSourceRef($this->_sourceLocURI);
}
if ($this->_targetLocURI != null) {
$status->setTargetRef((isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI));
}
$currentCmdID = $status->output($currentCmdID, $output); if ($this->_sourceLocURI != null) {
} $output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $this->_sourceLocURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
}
if ($this->_targetLocURI != null) {
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = (isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
}
$output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Anchor', $attrs);
$output->startElement($state->getURIMeta(), 'Last', $attrs);
$chars = $state->getServerAnchorLast($type);
$output->characters($chars);
$output->endElement($state->getURIMeta(), 'Last');
$output->startElement($state->getURIMeta(), 'Next', $attrs);
$chars = $state->getServerAnchorNext($type);
$output->characters($chars);
$output->endElement($state->getURIMeta(), 'Next');
$output->endElement($state->getURIMeta(), 'Anchor');
$output->endElement($state->getURI(), 'Meta');
$output->endElement($state->getURI(), 'Item');
$output->endElement($state->getURI(), 'Alert');
// Final packet of this message
$state->_sendFinal = true;
$currentCmdID++;
if ($state->_devinfoRequested == false &&
$this->_sourceLocURI != null &&
is_a($state->getPreferedContentTypeClient($this->_sourceLocURI), 'PEAR_Error')) {
Horde::logMessage("SyncML: PreferedContentTypeClient missing, sending <Get>", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$output->startElement($state->getURI(), 'Get', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Type', $attrs);
if (is_a($output, 'XML_WBXML_Encoder')) {
$output->characters('application/vnd.syncml-devinf+wbxml');
} else {
$output->characters('application/vnd.syncml-devinf+xml');
}
$output->endElement($state->getURIMeta(), 'Type');
$output->endElement($state->getURI(), 'Meta');
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
if ($state->getVersion() == 2) {
$output->characters('./devinf12');
} elseif ($state->getVersion() == 1) {
$output->characters('./devinf11');
} else {
$output->characters('./devinf10');
}
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->endElement($state->getURI(), 'Item');
$output->endElement($state->getURI(), 'Get');
$state->_devinfoRequested = true;
}
return $currentCmdID; return $currentCmdID;
} }
/** /**
* Setter for property sourceURI. * End element handler for the XML parser, delegated from
* SyncML_ContentHandler::endElement().
* *
* @param string $sourceURI New value of property sourceURI. * @param string $uri The namespace URI of the element.
* @param string $element The element tag name.
*/ */
function setSourceLocURI($sourceURI) function endElement($uri, $element)
{ {
$this->_sourceLocURI = $sourceURI; switch (count($this->_stack)) {
} case 2:
if ($element == 'Data') {
function getTargetLocURI() $this->_alert = intval(trim($this->_chars));
{
return $this->_targetURI;
}
/**
* Setter for property targetURI.
*
* @param string $targetURI New value of property targetURI.
*/
// is this function still used???
function setTargetURI($targetURI)
{
$this->_targetLocURI = $targetURI;
}
/**
* Setter for property targetURI.
*
* @param string $targetURI New value of property targetURI.
*/
function setTargetLocURI($targetURI)
{
$this->_targetLocURI = $targetURI;
}
function startElement($uri, $element, $attrs)
{
parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) {
case 3:
if ($element == 'Target') {
$this->_isInSource = false;
} else {
$this->_isInSource = true;
} }
break; break;
case 4:
if ($element == 'LocURI') {
switch ($this->_stack[2]) {
case 'Source':
$this->_sourceLocURI = trim($this->_chars);
break;
case 'Target':
$targetLocURIData = explode('?/',trim($this->_chars));
$this->_targetLocURI = $targetLocURIData[0];
if (isset($targetLocURIData[1])) {
$this->_targetLocURIParameters = $targetLocURIData[1];
}
break;
}
}
break;
case 5:
switch ($element) {
case 'Next':
$this->_metaAnchorNext = trim($this->_chars);
break;
case 'Last':
$this->_metaAnchorLast = trim($this->_chars);
break;
}
break;
case 7:
if ($element == 'Data'
&& $this->_stack[2] == 'Target'
&& $this->_stack[3] == 'Filter'
&& $this->_stack[4] == 'Record'
&& $this->_stack[5] == 'Item') {
$this->_filterExpression = trim($this->_chars);
}
break;
} }
} parent::endElement($uri, $element);
function endElement($uri, $element)
{
switch ($this->_xmlStack) {
case 1:
$state = & $_SESSION['SyncML.state'];
$sync = $state->getSync($this->_targetLocURI);
if (!$sync && $this->_alert < ALERT_RESULT_ALERT) {
Horde::logMessage('SyncML: create new sync for ' . $this->_targetLocURI . ' ' . $this->_alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$sync = &Horde_SyncML_Sync::factory($this->_alert);
$sync->_targetLocURI = $this->_targetLocURI;
$sync->_sourceLocURI = $this->_sourceLocURI;
if(isset($this->_targetLocURIParameters)) {
$sync->_targetLocURIParameters = $this->_targetLocURIParameters;
}
$state->setSync($this->_targetLocURI, $sync);
if($this->_alert == ALERT_SLOW_SYNC || $this->_alert == ALERT_REFRESH_FROM_SERVER) {
$state->removeAllUID($this->_targetLocURI);
}
}
break;
case 2:
if ($element == 'Data') {
$this->_alert = intval(trim($this->_chars));
}
break;
case 4:
if ($element == 'LocURI') {
if ($this->_isInSource) {
$this->_sourceLocURI = trim($this->_chars);
} else {
$targetLocURIData = explode('?/',trim($this->_chars));
$this->_targetLocURI = $targetLocURIData[0];
if(isset($targetLocURIData[1])) {
$this->_targetLocURIParameters = $targetLocURIData[1];
}
}
}
break;
case 5:
if ($element == 'Next') {
$this->_metaAnchorNext = trim($this->_chars);
} else if ($element == 'Last') {
$this->_metaAnchorLast = trim($this->_chars);
}
break;
}
parent::endElement($uri, $element);
}
function getAlert()
{
return $this->_alert;
}
function setAlert($alert)
{
$this->_alert = $alert;
} }
} }

View File

@ -1,24 +1,31 @@
<?php <?php
include_once 'Horde/SyncML/Command.php';
/** /**
* eGroupWare - SyncML based on Horde 3
*
* The Horde_SyncML_Command_Final class. * The Horde_SyncML_Command_Final class.
* *
* $Horde: framework/SyncML/SyncML/Command/Final.php,v 1.10 2004/05/26 17:41:30 chuck Exp $
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com> * Using the PEAR Log class (which need to be installed!)
* *
* See the enclosed file COPYING for license information (LGPL). If you * @link http://www.egroupware.org
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* * @package api
* @author Anthony Mills <amills@pyramid6.com> * @subpackage horde
* @version $Revision$ * @author Anthony Mills <amills@pyramid6.com>
* @since Horde 3.0 * @copyright (c) The Horde Project (http://www.horde.org/)
* @package Horde_SyncML * @version $Id$
*/ */
include_once 'Horde/SyncML/Command.php';
class Horde_SyncML_Command_Final extends Horde_SyncML_Command { class Horde_SyncML_Command_Final extends Horde_SyncML_Command {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Final';
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
{ {
$state = $_SESSION['SyncML.state']; $state = $_SESSION['SyncML.state'];

View File

@ -1,32 +1,37 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/State.php'; include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
include_once 'Horde/SyncML/Command/Results.php'; include_once 'Horde/SyncML/Command/Results.php';
/**
* The Horde_SyncML_Command_Get class.
*
* $Horde: framework/SyncML/SyncML/Command/Get.php,v 1.14 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Get extends Horde_SyncML_Command { class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
{ {
$state = $_SESSION['SyncML.state']; $state = $_SESSION['SyncML.state'];
$ref = ($state->getVersion() == 0) ? './devinf10' : './devinf11'; if ($state->getVersion() == 2) {
$ref = './devinf12';
} elseif ($state->getVersion() == 1) {
$ref = './devinf11';
} else {
$ref = './devinf10';
}
$status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), 'Get'); $status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), 'Get');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
@ -74,7 +79,13 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
$output->startElement($state->getURIDevInf() , 'DevInf', $attrs); $output->startElement($state->getURIDevInf() , 'DevInf', $attrs);
$output->startElement($state->getURIDevInf() , 'VerDTD', $attrs); $output->startElement($state->getURIDevInf() , 'VerDTD', $attrs);
$output->characters(($state->getVersion() == 0) ? '1.0' : '1.1'); if ($state->getVersion() == 2) {
$output->characters('1.2');
} elseif($state->getVersion() == 1) {
$output->characters('1.1');
} else {
$output->characters('1.0');
}
$output->endElement($state->getURIDevInf() , 'VerDTD', $attrs); $output->endElement($state->getURIDevInf() , 'VerDTD', $attrs);
$output->startElement($state->getURIDevInf() , 'Man', $attrs); $output->startElement($state->getURIDevInf() , 'Man', $attrs);
$output->characters('www.egroupware.org'); $output->characters('www.egroupware.org');
@ -85,12 +96,22 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
$output->startElement($state->getURIDevInf() , 'DevTyp', $attrs); $output->startElement($state->getURIDevInf() , 'DevTyp', $attrs);
$output->characters('server'); $output->characters('server');
$output->endElement($state->getURIDevInf() , 'DevTyp', $attrs); $output->endElement($state->getURIDevInf() , 'DevTyp', $attrs);
$output->startElement($state->getURIDevInf() , 'UTC', $attrs);
$output->endElement($state->getURIDevInf() , 'UTC', $attrs);
$output->startElement($state->getURIDevInf() , 'SupportNumberOfChanges', $attrs);
$output->endElement($state->getURIDevInf() , 'SupportNumberOfChanges', $attrs);
$output->startElement($state->getURIDevInf() , 'SupportLargeObjs', $attrs);
$output->endElement($state->getURIDevInf() , 'SupportLargeObjs', $attrs);
$this->_writeDataStore('./notes', 'text/x-vnote', '1.1', $output, $this->_writeDataStore('./notes', 'text/x-vnote', '1.1', $output,
array('text/plain' => '1.0')); array('text/plain' => '1.0'));
$this->_writeDataStore('./contacts', 'text/x-vcard', '2.1', $output); $this->_writeDataStore('./contacts', 'text/vcard', '3.0', $output,
$this->_writeDataStore('./tasks', 'text/x-vcalendar', '1.0', $output); array('text/x-vcard' => '2.1'));
$this->_writeDataStore('./calendar', 'text/x-vcalendar', '1.0', $output); $this->_writeDataStore('./tasks', 'text/calendar', '2.0', $output,
$this->_writeDataStore('./caltasks', 'text/x-vcalendar', '1.0', $output); array('text/x-vcalendar' => '1.0'));
$this->_writeDataStore('./calendar', 'text/calendar', '2.0', $output,
array('text/x-vcalendar' => '1.0'));
$this->_writeDataStore('./caltasks', 'text/calendar', '2.0', $output,
array('text/x-vcalendar' => '1.0'));
$output->endElement($state->getURIDevInf() , 'DevInf', $attrs); $output->endElement($state->getURIDevInf() , 'DevInf', $attrs);
$output->endElement($state->getURI(), 'Data'); $output->endElement($state->getURI(), 'Data');
@ -111,7 +132,7 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
* @param string $version: data for &lt;(R|T)x-Pref&gt;&lt;VerCT&gt; * @param string $version: data for &lt;(R|T)x-Pref&gt;&lt;VerCT&gt;
* @param string &$output contenthandler that will received the output. * @param string &$output contenthandler that will received the output.
* @param array $additionaltypes: array of additional types for Tx and Rx; * @param array $additionaltypes: array of additional types for Tx and Rx;
* format array('text/vcard' => '2.0') * format array('text/vcard' => '3.0')
*/ */
function _writeDataStore($sourceref, $mimetype, $version, &$output, function _writeDataStore($sourceref, $mimetype, $version, &$output,
$additionaltypes = false) $additionaltypes = false)
@ -124,6 +145,9 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
$output->startElement($state->getURIDevInf() , 'SourceRef', $attrs); $output->startElement($state->getURIDevInf() , 'SourceRef', $attrs);
$output->characters($sourceref); $output->characters($sourceref);
$output->endElement($state->getURIDevInf() , 'SourceRef', $attrs); $output->endElement($state->getURIDevInf() , 'SourceRef', $attrs);
$output->startElement($state->getURIDevInf() , 'MaxGUIDSize', $attrs);
$output->characters(255);
$output->endElement($state->getURIDevInf() , 'MaxGUIDSize', $attrs);
$output->startElement($state->getURIDevInf() , 'Rx-Pref', $attrs); $output->startElement($state->getURIDevInf() , 'Rx-Pref', $attrs);
$output->startElement($state->getURIDevInf() , 'CTType', $attrs); $output->startElement($state->getURIDevInf() , 'CTType', $attrs);
@ -170,12 +194,13 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
} }
$output->startElement($state->getURIDevInf() , 'SyncCap', $attrs); $output->startElement($state->getURIDevInf() , 'SyncCap', $attrs);
$output->startElement($state->getURIDevInf() , 'SyncType', $attrs); // We support all sync Types from 1-6: two way, slow, refresh|update
$output->characters('1'); // from client|server
$output->endElement($state->getURIDevInf() , 'SyncType', $attrs); for ($i = 1; $i <= 6; ++$i) {
$output->startElement($state->getURIDevInf() , 'SyncType', $attrs); $output->startElement($state->getURIDevInf(), 'SyncType', $attrs);
$output->characters('2'); $output->characters($i);
$output->endElement($state->getURIDevInf() , 'SyncType', $attrs); $output->endElement($state->getURIDevInf(), 'SyncType', $attrs);
}
$output->endElement($state->getURIDevInf() , 'SyncCap', $attrs); $output->endElement($state->getURIDevInf() , 'SyncCap', $attrs);
$output->endElement($state->getURIDevInf() , 'DataStore', $attrs); $output->endElement($state->getURIDevInf() , 'DataStore', $attrs);
} }

View File

@ -1,43 +1,60 @@
<?php <?php
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php';
/** /**
* eGroupWare - SyncML based on Horde 3
*
* The Horde_SyncML_Map class provides a SyncML implementation of * The Horde_SyncML_Map class provides a SyncML implementation of
* the Map command as defined in SyncML Representation Protocol, * the Map command as defined in SyncML Representation Protocol,
* version 1.0.1 5.5.8. * version 1.0.1 5.5.8.
* *
* $Horde: framework/SyncML/SyncML/Command/Map.php,v 1.1 2004/07/02 19:24:44 chuck Exp $
* *
* Copyright 2004 Karsten Fourmont <fourmont@gmx.de> * Using the PEAR Log class (which need to be installed!)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Karsten Fourmont <fourmont@gmx.de> * @author Karsten Fourmont <fourmont@gmx.de>
* @version $Revision$ * @copyright (c) The Horde Project (http://www.horde.org/)
* @since Horde 3.0 * @version $Id$
* @package Horde_SyncML
*/ */
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php';
class Horde_SyncML_Command_Map extends Horde_SyncML_Command { class Horde_SyncML_Command_Map extends Horde_SyncML_Command {
/** /**
* @var string $_sourceURI * Name of the command.
*
* @var string
*/
var $_cmdName = 'Map';
/**
* Source database of the Map command.
*
* @var string
*/ */
var $_sourceLocURI; var $_sourceLocURI;
/** /**
* @var string $_targetURI * Target database of the Map command.
*
* @var string
*/ */
var $_targetLocURI; var $_targetLocURI;
/** /**
* Use in xml tag. * Recipient map item specifier.
*
* @var string
*/ */
var $_isInSource;
var $_mapTarget; var $_mapTarget;
/**
* Originator map item specifier.
*
* @var string
*/
var $_mapSource; var $_mapSource;
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
@ -60,76 +77,25 @@ class Horde_SyncML_Command_Map extends Horde_SyncML_Command {
return $currentCmdID; return $currentCmdID;
} }
/**
* Setter for property sourceURI.
*
* @param string $sourceURI New value of property sourceURI.
*/
function setSourceLocURI($sourceURI)
{
$this->_sourceURI = $sourceURI;
}
function getTargetLocURI()
{
return $this->_targetURI;
}
/**
* Setter for property targetURI.
*
* @param string $targetURI New value of property targetURI.
*/
function setTargetURI($targetURI)
{
$this->_targetURI = $targetURI;
}
function startElement($uri, $element, $attrs) function startElement($uri, $element, $attrs)
{ {
parent::startElement($uri, $element, $attrs); parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) { if (count($this->_stack) == 2 &&
case 2: $element == 'MapItem') {
if ($element == 'Target') { unset($this->_mapTarget);
$this->_isInSource = false; unset($this->_mapSource);
}
if ($element == 'Source') {
$this->_isInSource = true;
}
if ($element == 'MapItem') {
unset($this->_mapTarget);
unset($this->_mapSource);
}
break;
case 3:
if ($element == 'Target') {
$this->_isInSource = false;
}
if ($element == 'Source') {
$this->_isInSource = true;
}
break;
} }
} }
function endElement($uri, $element) function endElement($uri, $element)
{ {
switch ($this->_xmlStack) {
case 1:
$state = $_SESSION['SyncML.state'];
$sync = $state->getSync($this->_targetLocURI);
if (!$sync) { $state = &$_SESSION['SyncML.state'];
}
$_SESSION['SyncML.state'] = $state;
break;
switch (count($this->_stack)) {
case 2: case 2:
if ($element == 'MapItem') { if ($element == 'MapItem') {
$state = $_SESSION['SyncML.state'];
$sync = $state->getSync($this->_targetLocURI); $sync = $state->getSync($this->_targetLocURI);
if (!$state->isAuthorized()) { if (!$state->isAuthorized()) {
Horde::logMessage('SyncML: Not Authorized in the middle of MapItem!', __FILE__, __LINE__, PEAR_LOG_ERR); Horde::logMessage('SyncML: Not Authorized in the middle of MapItem!', __FILE__, __LINE__, PEAR_LOG_ERR);
@ -149,26 +115,20 @@ class Horde_SyncML_Command_Map extends Horde_SyncML_Command {
case 3: case 3:
if ($element == 'LocURI') { if ($element == 'LocURI') {
if ($this->_isInSource) { if ($this->_stack[1] == 'Source') {
$this->_sourceLocURI = trim($this->_chars); $this->_sourceLocURI = trim($this->_chars);
} else { } elseif ($this->_stack[1] == 'Target') {
$targetLocURIData = explode('?/',trim($this->_chars)); $targetLocURIData = explode('?/',trim($this->_chars));
$this->_targetLocURI = $targetLocURIData[0];
$this->_targetLocURI = $targetLocURIData[0];
if(isset($targetLocURIData[1]))
{
$this->_targetLocURIParameters = $targetLocURIData[1];
}
} }
} }
break; break;
case 4: case 4:
if ($element == 'LocURI') { if ($element == 'LocURI') {
if ($this->_isInSource) { if ($this->_stack[2] == 'Source') {
$this->_mapSource = trim($this->_chars); $this->_mapSource = trim($this->_chars);
} else { } elseif ($this->_stack[2] == 'Target') {
$this->_mapTarget = trim($this->_chars); $this->_mapTarget = trim($this->_chars);
} }
} }

View File

@ -1,23 +1,31 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/State.php'; include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Put.php,v 1.12 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Put extends Horde_SyncML_Command { class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Put';
/** /**
* @var string $_manufacturer * @var string $_manufacturer
*/ */
@ -36,6 +44,12 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
var $_oem; var $_oem;
/**
* @var array $_deviceInfo
*/
var $_deviceInfo;
/** /**
* @var string $_softwareVersion * @var string $_softwareVersion
*/ */
@ -43,62 +57,63 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
var $_softwareVersion; var $_softwareVersion;
function endElement($uri, $element) { function endElement($uri, $element) {
switch ($this->_xmlStack) { switch (count($this->_stack)) {
case 5: case 5:
switch($element) { switch ($element) {
case 'DataStore': case 'DataStore':
$this->_deviceInfo['dataStore'][$this->_sourceReference] = array( $this->_deviceInfo['dataStore'][$this->_sourceReference] = array (
'maxGUIDSize' => $this->_maxGUIDSize, 'maxGUIDSize' => $this->_maxGUIDSize,
'rxPreference' => $this->_rxPreference, 'rxPreference' => $this->_rxPreference,
'txPreference' => $this->_txPreference, 'txPreference' => $this->_txPreference,
'syncCapabilities' => $this->_syncCapabilities, 'syncCapabilities' => $this->_syncCapabilities,
'properties' => $this->_properties,
); );
break; break;
case 'DevID': case 'DevID':
$this->_deviceInfo['deviceID'] = trim($this->_chars); $this->_deviceInfo['deviceID'] = trim($this->_chars);
break; break;
case 'DevTyp': case 'DevTyp':
$this->_deviceInfo['deviceType'] = trim($this->_chars); $this->_deviceInfo['deviceType'] = trim($this->_chars);
break; break;
case 'FwV': case 'FwV':
$this->_deviceInfo['firmwareVersion'] = trim($this->_chars); $this->_deviceInfo['firmwareVersion'] = trim($this->_chars);
break; break;
case 'HwV': case 'HwV':
$this->_deviceInfo['hardwareVersion'] = trim($this->_chars); $this->_deviceInfo['hardwareVersion'] = trim($this->_chars);
break; break;
case 'Man': case 'Man':
$this->_deviceInfo['manufacturer'] = trim($this->_chars); $this->_deviceInfo['manufacturer'] = trim($this->_chars);
break; break;
case 'Mod': case 'Mod':
$this->_deviceInfo['model'] = trim($this->_chars); $this->_deviceInfo['model'] = trim($this->_chars);
break; break;
case 'OEM': case 'OEM':
$this->_deviceInfo['oem'] = trim($this->_chars); $this->_deviceInfo['oem'] = trim($this->_chars);
break; break;
case 'SwV': case 'SwV':
$this->_deviceInfo['softwareVersion'] = trim($this->_chars); $this->_deviceInfo['softwareVersion'] = trim($this->_chars);
break; break;
case 'SupportLargeObjs': case 'SupportLargeObjs':
$this->_deviceInfo['supportLargeObjs'] = true; $this->_deviceInfo['supportLargeObjs'] = true;
break; break;
case 'SupportNumberOfChanges': case 'SupportNumberOfChanges':
$this->_deviceInfo['supportNumberOfChanges'] = true; $this->_deviceInfo['supportNumberOfChanges'] = true;
break; break;
case 'UTC': case 'UTC':
$this->_deviceInfo['UTC'] = true; $this->_deviceInfo['UTC'] = true;
break; break;
case 'VerDTD': case 'VerDTD':
$this->_deviceInfo['DTDVersion'] = trim($this->_chars); $this->_deviceInfo['DTDVersion'] = trim($this->_chars);
break; break;
@ -109,18 +124,18 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
case 'MaxGUIDSize': case 'MaxGUIDSize':
$this->_maxGUIDSize = trim($this->_chars); $this->_maxGUIDSize = trim($this->_chars);
break; break;
case 'Rx-Pref': case 'Rx-Pref':
$this->_rxPreference = array( $this->_rxPreference = array(
'contentType' => $this->_contentType, 'contentType' => $this->_contentType,
'contentVersion' => $this->_contentVersion, 'contentVersion' => $this->_contentVersion,
); );
break; break;
case 'SourceRef': case 'SourceRef':
$this->_sourceReference = trim($this->_chars); $this->_sourceReference = trim($this->_chars);
break; break;
case 'Tx-Pref': case 'Tx-Pref':
$this->_txPreference = array( $this->_txPreference = array(
'contentType' => $this->_contentType, 'contentType' => $this->_contentType,
@ -129,7 +144,7 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
break; break;
} }
break; break;
case 7: case 7:
switch($element) { switch($element) {
case 'CTType': case 'CTType':
@ -178,87 +193,165 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
} }
} }
break; break;
case 'SyncType': case 'SyncType':
$this->_syncCapabilities[] = trim($this->_chars); $this->_syncCapabilities[] = trim($this->_chars);
break; break;
case 'VerCT': case 'VerCT':
$this->_contentVersion = trim($this->_chars); $this->_contentVersion = trim($this->_chars);
break; break;
case 'Property':
if (isset($this->_PropName)) {
$this->_properties[$this->_contentType][$this->_contentVersion][$this->_PropName] = array(
'Size' => $this->_PropSize,
'NoTruncate' => $this->_PropNoTruncate,
);
}
break;
} }
break; break;
case 8:
switch($element) {
case 'PropName':
$this->_PropName = trim($this->_chars);
break;
case 'Size':
$this->_PropSize = trim($this->_chars);
break;
case 'NoTruncate':
$this->_PropNoTruncate = true;
break;
}
beak;
} }
parent::endElement($uri, $element); parent::endElement($uri, $element);
} }
function finalizeDeviceInfo() function finalizeDeviceInfo()
{ {
// get some more information about the device from out of band data // get some more information about the device from out of band data
$ua = $_SERVER['HTTP_USER_AGENT']; $ua = $_SERVER['HTTP_USER_AGENT'];
if (preg_match("/^\s*Funambol (.*) (\d+\.\d+\.\d+)\s*$/i", $ua, $matches)) if (preg_match("/^\s*Funambol (.*) [^\d]*(\d+\.?\d*)[\.|\d]*\s*$/i", $ua, $matches)) {
{ // Funambol uses the hardware Manufacturer we don't care about
if (!isset($this->_deviceInfo['manufacturer'])) $this->_deviceInfo['manufacturer'] = 'Funambol';
$this->_deviceInfo['manufacturer'] = 'Funambol'; $this->_deviceInfo['model'] = trim($matches[1]);
if (!isset($this->_deviceInfo['model'])) $this->_deviceInfo['softwareVersion'] = floatval($matches[2]);
$this->_deviceInfo['model'] = 'Funambol ' . trim($matches[1]);
if (!isset($this->_deviceInfo['softwareVersion']))
$this->_deviceInfo['softwareVersion'] = $matches[2];
if (!isset($this->_deviceInfo['deviceType'])) if (!isset($this->_deviceInfo['deviceType'])) {
{ switch (strtolower(trim($matches[1]))) {
switch (strtolower(trim($matches[1])))
{
case 'outlook plug-in':
default:
$this->_deviceInfo['deviceType'] = 'workstation';
break;
case 'pocket pc plug-in': case 'pocket pc plug-in':
$this->_deviceInfo['deviceType'] = 'windowsmobile'; $this->_deviceInfo['deviceType'] = 'windowsmobile';
break; break;
case 'outlook plug-in':
default:
$this->_deviceInfo['deviceType'] = 'workstation';
break;
} }
} }
} }
$devid = $this->_deviceInfo['deviceID']; switch (strtolower($this->_deviceInfo['deviceID'])) {
switch (strtolower($devid))
{
case 'fmz-thunderbird-plugin': case 'fmz-thunderbird-plugin':
if (empty($this->_devinceInfo['manufacturer'])) if (empty($this->_devinceInfo['manufacturer'])) {
$this->_deviceInfo['manufacturer'] = 'Funambol'; $this->_deviceInfo['manufacturer'] = 'Funambol';
if (empty($this->_devinceInfo['model'])) }
if (empty($this->_devinceInfo['model'])) {
$this->_deviceInfo['model'] = 'ThunderBird'; $this->_deviceInfo['model'] = 'ThunderBird';
if (empty($this->_devinceInfo['softwareVersion'])) }
$this->_deviceInfo['softwareVersion'] = '0.3'; if (empty($this->_devinceInfo['softwareVersion'])) {
$this->_deviceInfo['softwareVersion'] = '3.0';
}
break;
}
if (preg_match('/Funambol.*/i', $this->_deviceInfo['manufacturer'])) {
$this->_deviceInfo['supportLargeObjs'] = true;
}
switch (strtolower($this->_deviceInfo['manufacturer'])) {
case 'sonyericsson':
case 'sony ericsson':
if (strtolower($this->_deviceInfo['model']) == 'w890i') {
$this->_deviceInfo['supportLargeObjs'] = false;
}
break;
case 'synthesis ag':
foreach ($this->_deviceInfo['dataStore'] as &$ctype) {
$ctype['maxGUIDSize'] = 255;
}
break; break;
} }
} }
function output($currentCmdID, &$output ) { function output($currentCmdID, &$output ) {
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), 'Put'); $status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), $this->_cmdName);
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
$ref = ($state->getVersion() == 0) ? './devinf10' : './devinf11'; if ($state->getVersion() == 2) {
$ref = './devinf12';
} elseif ($state->getVersion() == 1) {
$ref = './devinf11';
} else {
$ref = './devinf10';
}
$status->setSourceRef($ref); $status->setSourceRef($ref);
if($state->isAuthorized()) { if($state->isAuthorized()) {
$this->finalizeDeviceInfo(); $this->finalizeDeviceInfo();
if(count((array)$this->_deviceInfo) > 0) { if(count((array)$this->_deviceInfo) > 0) {
$state->setClientDeviceInfo($this->_deviceInfo); $devInfo = $state->getClientDeviceInfo();
if (is_array($devInfo['dataStore'])
&& $devInfo['softwareVersion'] == $this->_deviceInfo['softwareVersion']) {
// merge with existing information
$devInfo['dataStore'] =
array_merge($devInfo['dataStore'],
$this->_deviceInfo['dataStore']);
} else {
// new device
$devInfo = $this->_deviceInfo;
}
#Horde::logMessage("SyncML: Put DeviceInfo:\n" . print_r($this->_deviceInfo, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setClientDeviceInfo($devInfo);
$state->writeClientDeviceInfo(); $state->writeClientDeviceInfo();
} }
} }
return $status->output($currentCmdID, $output); return $status->output($currentCmdID, $output);
} }
function startElement($uri, $element, $attrs) { function startElement($uri, $element, $attrs) {
#Horde::logMessage("SyncML: startElement[" . count($this->_stack) . "] $uri $element", __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch (count($this->_stack)) {
case 4:
switch ($element) {
case 'DataStore':
$this->_properties = array();
break;
}
break;
case 6:
switch ($element) {
case 'Property':
unset($this->_PropName);
$this->_PropSize = -1;
$this->_PropNoTruncate = false;
break;
}
break;
}
parent::startElement($uri, $element, $attrs); parent::startElement($uri, $element, $attrs);
} }

View File

@ -1,22 +1,29 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Replace.php,v 1.7 2004/05/26 17:41:30 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Final extends Horde_SyncML_Command { class Horde_SyncML_Command_Final extends Horde_SyncML_Command {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Replace';
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
{ {
return $currentCmdID; return $currentCmdID;

View File

@ -1,291 +1,36 @@
<?php <?php
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php';
/** /**
* $Horde: framework/SyncML/SyncML/Command/Results.php,v 1.11 2004/07/02 19:24:44 chuck Exp $ * eGroupWare - SyncML based on Horde 3
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com> * The SyncML_Command_Results class provides a SyncML implementation of the
* Results command as defined in SyncML Representation Protocol, version 1.1,
* section 5.5.12.
* *
* See the enclosed file COPYING for license information (LGPL). If you * The Results command is used to return the results of a Search or Get
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * command. Currently SyncML_Command_Results behaves the same as
* SyncML_Command_Put. The only results we get is the same DevInf as for the
* Put command.
* *
* @author Anthony Mills <amills@pyramid6.com> *
* @version $Revision$ * Using the PEAR Log class (which need to be installed!)
* @since Horde 3.0 *
* @package Horde_SyncML * @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
class Horde_SyncML_Command_Results extends Horde_SyncML_Command { include_once 'Horde/SyncML/Command/Put.php';
var $_cmdRef; class Horde_SyncML_Command_Results extends Horde_SyncML_Command_Put {
var $_type;
var $_data;
var $_locSourceURI;
var $_deviceInfo;
function endElement($uri, $element) {
#Horde::logMessage('SyncML: put endelement ' . $element . ' chars ' . $this->_chars, __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch ($this->_xmlStack) {
case 5:
switch($element) {
case 'DataStore':
$this->_deviceInfo['dataStore'][$this->_sourceReference] = array (
'maxGUIDSize' => $this->_maxGUIDSize,
'rxPreference' => $this->_rxPreference,
'txPreference' => $this->_txPreference,
'syncCapabilities' => $this->_syncCapabilities,
);
break;
case 'DevID':
$this->_deviceInfo['deviceID'] = trim($this->_chars);
break;
case 'DevTyp':
$this->_deviceInfo['deviceType'] = trim($this->_chars);
break;
case 'FwV':
$this->_deviceInfo['firmwareVersion'] = trim($this->_chars);
break;
case 'HwV':
$this->_deviceInfo['hardwareVersion'] = trim($this->_chars);
break;
case 'Man':
$this->_deviceInfo['manufacturer'] = trim($this->_chars);
break;
case 'Mod':
$this->_deviceInfo['model'] = trim($this->_chars);
break;
case 'OEM':
$this->_deviceInfo['oem'] = trim($this->_chars);
break;
case 'SwV':
$this->_deviceInfo['softwareVersion'] = trim($this->_chars);
break;
case 'SupportLargeObjs':
$this->_deviceInfo['supportLargeObjs'] = true;
break;
case 'SupportNumberOfChanges':
$this->_deviceInfo['supportNumberOfChanges'] = true;
break;
case 'UTC':
$this->_deviceInfo['UTC'] = true;
break;
case 'VerDTD':
$this->_deviceInfo['DTDVersion'] = trim($this->_chars);
break;
}
break;
case 6: /**
switch($element) { * Name of the command.
case 'MaxGUIDSize': *
$this->_maxGUIDSize = trim($this->_chars); * @var string
break; */
var $_cmdName = 'Results';
case 'Rx-Pref':
$this->_rxPreference = array (
'contentType' => $this->_contentType,
'contentVersion' => $this->_contentVersion,
);
break;
case 'SourceRef':
$this->_sourceReference = trim($this->_chars);
break;
case 'Tx-Pref':
$this->_txPreference = array(
'contentType' => $this->_contentType,
'contentVersion' => $this->_contentVersion,
);
break;
}
break;
case 7:
switch($element) {
case 'CTType':
$this->_contentType = trim($this->_chars);
break;
case 'SyncType':
$this->_syncCapabilities[] = trim($this->_chars);
break;
case 'VerCT':
$this->_contentVersion = trim($this->_chars);
break;
}
break;
}
parent::endElement($uri, $element);
}
function finalizeDeviceInfo()
{
// get some more information about the device from out of band data
$ua = $_SERVER['HTTP_USER_AGENT'];
if (preg_match("/^\s*Funambol (.*) (\d+\.\d+\.\d+)\s*$/i", $ua, $matches))
{
if (!isset($this->_deviceInfo['manufacturer']))
$this->_deviceInfo['manufacturer'] = 'Funambol';
if (!isset($this->_deviceInfo['model']))
$this->_deviceInfo['model'] = 'Funambol ' . trim($matches[1]);
if (!isset($this->_deviceInfo['softwareVersion']))
$this->_deviceInfo['softwareVersion'] = $matches[2];
if (!isset($this->_deviceInfo['deviceType']))
{
switch (strtolower(trim($matches[1])))
{
case 'outlook plug-in':
default:
$this->_deviceInfo['deviceType'] = 'workstation';
break;
case 'pocket pc plug-in':
$this->_deviceInfo['deviceType'] = 'windowsmobile';
break;
}
}
}
$devid = $this->_deviceInfo['deviceID'];
switch (strtolower($devid))
{
case 'fmz-thunderbird-plugin':
if (empty($this->_devinceInfo['manufacturer']))
$this->_deviceInfo['manufacturer'] = 'Funambol';
if (empty($this->_devinceInfo['model']))
$this->_deviceInfo['model'] = 'ThunderBird';
if (empty($this->_devinceInfo['softwareVersion']))
$this->_deviceInfo['softwareVersion'] = '0.3';
break;
}
}
function output($currentCmdID, &$output) {
if(!isset($this->_locSourceURI)) {
#Horde::logMessage('SyncML: BIG TODO!!!!!!!!!!!!!!!!!! parse reply', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state = &$_SESSION['SyncML.state'];
$status = new Horde_SyncML_Command_Status((($state->isAuthorized()) ? RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS), 'Results');
$status->setCmdRef($this->_cmdID);
$ref = ($state->getVersion() == 0) ? './devinf10' : './devinf11';
$status->setSourceRef($ref);
if($state->isAuthorized()) {
$this->finalizeDeviceInfo();
if(count((array)$this->_deviceInfo) > 0) {
$state->setClientDeviceInfo($this->_deviceInfo);
$state->writeClientDeviceInfo();
}
}
return $status->output($currentCmdID, $output);
} else {
#Horde::logMessage('SyncML: BIG TODO!!!!!!!!!!!!!!!!!! generate reponse', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state = $_SESSION['SyncML.state'];
$attrs = array();
$output->startElement($state->getURI(), 'Results', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID;
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'MsgRef', $attrs);
$chars = $state->getMsgID();
$output->characters($chars);
$output->endElement($state->getURI(), 'MsgRef');
$output->startElement($state->getURI(), 'CmdRef', $attrs);
$chars = $this->_cmdRef;
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdRef');
$output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Type', $attrs);
$output->characters($this->_type);
$output->endElement($state->getURIMeta(), 'Type');
$output->endElement($state->getURI(), 'Meta');
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $this->_locSourceURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
$output->startElement($state->getURI(), 'Data', $attrs);
// Need to send this information as opaque data so the WBXML
// will understand it.
$output->opaque($this->_data);
$output->endElement($state->getURI(), 'Data');
$output->endElement($state->getURI(), 'Item');
$output->endElement($state->getURI(), 'Results');
$currentCmdID++;
return $currentCmdID;
}
}
/**
* Setter for property cmdRef.
*
* @param string $cmdRef New value of property cmdRef.
*/
function setCmdRef($cmdRef) {
$this->_cmdRef = $cmdRef;
}
/**
* Setter for property Type.
*
* @param string $type New value of property type.
*/
function setType($type) {
$this->_type = $type;
}
/**
* Setter for property data.
*
* @param string $data New value of property data.
*/
function setData($data) {
$this->_data = $data;
}
/**
* Setter for property locSourceURI.
*
* @param string $locSourceURI New value of property locSourceURI.
*/
function setlocSourceURI($locSourceURI) {
$this->_locSourceURI = $locSourceURI;
}
} }

View File

@ -1,37 +1,76 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/State.php'; include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Status.php,v 1.15 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Status extends Horde_SyncML_Command { class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Status';
/**
* The Response code of the command sent to the client, that this
* Status response refers to.
*
* @var integer
*/
var $_response; var $_response;
/**
* The command ID (CmdID) of the command sent to the client, that this
* Status response refers to.
*
* @var integer
*/
var $_cmdRef; var $_cmdRef;
/** /**
* Must be present. * The command (Add, Replace, etc) sent to the client, that this Status
* response refers to.
*
* @var string
*/ */
var $_cmd; var $_cmd;
/** /**
* Must if not null (what does this mean?). * The server ID of the sent object, that this Status response refers to.
*
* This element is optional. If specified, Status response refers to a
* single Item in the command sent to the client. It refers to all Items in
* the sent command otherwise.
*
* @var string
*/ */
var $_sourceRef; var $_sourceRef;
/**
* The client ID of the sent object, that this Status response refers to.
*
* This element is optional. If specified, Status response refers to a
* single Item in the command sent to the client. It refers to all Items in
* the sent command otherwise.
*
* @var string
*/
var $_targetRef; var $_targetRef;
var $_chalMetaFormat; var $_chalMetaFormat;
@ -48,6 +87,15 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
var $_itemSourceLocURI; var $_itemSourceLocURI;
var $_syncItems;
/**
* Constructor.
*
* @param integer $response The response code.
* @param string $cmd The command sent to the client,
* that this Status response refers to.
*/
function Horde_SyncML_Command_Status($response = null, $cmd = null) function Horde_SyncML_Command_Status($response = null, $cmd = null)
{ {
if ($response != null) { if ($response != null) {
@ -61,12 +109,11 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
{ {
$state = &$_SESSION['SyncML.state'];
$attrs = array(); $attrs = array();
$state = $_SESSION['SyncML.state'];
if ($this->_cmd != null) { if ($this->_cmd != null) {
$attrs = array();
$output->startElement($state->getURI(), 'Status', $attrs); $output->startElement($state->getURI(), 'Status', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs); $output->startElement($state->getURI(), 'CmdID', $attrs);
@ -174,61 +221,41 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
$output->endElement($state->getURI(), 'Item'); $output->endElement($state->getURI(), 'Item');
} }
if (isset($this->_itemTargetLocURI) && isset($this->_itemSourceLocURI)) { if (isset($this->_syncItems)) {
$output->startElement($state->getURI(), 'Item', $attrs); // Support multible items per command
foreach ($this->_syncItems as $locURI => &$syncItem) {
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$output->characters($locURI);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
$output->endElement($state->getURI(), 'Item');
}
} elseif (isset($this->_itemTargetLocURI) || isset($this->_itemSourceLocURI)) {
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Target', $attrs); if (isset($this->_itemTargetLocURI)) {
$output->startElement($state->getURI(), 'LocURI', $attrs); $output->startElement($state->getURI(), 'Target', $attrs);
$output->characters($this->_itemTargetLocURI); $output->startElement($state->getURI(), 'LocURI', $attrs);
$output->endElement($state->getURI(), 'LocURI'); $output->characters($this->_itemTargetLocURI);
$output->endElement($state->getURI(), 'Target'); $output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs); }
$output->startElement($state->getURI(), 'LocURI', $attrs); if (isset($this->_itemSourceLocURI)) {
$output->characters($this->_itemSourceLocURI); $output->startElement($state->getURI(), 'Source', $attrs);
$output->endElement($state->getURI(), 'LocURI'); $output->startElement($state->getURI(), 'LocURI', $attrs);
$output->endElement($state->getURI(), 'Source'); $output->characters($this->_itemSourceLocURI);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Item'); $output->endElement($state->getURI(), 'Source');
} }
$output->endElement($state->getURI(), 'Item');
}
$output->endElement($state->getURI(), 'Status'); $output->endElement($state->getURI(), 'Status');
$currentCmdID++; $currentCmdID++;
// moredata pending request them
/* if($this->_response == RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED) {
$output->startElement($state->getURI(), 'Alert', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID;
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Data', $attrs);
$output->characters(ALERT_NEXT_MESSAGE);
$output->endElement($state->getURI(), 'Data');
if (isset($this->_itemTargetLocURI) && isset($this->_itemSourceLocURI)) {
$output->startElement($state->getURI(), 'Item', $attrs);
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$output->characters($this->_itemTargetLocURI);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$output->characters($this->_itemSourceLocURI);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
}
$output->endElement($state->getURI(), 'Alert');
$currentCmdID++;
} */
} }
return $currentCmdID; return $currentCmdID;
@ -323,4 +350,14 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
{ {
$this->_itemTargetLocURI = $itemTargetLocURI; $this->_itemTargetLocURI = $itemTargetLocURI;
} }
/**
* Setter for the the list of handled SyncItems
*
* @param array $syncItems The Items of the command
*/
function setSyncItems(&$syncItems)
{
$this->_syncItems = $syncItems;
}
} }

View File

@ -1,220 +1,258 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/State.php'; include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
include_once 'Horde/SyncML/Command/Sync/SyncElement.php'; include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
include_once 'Horde/SyncML/Sync/TwoWaySync.php'; include_once 'Horde/SyncML/Sync/TwoWaySync.php';
include_once 'Horde/SyncML/Sync/SlowSync.php'; include_once 'Horde/SyncML/Sync/SlowSync.php';
include_once 'Horde/SyncML/Sync/OneWayFromServerSync.php'; include_once 'Horde/SyncML/Sync/OneWayFromServerSync.php';
include_once 'Horde/SyncML/Sync/OneWayFromClientSync.php';
include_once 'Horde/SyncML/Sync/RefreshFromServerSync.php'; include_once 'Horde/SyncML/Sync/RefreshFromServerSync.php';
include_once 'Horde/SyncML/Sync/RefreshFromClientSync.php';
/** class Horde_SyncML_Command_Sync extends Horde_SyncML_Command {
* $Horde: framework/SyncML/SyncML/Command/Sync.php,v 1.17 2004/07/03 15:21:14 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync extends Horde_Syncml_Command {
var $_isInSource; /**
var $_currentSyncElement; * Name of the command.
var $_syncElements = array(); *
* @var string
function output($currentCmdID, &$output) { */
var $_cmdName = 'Sync';
$state = &$_SESSION['SyncML.state'];
/**
$attrs = array(); * Source database of the <Sync> command.
*
Horde::logMessage('SyncML: $this->_targetURI = ' . $this->_targetURI, __FILE__, __LINE__, PEAR_LOG_DEBUG); * @var string
*/
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Sync'); var $_sourceURI;
// $status->setState($state); /**
* Target database of the <Sync> command.
$status->setCmdRef($this->_cmdID); *
* @var string
if ($this->_targetURI != null) { */
$status->setTargetRef((isset($this->_targetURIParameters) ? $this->_targetURI.'?/'.$this->_targetURIParameters : $this->_targetURI)); var $_targetURI;
}
/**
if ($this->_sourceURI != null) { * Optional parameter for the Target.
$status->setSourceRef($this->_sourceURI); *
} * @var string
*/
$currentCmdID = $status->output($currentCmdID, $output); var $_targetURIParameters;
if($sync = $state->getSync($this->_targetURI)) { /**
$currentCmdID = $sync->startSync($currentCmdID, $output); * SyncML_SyncElement object for the currently parsed sync command.
*
foreach ($this->_syncElements as $element) { * @var SyncML_SyncElement
$currentCmdID = $sync->nextSyncCommand($currentCmdID, $element, $output); */
} var $_curItem;
}
/**
* List of all SyncML_SyncElement objects that have parsed.
*
* @var array
*/
var $_syncElements = array();
function output($currentCmdID, &$output)
{
$state = &$_SESSION['SyncML.state'];
Horde::logMessage('SyncML: $this->_targetURI = ' . $this->_targetURI, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Sync');
$status->setCmdRef($this->_cmdID);
if ($this->_targetURI != null) {
$status->setTargetRef((isset($this->_targetURIParameters) ? $this->_targetURI.'?/'.$this->_targetURIParameters : $this->_targetURI));
}
if ($this->_sourceURI != null) {
$status->setSourceRef($this->_sourceURI);
}
$currentCmdID = $status->output($currentCmdID, $output);
if ($sync = &$state->getSync($this->_targetURI)) {
$currentCmdID = $sync->startSync($currentCmdID, $output);
foreach ($this->_syncElements as $element) {
$currentCmdID = $sync->nextSyncCommand($currentCmdID, $element, $output);
}
}
return $currentCmdID;
}
return $currentCmdID;
}
function getTargetURI() {
return $this->_targetURI;
}
function startElement($uri, $element, $attrs) function startElement($uri, $element, $attrs)
{ {
parent::startElement($uri, $element, $attrs); parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) { switch (count($this->_stack)) {
case 2: case 2:
if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') { if ($element == 'Replace' ||
$this->_currentSyncElement = &Horde_SyncML_Command_Sync_SyncElement::factory($element); $element == 'Add' ||
// $this->_currentSyncElement->setVersion($this->_version); $element == 'Delete') {
// $this->_currentSyncElement->setCmdRef($this->_cmdID); Horde::logMessage("SyncML: sync element $element found", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// $this->_currentSyncElement->setMsgID($this->_msgID); $this->_curItem = &Horde_SyncML_Command_Sync_SyncElement::factory($element);
} elseif ($element == 'Target') {
$this->_isInSource = false;
} else {
$this->_isInSource = true;
} }
break; break;
} }
if (isset($this->_curItem)) {
if (isset($this->_currentSyncElement)) { $this->_curItem->startElement($uri, $element, $attrs);
$this->_currentSyncElement->startElement($uri, $element, $attrs);
} }
} }
// We create a seperate Sync Element for the Sync Data sent // We create a seperate Sync Element for the Sync Data sent
// from the Server to the client as we want to process the // from the Server to the client as we want to process the
// client sync information before. // client sync information before.
function syncToClient($currentCmdID, &$output) function syncToClient($currentCmdID, &$output)
{ {
Horde::logMessage('SyncML: starting sync to client', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: starting sync to client', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state = $_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
if($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED)
{
$deviceInfo = $state->getClientDeviceInfo();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
$targets = $state->getTargets();
Horde::logMessage('SyncML: starting sync to client '.$targets[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
$attrs = array();
if($state->getSyncStatus() >= CLIENT_SYNC_FINNISHED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED)
{
$deviceInfo = $state->getClientDeviceInfo();
$targets = $state->getTargets();
foreach($targets as $target) foreach($targets as $target)
{ {
$sync = $state->getSync($target); $sync = &$state->getSync($target);
Horde::logMessage('SyncML['. session_id() .']: sync alerttype '. $sync->_syncType .' found for target ' . $target, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// make sure that the state reflects what is currently being done if ($sync->_syncType == ALERT_ONE_WAY_FROM_CLIENT ||
$state->_currentSourceURI = $sync->_sourceLocURI; $sync->_syncType == ALERT_REFRESH_FROM_CLIENT) {
$state->_currentTargetURI = $sync->_targetLocURI;
Horde::logMessage('SyncML['. session_id() .']: From client Sync, no sync of '. $target .' to client', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$output->startElement($state->getURI(), 'Sync', $attrs); $state->clearSync($target);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID); } else if ($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED) {
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID'); Horde::logMessage("SyncML: starting sync to client $target", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$attrs = array();
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
$chars = $sync->_sourceLocURI;
$output->characters($chars); $output->startElement($state->getURI(), 'Sync', $attrs);
$output->endElement($state->getURI(), 'LocURI'); $output->startElement($state->getURI(), 'CmdID', $attrs);
$output->endElement($state->getURI(), 'Target'); $output->characters($currentCmdID);
$currentCmdID++;
$output->startElement($state->getURI(), 'Source', $attrs); $output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'LocURI', $attrs);
#$chars = $sync->_targetLocURI; $output->startElement($state->getURI(), 'Target', $attrs);
$chars = (isset($sync->_targetLocURIParameters) ? $sync->_targetLocURI.'?/'.$sync->_targetLocURIParameters : $sync->_targetLocURI); $output->startElement($state->getURI(), 'LocURI', $attrs);
$output->characters($chars); $chars = $sync->_sourceLocURI;
$output->endElement($state->getURI(), 'LocURI'); $output->characters($chars);
$output->endElement($state->getURI(), 'Source'); $output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
if(!$sync->_syncDataLoaded)
{ $output->startElement($state->getURI(), 'Source', $attrs);
$numberOfItems = $sync->loadData(); $output->startElement($state->getURI(), 'LocURI', $attrs);
if($deviceInfo['supportNumberOfChanges']) $chars = (isset($sync->_targetLocURIParameters) ? $sync->_targetLocURI.'?/'.$sync->_targetLocURIParameters : $sync->_targetLocURI);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source');
if(!$sync->_syncDataLoaded)
{ {
$output->startElement($state->getURI(), 'NumberOfChanged', $attrs); $numberOfItems = $sync->loadData();
$output->characters($numberOfItems); if($deviceInfo['supportNumberOfChanges'])
$output->endElement($state->getURI(), 'NumberOfChanged'); {
$output->startElement($state->getURI(), 'NumberOfChanges', $attrs);
$output->characters($numberOfItems);
$output->endElement($state->getURI(), 'NumberOfChanges');
}
} }
$currentCmdID = $sync->endSync($currentCmdID, $output);
$output->endElement($state->getURI(), 'Sync');
if (isset($state->curSyncItem) ||
$state->getNumberOfElements() === false) {
break;
}
} else {
Horde::logMessage("SyncML: Waiting for client ACKNOWLEDGE for $target", __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
$currentCmdID = $sync->endSync($currentCmdID, $output);
$output->endElement($state->getURI(), 'Sync');
break;
} }
// no syncs left // no syncs left
if($state->getTargets() === FALSE) if($state->getTargets() === FALSE &&
!isset($state->curSyncItem)) {
$state->setSyncStatus(SERVER_SYNC_FINNISHED); $state->setSyncStatus(SERVER_SYNC_FINNISHED);
}
Horde::logMessage('SyncML: syncStatus(server_sync_finnished) '. $state->getSyncStatus, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} Horde::logMessage('SyncML: syncStatus(syncToClient) = '. $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $currentCmdID; return $currentCmdID;
} }
function endElement($uri, $element) function endElement($uri, $element)
{ {
if (isset($this->_currentSyncElement)) { if (isset($this->_curItem)) {
$this->_currentSyncElement->endElement($uri, $element); $this->_curItem->endElement($uri, $element);
} }
switch ($this->_xmlStack) { switch (count($this->_stack)) {
case 2: case 2:
if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') { if ($element == 'Replace' ||
$this->_syncElements[] = $this->_currentSyncElement; $element == 'Add' ||
unset($this->_currentSyncElement); $element == 'Delete') {
$this->_syncElements[] = &$this->_curItem;
unset($this->_curItem);
} }
break; break;
case 3: case 3:
$state = & $_SESSION['SyncML.state']; if ($element == 'LocURI' && !isset($this->_curItem)) {
if ($this->_stack[1] == 'Source') {
if ($element == 'LocURI' && !isset($this->_currentSyncElement)) {
if ($this->_isInSource) {
$this->_sourceURI = trim($this->_chars); $this->_sourceURI = trim($this->_chars);
$state->_currentSourceURI = $this->_sourceURI; } elseif ($this->_stack[1] == 'Target') {
} else {
$this->_targetURI = trim($this->_chars);
$targetURIData = explode('?/',trim($this->_chars)); $targetURIData = explode('?/',trim($this->_chars));
$this->_targetURI = $targetURIData[0]; $this->_targetURI = $targetURIData[0];
$state->_currentTargetURI = $this->_targetURI;
if (isset($targetURIData[1])) {
if(isset($targetURIData[1])) $this->_targetURIParameters = $targetURIData[1];
{ }
$this->_targetURIParameters = $targetURIData[1];
$state->_currentTargetURIParameters = $this->_targetURIParameters;
}
} }
} }
break; break;
} }
parent::endElement($uri, $element); parent::endElement($uri, $element);
} }
function characters($str) function characters($str)
{ {
if (isset($this->_currentSyncElement)) { if (isset($this->_curItem)) {
$this->_currentSyncElement->characters($str); $this->_curItem->characters($str);
} else { } else {
if (isset($this->_chars)) { if (isset($this->_chars)) {
$this->_chars = $this->_chars . $str; $this->_chars .= $str;
} else { } else {
$this->_chars = $str; $this->_chars = $str;
} }

View File

@ -1,20 +1,20 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command/Sync/SyncElement.php'; include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Sync/Add.php,v 1.10 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync_Add extends Horde_SyncML_Command_Sync_SyncElement { class Horde_SyncML_Command_Sync_Add extends Horde_SyncML_Command_Sync_SyncElement {
var $_status = RESPONSE_ITEM_ADDED; var $_status = RESPONSE_ITEM_ADDED;
@ -24,9 +24,10 @@ class Horde_SyncML_Command_Sync_Add extends Horde_SyncML_Command_Sync_SyncElemen
$status = new Horde_SyncML_Command_Status($this->_status, 'Add'); $status = new Horde_SyncML_Command_Status($this->_status, 'Add');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
if (isset($this->_luid)) { if (!empty($this->_items)) {
$status->setSourceRef($this->_luid); $status->setSyncItems($this->_items);
} }
return $status->output($currentCmdID, $output); return $status->output($currentCmdID, $output);
} }

View File

@ -1,106 +1,44 @@
<?php <?php
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
/** /**
* $Horde: framework/SyncML/SyncML/Command/Sync/ContentSyncElement.php,v 1.12 2004/07/02 19:24:44 chuck Exp $ * eGroupWare - SyncML based on Horde 3
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
* *
* See the enclosed file COPYING for license information (LGPL). If you * Using the PEAR Log class (which need to be installed!)
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com> * @link http://www.egroupware.org
* @version $Revision$ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @since Horde 3.0 * @package api
* @package Horde_SyncML * @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
class Horde_SyncML_Command_Sync_ContentSyncElement extends Horde_SyncML_Command_Sync_SyncElement { include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command/Sync/SyncElementItem.php';
/** class Horde_SyncML_Command_Sync_ContentSyncElement extends Horde_SyncML_Command_Sync_SyncElementItem {
* The content: vcard data, etc.
*/
var $_content;
/**
* Local to server: our Horde guid.
*/
var $_locURI;
var $_targetURI;
var $_contentType;
function setSourceURI($uri)
{
$this->_locURI = $uri;
}
function getSourceURI()
{
return $this->_locURI;
}
function setTargetURI($uri)
{
$this->_targetURI = $uri;
}
function getTargetURI()
{
return $this->_targetURI;
}
function setContentType($c)
{
$this->_contentType = $c;
}
function setContentFormat($_format)
{
$this->_contentFormat = $_format;
}
function getContentType()
{
return $this->_contentType;
}
function getContent()
{
return $this->_content;
}
function setContent($content)
{
$this->_content = $content;
}
function endElement($uri, $element)
{
switch ($this->_xmlStack) {
case 2:
if ($element == 'Data') {
$this->_content = trim($this->_chars);
}
break;
}
parent::endElement($uri, $element);
}
function outputCommand($currentCmdID, &$output, $command) function outputCommand($currentCmdID, &$output, $command)
{ {
$state = $_SESSION['SyncML.state']; $state = $_SESSION['SyncML.state'];
$maxMsgSize = $state->getMaxMsgSizeClient();
$maxGUIDSize = $state->getMaxGUIDSizeClient();
if ($this->_moreData) {
$command = $this->_command;
} else {
$this->_command = $command;
}
$attrs = array(); $attrs = array();
$output->startElement($state->getURI(), $command, $attrs); $output->startElement($state->getURI(), $command, $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs); $output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID; $output->characters($currentCmdID);
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdID'); $output->endElement($state->getURI(), 'CmdID');
/*
if (isset($this->_contentType)) { if (isset($this->_contentType)) {
$output->startElement($state->getURI(), 'Meta', $attrs); $output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Type', $attrs); $output->startElement($state->getURIMeta(), 'Type', $attrs);
@ -108,43 +46,100 @@ class Horde_SyncML_Command_Sync_ContentSyncElement extends Horde_SyncML_Command_
$output->endElement($state->getURIMeta(), 'Type'); $output->endElement($state->getURIMeta(), 'Type');
$output->endElement($state->getURI(), 'Meta'); $output->endElement($state->getURI(), 'Meta');
} }
*/
if (isset($this->_content) && !$this->_moreData) {
$this->_content = trim($this->_content);
$this->_contentSize = strlen($this->_content);
if (strtolower($this->_contentFormat) == 'b64') {
$this->_content = base64_encode($this->_content);
}
} else {
$this->_contentSize = 0;
}
if (isset($this->_content) // <command><Meta>
|| isset($this->_locURI) || isset($this->targetURI)) { if ($this->_contentSize || isset($this->_contentType) || isset($this->_contentFormat)) {
$output->startElement($state->getURI(), 'Meta', $attrs);
if (isset($this->_contentType)) {
$output->startElement($state->getURIMeta(), 'Type', $attrs);
$output->characters($this->_contentType);
$output->endElement($state->getURIMeta(), 'Type');
}
if (isset($this->_contentFormat)) {
$output->startElement($state->getURIMeta(), 'Format', $attrs);
$output->characters($this->_contentFormat);
$output->endElement($state->getURIMeta(), 'Format');
}
if ($this->_contentSize) {
$output->startElement($state->getURIMeta(), 'Size', $attrs);
$output->characters(($this->_contentSize));
$output->endElement($state->getURIMeta(), 'Size');
}
$output->endElement($state->getURI(), 'Meta');
}
if (isset($this->_content) || isset($this->_luid) || isset($this->_guid)) {
$output->startElement($state->getURI(), 'Item', $attrs); $output->startElement($state->getURI(), 'Item', $attrs);
// send only when sending adds
if ($this->_locURI != null && (strtolower($command) == 'add')) { // <command><Item><Source><LocURI>
if (isset($this->_guid)) {
$output->startElement($state->getURI(), 'Source', $attrs); $output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs); $output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = substr($this->_locURI,0,39); $chars = substr($this->_guid, 0, $maxGUIDSize);
$state->setUIDMapping($this->_locURI, $chars); $state->setUIDMapping($this->_guid, $chars);
$output->characters($chars); $output->characters($chars);
$output->endElement($state->getURI(), 'LocURI'); $output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Source'); $output->endElement($state->getURI(), 'Source');
} }
if(isset($this->_contentFormat)) {
$output->startElement($state->getURI(), 'Meta', $attrs);
$output->startElement($state->getURIMeta(), 'Format', $attrs);
$output->characters($this->_contentFormat);
$output->endElement($state->getURIMeta(), 'Format');
$output->endElement($state->getURI(), 'Meta');
}
if ($this->_targetURI != null) { // <command><Item><Target><LocURI>
if (isset($this->_luid)) {
$output->startElement($state->getURI(), 'Target', $attrs); $output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs); $output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $this->_targetURI; $output->characters($this->_luid);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI'); $output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target'); $output->endElement($state->getURI(), 'Target');
} }
// <command><Item><Data>
if (isset($this->_content)) { if (isset($this->_content)) {
$output->startElement($state->getURI(), 'Data', $attrs); $output->startElement($state->getURI(), 'Data', $attrs);
#$chars = '<![CDATA['.$this->_content.']]>'; #$chars = '<![CDATA['.$this->_content.']]>';
$chars = $this->_content; $currentSize = $output->getOutputSize();
$output->characters($chars); Horde::logMessage("SyncML: $command: current = $currentSize, max = $maxMsgSize", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!$maxMsgSize ||
(($currentSize + MIN_MSG_LEFT + $this->_contentSize) <= $maxMsgSize)) {
$chars = $this->_content;
unset($this->_content);
$this->_moreData = false;
} else {
$sizeLeft = $maxMsgSize - $currentSize - MIN_MSG_LEFT;
if ($sizeLeft < 0) {
Horde::logMessage("SyncML: $command: split with $currentSize for $maxMsgSize, increase MIN_MSG_LEFT!", __FILE__, __LINE__, PEAR_LOG_WARNING);
$sizeLeft = 0;
}
// don't let us loose characters by trimming
while (($this->_contentSize > $sizeLeft) &&
(strlen(trim(substr($this->_content, $sizeLeft - 1, 2))) < 2)) {
Horde::logMessage("SyncML: $command: split at $sizeLeft hit WS!", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$sizeLeft++;
}
$chars = substr($this->_content, 0, $sizeLeft);
$this->_content = substr($this->_content, $sizeLeft, $this->_contentSize - $sizeLeft);
Horde::logMessage("SyncML: $command: "
. $this->_contentSize . " split at $sizeLeft:\n"
. $chars, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->_moreData = true;
}
$output->characters($chars);
$output->endElement($state->getURI(), 'Data'); $output->endElement($state->getURI(), 'Data');
// <command><Item><MoreData/>
if ($this->_moreData) {
$output->startElement($state->getURI(), 'MoreData', $attrs);
$output->endElement($state->getURI(), 'MoreData');
}
} }
$output->endElement($state->getURI(), 'Item'); $output->endElement($state->getURI(), 'Item');
} }

View File

@ -1,20 +1,20 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command/Sync/SyncElement.php'; include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Sync/Delete.php,v 1.9 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync_Delete extends Horde_SyncML_Command_Sync_SyncElement { class Horde_SyncML_Command_Sync_Delete extends Horde_SyncML_Command_Sync_SyncElement {
function output($currentCmdID, &$output) function output($currentCmdID, &$output)
@ -22,8 +22,8 @@ class Horde_SyncML_Command_Sync_Delete extends Horde_SyncML_Command_Sync_SyncEle
$status = new Horde_SyncML_Command_Status($this->_status, 'Delete'); $status = new Horde_SyncML_Command_Status($this->_status, 'Delete');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
if (isset($this->_luid)) { if (!empty($this->_items)) {
$status->setSourceRef($this->_luid); $status->setSyncItems($this->_items);
} }
return $status->output($currentCmdID, $output); return $status->output($currentCmdID, $output);

View File

@ -1,32 +1,30 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command/Sync/SyncElement.php'; include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Sync/Replace.php,v 1.9 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync_Replace extends Horde_SyncML_Command_Sync_SyncElement { class Horde_SyncML_Command_Sync_Replace extends Horde_SyncML_Command_Sync_SyncElement {
function output($currentCmdID, &$output) { function output($currentCmdID, &$output) {
$status = new Horde_SyncML_Command_Status($this->_status, 'Replace'); $status = new Horde_SyncML_Command_Status($this->_status, 'Replace');
$status->setCmdRef($this->_cmdID); $status->setCmdRef($this->_cmdID);
if (isset($this->_luid)) { if (!empty($this->_items)) {
$status->setSourceRef($this->_luid); $status->setSyncItems($this->_items);
} }
#$status->setItemSourceLocURI($this->_sourceLocURI);
#$status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
return $status->output($currentCmdID, $output); return $status->output($currentCmdID, $output);
} }

View File

@ -1,37 +1,40 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Sync/SyncElement.php,v 1.11 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
* Copyright 2005-2006 Lars Kneschke <l.kneschke@metaways.de>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command { class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command {
var $_luid; var $_luid;
var $_guid; var $_guid;
var $_isSource; var $_contentSize;
var $_content;
var $_contentType; var $_contentType;
var $_contentFormat;
var $_status = RESPONSE_OK; var $_status = RESPONSE_OK;
var $_items; var $_curItem;
var $_items = array();
var $_moreData = false;
var $_command = false;
function &factory($command, $params = null) { function &factory($command, $params = null) {
include_once 'Horde/SyncML/Command/Sync/SyncElementItem.php'; include_once 'Horde/SyncML/Command/Sync/SyncElementItem.php';
@include_once 'Horde/SyncML/Command/Sync/' . $command . '.php'; @include_once 'Horde/SyncML/Command/Sync/' . $command . '.php';
$class = 'Horde_SyncML_Command_Sync_' . $command; $class = 'Horde_SyncML_Command_Sync_' . $command;
if (class_exists($class)) { if (class_exists($class)) {
#Horde::logMessage('SyncML: Class definition of ' . $class . ' found in SyncElement::factory.', __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage('SyncML: Class definition of ' . $class . ' found in SyncElement::factory.', __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $element = new $class($params); return $element = new $class($params);
@ -44,78 +47,122 @@ class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command {
function startElement($uri, $element, $attrs) { function startElement($uri, $element, $attrs) {
parent::startElement($uri, $element, $attrs); parent::startElement($uri, $element, $attrs);
$state = &$_SESSION['SyncML.state'];
switch ($this->_xmlStack) {
case 3: switch (count($this->_stack)) {
if ($element == 'Source') { case 1:
$this->_isSource = true; $this->_command = $element;
break;
case 2:
if ($element == 'Item') {
if (isset($state->curSyncItem)) {
// Copy from state in case of <MoreData>.
$this->_curItem = &$state->curSyncItem;
if (isset($this->_luid) &&
($this->_luid != $this->_curItem->_luid)) {
Horde::logMessage('SyncML: moreData mismatch for LocURI ' .
$this->_curItem->_luid . ' (' . $this->_luid . ')', __FILE__, __LINE__, PEAR_LOG_ERROR);
} else {
Horde::logMessage('SyncML: moreData item found for LocURI ' . $this->_curItem->_luid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
unset($state->curSyncItem);
} else {
$this->_curItem = new Horde_SyncML_Command_Sync_SyncElementItem();
}
$this->_moreData = false;
} }
break; break;
} }
} }
function endElement($uri, $element) { function endElement($uri, $element) {
$state = &$_SESSION['SyncML.state'];
$search = array('/ *\n/','/ *$/m'); $search = array('/ *\n/','/ *$/m');
$replace = array('',''); $replace = array('','');
switch ($this->_xmlStack) { switch (count($this->_stack)) {
case 1: case 1:
$this->_command = false;
// Need to add sync elements to the Sync method? // Need to add sync elements to the Sync method?
#error_log('total # of items: '.count($this->_items)); #error_log('total # of items: '.count($this->_items));
#error_log(print_r($this->_items[10], true)); #error_log(print_r($this->_items[10], true));
break; break;
case 2; case 2;
if($element == 'Item') { if($element == 'Item') {
$item = new Horde_SyncML_Command_Sync_SyncElementItem();
if($this->_luid) { if($this->_luid) {
$item->setLocURI($this->_luid); $this->_curItem->setLocURI($this->_luid);
$item->setContent($this->_content); $this->_curItem->setContentType($this->_contentType);
$item->setContentType($this->_contentType); $this->_curItem->setContentFormat($this->_contentFormat);
$this->_curItem->setCommand($this->_command);
if($this->_contentSize) if($this->_contentSize)
$item->setContentType($this->_contentSize); $this->_curItem->setContentSize($this->_contentSize);
if($this->_moreData) if($this->_moreData) {
$item->setMoreData($this->_moreData); $state->curSyncItem = &$this->_curItem;
Horde::logMessage('SyncML: moreData item saved for LocURI ' . $this->_curItem->_luid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->_items[$this->_luid] = $item; } else {
if (strtolower($this->_curItem->getContentFormat()) == 'b64') {
$content = $this->_curItem->getContent();
$content = ($content ? base64_decode($content) : '');
$this->_curItem->setContent($content);
#Horde::logMessage('SyncML: BASE64 encoded item for LocURI '
# . $this->_curItem->_luid . ":\n $content", __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
$this->_items[$this->_luid] = $this->_curItem;
}
} }
unset($this->_content);
unset($this->_contentSize); unset($this->_contentSize);
unset($this->_luid); unset($this->_luid);
} }
break; break;
case 3: case 3:
if ($element == 'Source') { switch ($element) {
$this->_isSource = false; case 'Data':
} elseif ($element == 'Data') { $this->_curItem->_content .= $this->_chars;
$this->_content = $this->_chars; break;
} elseif ($element == 'MoreData') { case 'MoreData':
$this->_moreData = TRUE; $this->_moreData = true;
} elseif ($element == 'Type') { break;
if(empty($this->_contentType)) case 'Type':
$this->_contentType = trim($this->_chars); if(empty($this->_contentType)) {
$this->_contentType = trim($this->_chars);
}
break;
case 'Format':
$this->_contentFormat = strtolower(trim($this->_chars));
break;
case 'Size':
$this->_contentSize = $this->_chars;
break;
} }
break; break;
case 4: case 4:
if ($element == 'LocURI' && $this->_isSource) { switch ($element) {
$this->_luid = trim($this->_chars); case 'LocURI':
} elseif ($element == 'Type') { if ($this->_stack[2] == 'Source') {
$this->_contentType = trim($this->_chars); $this->_luid = trim($this->_chars);
} elseif ($element == 'Size') { }
$this->_contentSize = trim($this->_chars); break;
case 'Type':
$this->_contentType = trim($this->_chars);
break;
case 'Format':
$this->_contentFormat = strtolower(trim($this->_chars));
break;
case 'Size':
$this->_contentSize = trim($this->_chars);
break;
} }
break; break;
} }
parent::endElement($uri, $element); parent::endElement($uri, $element);
} }
function getSyncElementItems() { function getSyncElementItems() {
return (array)$this->_items; return (array)$this->_items;
} }
function getLocURI() function getLocURI()
{ {
@ -149,14 +196,17 @@ class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command {
function getContent() function getContent()
{ {
return $this->_content; if ($this->_curItem) {
return $this->_curItem->getcontent();
}
return false;
} }
function setContent($content) function hasMoreData()
{ {
$this->_content = $content; return $this->_moreData;
} }
function setStatus($_status) function setStatus($_status)
{ {
$this->_status = $_status; $this->_status = $_status;

View File

@ -1,67 +1,95 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Command.php'; include_once 'Horde/SyncML/Command.php';
/**
* $Horde: framework/SyncML/SyncML/Command/Sync/SyncElement.php,v 1.11 2004/07/02 19:24:44 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
* Copyright 2005-2006 Lars Kneschke <l.kneschke@metaways.de>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Command_Sync_SyncElementItem { class Horde_SyncML_Command_Sync_SyncElementItem {
var $_luid; var $_luid;
var $_guid; var $_guid;
var $_content; var $_content = '';
var $_contentSize; var $_contentSize;
var $_contentType; var $_contentType;
var $_contentFormat;
var $_command;
var $_moreData = false; var $_moreData = false;
function getLocURI() { function getLocURI() {
return $this->_luid; return $this->_luid;
} }
function getGUID() { function getGUID() {
return $this->_guid; return $this->_guid;
} }
function getContentType() { function getContentType() {
return $this->_contentType; return $this->_contentType;
} }
function getContentFormat() {
return $this->_contentFormat;
}
function getContent() { function getContent() {
return $this->_content; return $this->_content;
} }
function getContentSize() {
if (isset($this->_contentSize)) {
return $this->_contentSize;
}
return false;
}
function getCommand() {
return $this->_command;
}
function setLocURI($luid) { function setLocURI($luid) {
$this->_luid = $luid; $this->_luid = $luid;
} }
function setGUID($guid) { function setGUID($guid) {
$this->_guid = $guid; $this->_guid = $guid;
} }
function setContent($content) { function setContent($_content) {
$this->_content = $content; $this->_content = $_content;
} }
function setContentSize($_size) { function setContentSize($_size) {
$this->_contentSize = $_size; $this->_contentSize = $_size;
} }
function setContentType($_contentType) { function setContentType($_type) {
$this->_contentType = $_contentType; $this->_contentType = $_type;
}
function setContentFormat($_format) {
$this->_contentFormat = $_format;
} }
function setMoreData($_status) { function setMoreData($_status) {
$this->_moreData = $_status; $this->_moreData = $_status;
} }
function hasMoreData() {
return $this->_moreData;
}
function setCommand($_command) {
$this->_command = $_command;
}
} }

View File

@ -1,4 +1,18 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
define('ALERT_DISPLAY', 100); define('ALERT_DISPLAY', 100);
@ -21,6 +35,10 @@ define('ALERT_RESULT_ALERT', 221);
define('ALERT_NEXT_MESSAGE', 222); define('ALERT_NEXT_MESSAGE', 222);
define('ALERT_NO_END_OF_DATA', 223); define('ALERT_NO_END_OF_DATA', 223);
// Not (really) implemented.
define('ALERT_SUSPEND', 224); // New in SyncML 1.2
define('ALERT_RESUME', 225); // New in SyncML 1.2
define('MIME_SYNCML_XML', 'application/vnd.syncml+xml'); define('MIME_SYNCML_XML', 'application/vnd.syncml+xml');
define('MIME_SYNCML_WBXML', 'application/vnd.syncml+wbxml'); define('MIME_SYNCML_WBXML', 'application/vnd.syncml+wbxml');
@ -107,7 +125,7 @@ define('RESPONSE_COMMAND_FAILED', 500);
// define('RESPONSE_COMMAND_FAILED', 505); // define('RESPONSE_COMMAND_FAILED', 505);
// define('RESPONSE_COMMAND_FAILED', 506); // define('RESPONSE_COMMAND_FAILED', 506);
// define('RESPONSE_COMMAND_FAILED', 507); // define('RESPONSE_COMMAND_FAILED', 507);
// define('RESPONSE_COMMAND_FAILED', 508); define('RESPONSE_REFRESH_REQUIRED', 508);
// define('RESPONSE_COMMAND_FAILED', 509); // define('RESPONSE_COMMAND_FAILED', 509);
// define('RESPONSE_COMMAND_FAILED', 510); // define('RESPONSE_COMMAND_FAILED', 510);
// define('RESPONSE_COMMAND_FAILED', 511); // define('RESPONSE_COMMAND_FAILED', 511);
@ -117,12 +135,15 @@ define('RESPONSE_COMMAND_FAILED', 500);
// define('RESPONSE_COMMAND_FAILED', 515); // define('RESPONSE_COMMAND_FAILED', 515);
define('RESPONSE_ATOMIC_ROLL_BACK_FAILED', 516); define('RESPONSE_ATOMIC_ROLL_BACK_FAILED', 516);
define('NAME_SPACE_URI_SYNCML', 'syncml:syncml1.0'); define('NAME_SPACE_URI_SYNCML_1_0', 'syncml:syncml1.0');
define('NAME_SPACE_URI_SYNCML_1_1', 'syncml:syncml1.1'); define('NAME_SPACE_URI_SYNCML_1_1', 'syncml:syncml1.1');
define('NAME_SPACE_URI_METINF', 'syncml:metinf'); define('NAME_SPACE_URI_SYNCML_1_2', 'syncml:syncml1.2');
define('NAME_SPACE_URI_METINF_1_0', 'syncml:metinf1.0');
define('NAME_SPACE_URI_METINF_1_1', 'syncml:metinf1.1'); define('NAME_SPACE_URI_METINF_1_1', 'syncml:metinf1.1');
define('NAME_SPACE_URI_DEVINF', 'syncml:devinf'); define('NAME_SPACE_URI_METINF_1_2', 'syncml:metinf1.2');
define('NAME_SPACE_URI_DEVINF_1_0', 'syncml:devinf1.0');
define('NAME_SPACE_URI_DEVINF_1_1', 'syncml:devinf1.1'); define('NAME_SPACE_URI_DEVINF_1_1', 'syncml:devinf1.1');
define('NAME_SPACE_URI_DEVINF_1_2', 'syncml:devinf1.2');
define('CLIENT_SYNC_STARTED', 1); define('CLIENT_SYNC_STARTED', 1);
define('CLIENT_SYNC_FINNISHED', 2); define('CLIENT_SYNC_FINNISHED', 2);
@ -131,8 +152,18 @@ define('SERVER_SYNC_DATA_PENDING', 4);
define('SERVER_SYNC_FINNISHED', 5); define('SERVER_SYNC_FINNISHED', 5);
define('SERVER_SYNC_ACKNOWLEDGED', 6); define('SERVER_SYNC_ACKNOWLEDGED', 6);
// conflict management
define('CONFLICT_CLIENT_WINNING', 0);
define('CONFLICT_SERVER_WINNING', 1);
define('CONFLICT_MERGE_DATA', 2);
define('CONFLICT_RESOLVED_WITH_DUPLICATE', 3);
define('CONFLICT_CLIENT_CHANGES_IGNORED', 4);
define('CONFLICT_CLIENT_REFRESH_ENFORCED', 5);
define('MAX_DATA', 19); define('MAX_DATA', 19);
define('MAX_ENTRIES', 10); define('MAX_ENTRIES', 10); // default
define('MAX_GUID_SIZE', 64);
define('MIN_MSG_LEFT', 200); // Overhead
/** /**
* The Horde_SyncML_State class provides a SyncML state object. * The Horde_SyncML_State class provides a SyncML state object.
@ -148,6 +179,7 @@ define('MAX_ENTRIES', 10);
* @version $Revision$ * @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_SyncML * @package Horde_SyncML
* @modified Joerg Lehrke <jlehrke@noc.de> 2009/01/20, support all syn types
*/ */
class Horde_SyncML_State { class Horde_SyncML_State {
@ -157,6 +189,10 @@ class Horde_SyncML_State {
var $_msgID; var $_msgID;
var $_maxMsgSize;
var $_maxGUIDSize;
var $_targetURI; var $_targetURI;
var $_sourceURI; var $_sourceURI;
@ -169,6 +205,8 @@ class Horde_SyncML_State {
var $_isAuthorized; var $_isAuthorized;
var $_AuthConfirmed;
var $_uri; var $_uri;
var $_uriMeta; var $_uriMeta;
@ -181,7 +219,7 @@ class Horde_SyncML_State {
var $_serverAnchorNext = array(); // written to db after successful sync var $_serverAnchorNext = array(); // written to db after successful sync
var $_clientDeviceInfo = array(); var $_clientDeviceInfo;
// array list of changed items, which need to be synced to the client // array list of changed items, which need to be synced to the client
var $_changedItems; var $_changedItems;
@ -192,8 +230,11 @@ class Horde_SyncML_State {
// array list of added items, which need to be synced to the client // array list of added items, which need to be synced to the client
var $_addedItems; var $_addedItems;
// bool flag that we need to more data // array list of items, which need to be refreshed at the client
var $_syncStatus; var $_conflictItems;
// current session status
var $_syncStatus = 0;
var $_log = array(); var $_log = array();
@ -208,6 +249,22 @@ class Horde_SyncML_State {
*/ */
var $_uidMappings = array(); var $_uidMappings = array();
/**
* Current sync element sent from client.
*
* Stored in state if one element is split into multiple message packets.
*
* @var SyncML_SyncElement
*/
var $curSyncItem;
/**
* Number of sync elements sent to client within current message.
*
* @var _numberOfElements
*/
var $_numberOfElements;
/** /**
* Creates a new instance of Horde_SyncML_State. * Creates a new instance of Horde_SyncML_State.
*/ */
@ -220,7 +277,8 @@ class Horde_SyncML_State {
$this->setPassword($password); $this->setPassword($password);
} }
$this->isAuthorized = false; $this->_isAuthorized = false;
$this->_isAuthConfirmed = false;
} }
/** /**
@ -234,7 +292,7 @@ class Horde_SyncML_State {
* retrieve the real egw uid for a given send uid * retrieve the real egw uid for a given send uid
*/ */
function getUIDMapping($_sentEgwUid) { function getUIDMapping($_sentEgwUid) {
if(isset($this->_uidMappings[$_sentEgwUid])) { if(strlen("$_sentEgwUid") && isset($this->_uidMappings[$_sentEgwUid])) {
return $this->_uidMappings[$_sentEgwUid]; return $this->_uidMappings[$_sentEgwUid];
} }
@ -311,6 +369,16 @@ class Horde_SyncML_State {
return false; return false;
} }
function &getConflictItems($_type)
{
if(isset($this->_conflictItems[$_type]))
{
return $this->_conflictItems[$_type];
}
return false;
}
function getMoreDataPending() function getMoreDataPending()
{ {
return $this->_moreDataPending; return $this->_moreDataPending;
@ -321,6 +389,14 @@ class Horde_SyncML_State {
return $this->_msgID; return $this->_msgID;
} }
function getMaxMsgSizeClient()
{
if (isset($this->_maxMsgSize)) {
return $this->_maxMsgSize;
}
return false;
}
function setWBXML($wbxml) function setWBXML($wbxml)
{ {
$this->_wbxml = $wbxml; $this->_wbxml = $wbxml;
@ -341,6 +417,11 @@ class Horde_SyncML_State {
$this->_addedItems[$_type] = $_addedItems; $this->_addedItems[$_type] = $_addedItems;
} }
function pushAddedItem($_type, $_addedItems)
{
$this->_addedItems[$_type] = $_addedItems;
}
function setChangedItems($_type, $_changedItems) function setChangedItems($_type, $_changedItems)
{ {
$this->_changedItems[$_type] = $_changedItems; $this->_changedItems[$_type] = $_changedItems;
@ -356,9 +437,14 @@ class Horde_SyncML_State {
$this->_deletedItems[$_type] = $_deletedItems; $this->_deletedItems[$_type] = $_deletedItems;
} }
function setMoreDataPending($_state) function addConflictItem($_type, $_conflict)
{ {
$this->_moreDataPending = $_state; $this->_conflictItems[$_type][] = $_conflict;
}
function clearConflictItems($_type)
{
$this->_conflictItems[$_type] = array();
} }
/** /**
@ -370,6 +456,15 @@ class Horde_SyncML_State {
$this->_msgID = $msgID; $this->_msgID = $msgID;
} }
/**
* Setter for property maxMsgSize.
* @param size New value of property maxMsgSize.
*/
function setMaxMsgSize($size)
{
$this->_maxMsgSize = $size;
}
/** /**
* Setter for property locName. * Setter for property locName.
* @param locName New value of property locName. * @param locName New value of property locName.
@ -408,15 +503,20 @@ class Horde_SyncML_State {
{ {
$this->_version = $version; $this->_version = $version;
if ($version == 0) { if ($version == 2) {
$this->_uri = NAME_SPACE_URI_SYNCML; $this->_uri = NAME_SPACE_URI_SYNCML_1_2;
$this->_uriMeta = NAME_SPACE_URI_METINF; $this->_uriMeta = NAME_SPACE_URI_METINF_1_2;
$this->_uriDevInf = NAME_SPACE_URI_DEVINF; $this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_2;
} else { } elseif ($version == 1) {
$this->_uri = NAME_SPACE_URI_SYNCML_1_1; $this->_uri = NAME_SPACE_URI_SYNCML_1_1;
$this->_uriMeta = NAME_SPACE_URI_METINF_1_1; $this->_uriMeta = NAME_SPACE_URI_METINF_1_1;
$this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_1; $this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_1;
} } else {
$this->_uri = NAME_SPACE_URI_SYNCML_1_0;
$this->_uriMeta = NAME_SPACE_URI_METINF_1_0;
$this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_0;
}
} }
function setSessionID($sessionID) function setSessionID($sessionID)
@ -457,6 +557,16 @@ class Horde_SyncML_State {
return $this->_isAuthorized; return $this->_isAuthorized;
} }
function isAuthConfirmed()
{
return $this->_AuthConfirmed;
}
function AuthConfirmed()
{
$this->_AuthConfirmed = true;
}
function clearSync($target) function clearSync($target)
{ {
unset($this->_syncs[$target]); unset($this->_syncs[$target]);
@ -486,6 +596,9 @@ class Horde_SyncML_State {
$targets[] = $target; $targets[] = $target;
} }
// Make sure we keep the order
sort($targets);
return $targets; return $targets;
} }
@ -502,6 +615,7 @@ class Horde_SyncML_State {
return ''; return '';
} }
} }
function getURIMeta() function getURIMeta()
{ {
return $this->_uriMeta; return $this->_uriMeta;
@ -548,8 +662,9 @@ class Horde_SyncML_State {
* this->_locName . $this->_sourceURI . $type . $locid so you can * this->_locName . $this->_sourceURI . $type . $locid so you can
* have different syncs with different devices. If an entry * have different syncs with different devices. If an entry
* already exists, it is overwritten. * already exists, it is overwritten.
* Expired entries can be deleted at the next session start.
*/ */
function setUID($type, $locid, $guid, $ts=0) function setUID($type, $locid, $guid, $ts=0, $expired=0)
{ {
$dt = &$this->getDataTree(); $dt = &$this->getDataTree();
@ -558,6 +673,7 @@ class Horde_SyncML_State {
$gid->set('type', $type); $gid->set('type', $type);
$gid->set('locid', $locid); $gid->set('locid', $locid);
$gid->set('ts', $ts); $gid->set('ts', $ts);
$gid->set('expired', $expired);
$r = $dt->add($gid); $r = $dt->add($gid);
if (is_a($r, 'PEAR_Error')) { if (is_a($r, 'PEAR_Error')) {
@ -657,54 +773,41 @@ class Horde_SyncML_State {
*/ */
function adjustContentType($type, $target = null) function adjustContentType($type, $target = null)
{ {
$ctype; if (is_array($type)) {
if (is_array($type))
{
$ctype = $type['ContentType']; $ctype = $type['ContentType'];
$res = $type; $res = $type;
} } else {
else
{
$ctype = $type; $ctype = $type;
$res = array(); $res = array();
$res['ContentType'] = $ctype; $res['ContentType'] = $ctype;
} }
$deviceInfo = $this->getClientDeviceInfo(); $deviceInfo = $this->getClientDeviceInfo();
$manufacturer = isset($deviceInfo['manufacturer']) ? strtolower($deviceInfo['manufacturer']) : 'unknown';
switch ($manufacturer) {
case 'funambol':
switch (strtolower($deviceInfo['model'])) {
case 'thunderbird':
case 'mozilla plugin':
$res['mayFragment'] = 1;
break;
default:
if (isset($deviceInfo['softwareVersion'])
&& $deviceInfo['softwareVersion'] < 4.0) {
$res['mayFragment'] = 0;
} else {
$res['mayFragment'] = 1;
}
break;
}
break;
default:
$res['mayFragment'] = 1;
break;
}
if (isset($deviceInfo['manufacturer'])) // the funambol specific types need to be encoded in base64
{ switch (strtolower($ctype)) {
switch (strtolower($deviceInfo['manufacturer']))
{
case 'funambol':
if (strtolower($deviceInfo['model']) == 'thunderbird')
{
$res['mayFragment'] = 1;
}
if (isset($deviceInfo['softwareVersion'])
&& $deviceInfo['softwareVersion']{0} == '3')
{
// anything beyond 6.0 supports fragmentation
$res['mayFragment'] = 0;
}
else
{
$res['mayFragment'] = 1;
}
break;
}
}
if (!isset($res['mayFragment']))
{
$res['mayFragment'] = 1;
}
// the funambol specific types need to be encoded in base 64
switch(strtolower($ctype))
{
case 'text/x-s4j-sifc': case 'text/x-s4j-sifc':
case 'text/x-s4j-sife': case 'text/x-s4j-sife':
case 'text/x-s4j-sift': case 'text/x-s4j-sift':
@ -712,17 +815,15 @@ class Horde_SyncML_State {
$res['ContentFormat'] = 'b64'; $res['ContentFormat'] = 'b64';
break; break;
} }
return $res; return $res;
} }
function getPreferedContentType($type) function getPreferedContentType($type)
{ {
$_type = str_replace('./','',$type); $_type = str_replace('./','',$type);
switch(strtolower($_type)) switch (strtolower($_type)) {
{
case 'contacts': case 'contacts':
return 'text/x-vcard'; return 'text/vcard';
break; break;
case 'notes': case 'notes':
@ -730,9 +831,10 @@ class Horde_SyncML_State {
break; break;
case 'calendar': case 'calendar':
case 'events':
case 'tasks': case 'tasks':
case 'caltasks': case 'caltasks':
return 'text/x-vcalendar'; return 'text/calendar';
break; break;
case 'sifcalendar': case 'sifcalendar':
@ -778,6 +880,7 @@ class Horde_SyncML_State {
return 'tasks'; return 'tasks';
break; break;
case 'events':
case 'calendar': case 'calendar':
return 'calendar'; return 'calendar';
break; break;
@ -815,7 +918,6 @@ class Horde_SyncML_State {
} }
} }
/**
/** /**
* Returns the preferred contenttype of the client for the given * Returns the preferred contenttype of the client for the given
* sync data type (database). * sync data type (database).
@ -826,12 +928,33 @@ class Horde_SyncML_State {
function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) { function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) {
$deviceInfo = $this->getClientDeviceInfo(); $deviceInfo = $this->getClientDeviceInfo();
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'])) if(isset($deviceInfo['dataStore'][$_sourceLocURI]['maxGUIDSize']['contentType'])) {
{ $this->_maxGUIDSize = $deviceInfo['dataStore'][$this->_sourceURI]['maxGUIDSize']['contentType'];
return $this->adjustContentType($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'], $_targetLocURI);
} }
Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI .' not found', __FILE__, __LINE__, PEAR_LOG_DEBUG); if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']))
{
$ctype = $deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'];
$cvers = $deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentVersion'];
$cfrmt = $deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentFormat'];
$cprops = $deviceInfo['dataStore'][$_sourceLocURI]['properties'][$ctype][$cvers];
if (isset($deviceInfo['dataStore'][$_sourceLocURI]['maxGUIDSize'])) {
// get UID properties from maxGUIDSize
$cprops['UID']['Size'] = $deviceInfo['dataStore'][$_sourceLocURI]['maxGUIDSize'];
$cprops['UID']['NoTruncate'] = true;
}
$clientPrefs = array(
'ContentType' => $ctype,
'ContentFormat' => $cfrmt,
'mayFragment' => 1,
'Properties' => $cprops,
);
#Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI . " clientPrefs:\n"
# . print_r($clientPrefs, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $this->adjustContentType($clientPrefs, $_targetLocURI);
}
Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI . " not found:\n" . print_r($deviceInfo, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($_targetLocURI != null) if ($_targetLocURI != null)
{ {
@ -841,6 +964,19 @@ class Horde_SyncML_State {
return PEAR::raiseError(_('sourceLocURI not found')); return PEAR::raiseError(_('sourceLocURI not found'));
} }
/**
* Returns the MaxGUIDSize of the client
*/
function getMaxGUIDSizeClient() {
$maxGUIDSize = MAX_GUID_SIZE;
if (isset($this->_maxGUIDSize)) {
$maxGUIDSize = $this->_maxGUIDSize;
}
return $maxGUIDSize;
}
function setClientAnchorNext($type, $a) function setClientAnchorNext($type, $a)
{ {
$this->_clientAnchorNext[$type] = $a; $this->_clientAnchorNext[$type] = $a;
@ -900,6 +1036,11 @@ class Horde_SyncML_State {
*/ */
function getClientDeviceInfo() function getClientDeviceInfo()
{ {
if (isset($this->_clientDeviceInfo) && is_array($this->_clientDeviceInfo)) {
// use cached information
return $this->_clientDeviceInfo;
}
$dt = &$this->getDataTree(); $dt = &$this->getDataTree();
$id = $dt->getId($this->_locName . $this->_sourceURI . 'deviceInfo'); $id = $dt->getId($this->_locName . $this->_sourceURI . 'deviceInfo');
@ -907,7 +1048,7 @@ class Horde_SyncML_State {
return false; return false;
} }
$info = $dt->getObjectById($id); $info = $dt->getObjectById($id);
return $info->get('ClientDeviceInfo'); return $info->get('ClientDeviceInfo');
} }
@ -1070,4 +1211,23 @@ class Horde_SyncML_State {
$this->_receivedAlert222 = (bool)$_status; $this->_receivedAlert222 = (bool)$_status;
} }
function incNumberOfElements() {
$this->_numberOfElements++;
}
function clearNumberOfElements() {
$this->_numberOfElements = 0;
}
function getNumberOfElements() {
if (isset($this->_numberOfElements)) {
return $this->_numberOfElements;
} else {
return false;
}
}
function maxNumberOfElements() {
unset($this->_numberOfElements);
}
} }

View File

@ -1,14 +1,18 @@
<?php <?php
/** /**
* eGroupWare SyncML * eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
* *
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @author Lars Kneschke
* @package syncml
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Lars Kneschke <lkneschke@egroupware.org>
* @author Joerg Lehrke <jlehrke@noc.de>
* @version $Id$ * @version $Id$
*/ */
include_once dirname(__FILE__).'/State.php'; include_once dirname(__FILE__).'/State.php';
/** /**
@ -17,45 +21,42 @@ include_once dirname(__FILE__).'/State.php';
class EGW_SyncML_State extends Horde_SyncML_State class EGW_SyncML_State extends Horde_SyncML_State
{ {
var $table_devinfo = 'egw_syncmldevinfo'; var $table_devinfo = 'egw_syncmldevinfo';
/* /*
* store the mappings of egw uids to client uids * store the mappings of egw uids to client uids
*/ */
var $uidMappings = array(); var $uidMappings = array();
/**
/** * get the local content id from a syncid
* get the local content id from a syncid *
* * @param sting $_syncid id used in syncml
* @param sting $_syncid id used in syncml * @return int local egw content id
* @return int local egw content id */
*/ function get_egwID($_syncid) {
function get_egwID($_syncid)
{
$syncIDParts = explode('-',$_syncid); $syncIDParts = explode('-',$_syncid);
array_shift($syncIDParts); array_shift($syncIDParts);
$_id = implode ('', $syncIDParts); $_id = implode ('', $syncIDParts);
return $_id; return $_id;
} }
/** /**
* when got a entry last added/modified/deleted * when got a entry last added/modified/deleted
* *
* @param $_syncid containing appName-contentid * @param $_syncid containing appName-contentid
* @param $_action string can be add, delete or modify * @param $_action string can be add, delete or modify
* @return string the last timestamp * @return string the last timestamp
*/ */
function getSyncTSforAction($_syncid, $_action) function getSyncTSforAction($_syncid, $_action) {
{
$syncIDParts = explode('-',$_syncid); $syncIDParts = explode('-',$_syncid);
$_appName = array_shift($syncIDParts); $_appName = array_shift($syncIDParts);
$_id = implode ('', $syncIDParts); $_id = implode ('', $syncIDParts);
$ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action); $ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action);
return $ts; return $ts;
} }
/** /**
* get the timestamp for action * get the timestamp for action
* *
@ -63,57 +64,94 @@ class EGW_SyncML_State extends Horde_SyncML_State
* *
* @param string$_appName the appname example: infolog_notes * @param string$_appName the appname example: infolog_notes
* @param string $_action can be modify, add or delete * @param string $_action can be modify, add or delete
* @param string $_ts timestamp where to start searching from * @param string $_ts timestamp where to start searching from
* @return array containing syncIDs with changes * @return array containing syncIDs with changes
*/ */
function getHistory($_appName, $_action, $_ts) function getHistory($_appName, $_action, $_ts) {
{
$guidList = array (); $guidList = array ();
$syncIdList = array (); $syncIdList = array ();
$idList = $GLOBALS['egw']->contenthistory->getHistory($_appName, $_action, $_ts); $idList = $GLOBALS['egw']->contenthistory->getHistory($_appName, $_action, $_ts);
foreach ($idList as $idItem) foreach ($idList as $idItem)
{ {
$syncIdList[] = $_appName . '-' . $idItem; if ($idItem) // ignore inconsistent entries
{
$syncIdList[] = $_appName . '-' . $idItem;
}
} }
return $syncIdList; return $syncIdList;
} }
/**
* Returns the timestamp (if set) of the last change to the
* obj:guid, that was caused by the client. This is stored to
* avoid mirroring these changes back to the client.
*/
function getChangeTS($type, $guid)
{
$mapID = $this->_locName . $this->_sourceURI . $type;
#Horde::logMessage('SyncML: getChangeTS for ' . $mapID .' / '. $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($ts = $GLOBALS['egw']->db->select('egw_contentmap', 'map_timestamp', array(
'map_id' => $mapID,
'map_guid' => $guid,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())
{
#Horde::logMessage('SyncML: getChangeTS changets is ' . $GLOBALS['egw']->db->from_timestamp($ts), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $GLOBALS['egw']->db->from_timestamp($ts);
}
return false;
}
/** /**
* Retrieves information about the clients device info if any. Returns * Returns the timestamp (if set) of the last change to the
* false if no info found or a DateTreeObject with at least the * obj:guid, that was caused by the client. This is stored to
* following attributes: * avoid mirroring these changes back to the client.
* */
* a array containing all available infos about the device function getChangeTS($type, $guid) {
*/ $mapID = $this->_locName . $this->_sourceURI . $type;
function getClientDeviceInfo()
{ #Horde::logMessage('SyncML: getChangeTS for ' . $mapID
if(($deviceID = $GLOBALS['egw']->db->select('egw_syncmldeviceowner', 'owner_devid',array ( # . ' / '. $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI, if ($ts = $GLOBALS['egw']->db->select('egw_contentmap', 'map_timestamp',
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) array(
'map_id' => $mapID,
'map_guid' => $guid,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn()) {
#Horde::logMessage('SyncML: getChangeTS changets is '
# . $GLOBALS['egw']->db->from_timestamp($ts),
# __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $GLOBALS['egw']->db->from_timestamp($ts);
}
return false;
}
/**
* Returns the exceptions for a GUID which the client knows of
*/
function getGUIDExceptions($type, $guid) {
$mapID = $this->_locName . $this->_sourceURI . $type;
#Horde::logMessage('SyncML: getChangeTS for ' . $mapID
# . ' / '. $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$guid_exceptions = array();
$where = array ('map_id' => $mapID,);
$where[] = "map_guid LIKE '$guid" . ":%'";
// Fetch all exceptions which the client knows of
foreach ($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', $where,
__LINE__,__FILE__, false, '', 'syncml') as $row)
{ {
$parts = preg_split('/:/', $row['map_guid']);
$Id = $parts[0];
$extension = $parts[1];
$guid_exceptions[$extension] = $row['map_guid'];
}
return $guid_exceptions;
}
/**
* Retrieves information about the clients device info if any. Returns
* false if no info found or a DateTreeObject with at least the
* following attributes:
*
* a array containing all available infos about the device
*/
function getClientDeviceInfo() {
#Horde::logMessage("SyncML: getClientDeviceInfo " . $this->_locName
# . ", " . $this->_sourceURI, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (isset($this->_clientDeviceInfo)
&& is_array($this->_clientDeviceInfo)) {
// use cached information
return $this->_clientDeviceInfo;
}
if (($deviceID = $GLOBALS['egw']->db->select('egw_syncmldeviceowner',
'owner_devid',
array (
'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
$cols = array( $cols = array(
'dev_dtdversion', 'dev_dtdversion',
'dev_numberofchanges', 'dev_numberofchanges',
@ -129,156 +167,156 @@ class EGW_SyncML_State extends Horde_SyncML_State
'dev_utc', 'dev_utc',
); );
#Horde::logMessage("SyncML: getClientDeviceInfo $deviceID", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$where = array( $where = array(
'dev_id' => $deviceID, 'dev_id' => $deviceID,
); );
if (($row = $GLOBALS['egw']->db->select('egw_syncmldevinfo', $cols, $where, __LINE__, __FILE__, false, '', 'syncml')->fetch())) if (($row = $GLOBALS['egw']->db->select('egw_syncmldevinfo',
{ $cols, $where, __LINE__, __FILE__, false, '', 'syncml')->fetch())) {
return array ( $deviceMaxEntries = 'maxEntries-' . $this->_sourceURI;
'DTDVersion' => $row['dev_dtdversion'], $deviceUIDExtension = 'uidExtension-' . $this->_sourceURI;
'supportNumberOfChanges'=> $row['dev_numberofchanges'], $deviceNonBlockingAllday = 'nonBlockingAllday-' . $this->_sourceURI;
'supportLargeObjs' => $row['dev_largeobjs'], $syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
'UTC' => $row['dev_utc'], $this->_clientDeviceInfo = array (
'softwareVersion' => $row['dev_swversion'], 'DTDVersion' => $row['dev_dtdversion'],
'hardwareVersion' => $row['dev_hwversion'], 'supportNumberOfChanges' => $row['dev_numberofchanges'],
'firmwareVersion' => $row['dev_fwversion'], 'supportLargeObjs' => $row['dev_largeobjs'],
'oem' => $row['dev_oem'], 'UTC' => $row['dev_utc'],
'model' => $row['dev_model'], 'softwareVersion' => $row['dev_swversion'],
'manufacturer' => $row['dev_manufacturer'], 'hardwareVersion' => $row['dev_hwversion'],
'deviceType' => $row['dev_devicetype'], 'firmwareVersion' => $row['dev_fwversion'],
'dataStore' => unserialize($row['dev_datastore']), 'oem' => $row['dev_oem'],
'model' => $row['dev_model'],
'manufacturer' => $row['dev_manufacturer'],
'deviceType' => $row['dev_devicetype'],
'maxMsgSize' => $this->_maxMsgSize,
'maxEntries' => $syncml_prefs[$deviceMaxEntries],
'uidExtension' => $syncml_prefs[$deviceUIDExtension],
'nonBlockingAllday' => $syncml_prefs[$deviceNonBlockingAllday],
'dataStore' => unserialize($row['dev_datastore']),
); );
return $this->_clientDeviceInfo;
} }
} }
return false; return false;
} }
/** /**
* returns GUIDs of all client items * returns GUIDs of all client items
*/ */
function _getClientItems($type) function getClientItems() {
{ $mapID = $this->_locName . $this->_sourceURI . $this->_targetURI;
$mapID = $this->_locName . $this->_sourceURI . $type;
$guids = array(); $guids = array();
foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array( foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array(
'map_id' => $mapID, 'map_id' => $mapID,
'map_expired' => 0, 'map_expired' => false,
), __LINE__, __FILE__, false, '', 'syncml') as $row) ), __LINE__, __FILE__, false, '', 'syncml') as $row) {
{ $guids[] = $row['map_guid'];
$guids[] = $row['map_guid']; }
} return $guids ? $guids : false;
return $guids ? $guids : false;
} }
/** /**
* Retrieves the Horde server guid (like * Retrieves the Horde server guid (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) for a given client * kronolith:0d1b415fc124d3427722e95f0e926b75) for a given client
* locid. Returns false if no such id is stored yet. * locid. Returns false if no such id is stored yet.
* *
* Opposite of getLocId which returns the locid for a given guid. * Opposite of getLocId which returns the locid for a given guid.
*/ */
function getGlobalUID($type, $locid) function getGlobalUID($type, $locid) {
{ $mapID = $this->_locName . $this->_sourceURI . $type;
$mapID = $this->_locName . $this->_sourceURI . $type;
#Horde::logMessage('SyncML: search GlobalUID for ' . $mapID .' / '.$locid, __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage('SyncML: search GlobalUID for ' . $mapID .' / '.$locid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array( return $GLOBALS['egw']->db->select('egw_contentmap', 'map_guid',
'map_id' => $mapID, array(
'map_locuid' => $locid, 'map_id' => $mapID,
'map_expired' => 0, 'map_locuid' => $locid,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn(); 'map_expired' => false,
} ), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn();
}
/** /**
* Converts a EGW GUID (like * Converts a EGW GUID (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) to a client ID as * kronolith:0d1b415fc124d3427722e95f0e926b75) to a client ID as
* used by the sync client (like 12) returns false if no such id * used by the sync client (like 12) returns false if no such id
* is stored yet. * is stored yet.
*/ */
function getLocID($type, $guid) function getLocID($type, $guid) {
{ $mapID = $this->_locName . $this->_sourceURI . $type;
$mapID = $this->_locName . $this->_sourceURI . $type;
Horde::logMessage('SyncML: search LocID for ' . $mapID .' / '.$guid, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: search LocID for ' . $mapID . ' / ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (($locuid = $GLOBALS['egw']->db->select('egw_contentmap', 'map_locuid', array( if (($locuid = $GLOBALS['egw']->db->select('egw_contentmap', 'map_locuid', array(
'map_id' => $mapID, 'map_id' => $mapID,
'map_guid' => $guid 'map_guid' => $guid
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) ), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
{ Horde::logMessage('SyncML: found LocID: '.$locuid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage('SyncML: found LocID: '.$locuid, __FILE__, __LINE__, PEAR_LOG_DEBUG); }
} return $locuid;
return $locuid; }
}
/** /**
* Retrieves information about the previous sync if any. Returns * Retrieves information about the previous sync if any. Returns
* false if no info found or a DateTreeObject with at least the * false if no info found or a DateTreeObject with at least the
* following attributes: * following attributes:
* *
* ClientAnchor: the clients Next Anchor of the previous sync. * ClientAnchor: the clients Next Anchor of the previous sync.
* ServerAnchor: the Server Next Anchor of the previous sync. * ServerAnchor: the Server Next Anchor of the previous sync.
*/ */
function getSyncSummary($type) function getSyncSummary($type) {
{
$deviceID = $this->_locName . $this->_sourceURI; $deviceID = $this->_locName . $this->_sourceURI;
#Horde::logMessage("SyncML: get SYNCSummary for $deviceID", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: getSyncSummary for $deviceID", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (($row = $GLOBALS['egw']->db->select('egw_syncmlsummary', array('sync_serverts','sync_clientts'), array( if (($row = $GLOBALS['egw']->db->select('egw_syncmlsummary', array('sync_serverts','sync_clientts'), array(
'dev_id' => $deviceID, 'dev_id' => $deviceID,
'sync_path' => $type 'sync_path' => $type
), __LINE__, __FILE__, false, '', 'syncml')->fetch())) ), __LINE__, __FILE__, false, '', 'syncml')->fetch())) {
{ Horde::logMessage("SyncML: getSyncSummary for $deviceID serverts: ".$row['sync_serverts']." clients: ".$row['sync_clientts'], __FILE__, __LINE__, PEAR_LOG_DEBUG);
#Horde::logMessage("SyncML: get SYNCSummary for $deviceID serverts: ".$row['sync_serverts']." clients: ".$row['sync_clientts'], __FILE__, __LINE__, PEAR_LOG_DEBUG); return array(
return array( 'ClientAnchor' => $row['sync_clientts'],
'ClientAnchor' => $row['sync_clientts'], 'ServerAnchor' => $row['sync_serverts'],
'ServerAnchor' => $row['sync_serverts'], );
); }
} return false;
return false; }
}
function isAuthorized() function isAuthorized() {
{ if (!$this->_isAuthorized) {
if (!$this->_isAuthorized) if(!isset($this->_locName) && !isset($this->_password)) {
{
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); Horde::logMessage('SyncML: Authentication not yet possible currently. Username and password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE; return FALSE;
} }
if(!isset($this->_password)) if(!isset($this->_password)) {
{
Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE; return FALSE;
} }
if(strpos($this->_locName,'@') === False) if(strpos($this->_locName,'@') === False) {
{
$this->_locName .= '@'.$GLOBALS['egw_info']['server']['default_domain']; $this->_locName .= '@'.$GLOBALS['egw_info']['server']['default_domain'];
} }
#Horde::logMessage('SyncML: authenticate with username: ' . $this->_locName . ' and password: ' . $this->_password, __FILE__, __LINE__, PEAR_LOG_DEBUG); #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['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text')) {
{
$this->_isAuthorized = true; if ($GLOBALS['egw_info']['user']['apps']['syncml']) {
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG); $this->_isAuthorized = true;
} Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
else } else {
{ $this->_isAuthorized = false;
Horde::logMessage('SyncML is not enabled for this user', __FILE__, __LINE__, PEAR_LOG_ERROR);
}
} else {
$this->_isAuthorized = false; $this->_isAuthorized = false;
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_INFO);
} }
} } else {
else
{
// store sessionID in a variable, because ->verify maybe resets that value // store sessionID in a variable, because ->verify maybe resets that value
$sessionID = session_id(); $sessionID = session_id();
if(!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3')) { if(!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3')) {
@ -290,11 +328,10 @@ class EGW_SyncML_State extends Horde_SyncML_State
} }
/** /**
* Removes all locid<->guid mappings for the given type. * Removes all locid<->guid mappings for the given type.
* Returns always true. * Returns always true.
*/ */
function removeAllUID($type) function removeAllUID($type) {
{
$mapID = $this->_locName . $this->_sourceURI . $type; $mapID = $this->_locName . $this->_sourceURI . $type;
Horde::logMessage("SyncML: state->removeAllUID(type=$type)", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: state->removeAllUID(type=$type)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
@ -302,17 +339,16 @@ class EGW_SyncML_State extends Horde_SyncML_State
$GLOBALS['egw']->db->delete('egw_contentmap', array('map_id' => $mapID), __LINE__, __FILE__, 'syncml'); $GLOBALS['egw']->db->delete('egw_contentmap', array('map_id' => $mapID), __LINE__, __FILE__, 'syncml');
return true; return true;
} }
/** /**
* Used in SlowSync * Used in SlowSync
* Removes all locid<->guid mappings for the given type, * Removes all locid<->guid mappings for the given type,
* that are older than $ts. * that are older than $ts.
* *
* Returns always true. * Returns always true.
*/ */
function removeOldUID($type, $ts) function removeOldUID($type, $ts) {
{
$mapID = $this->_locName . $this->_sourceURI . $type; $mapID = $this->_locName . $this->_sourceURI . $type;
$where[] = "map_id = '".$mapID."' AND map_timestamp < '".$GLOBALS['egw']->db->to_timestamp($ts)."'"; $where[] = "map_id = '".$mapID."' AND map_timestamp < '".$GLOBALS['egw']->db->to_timestamp($ts)."'";
@ -324,102 +360,151 @@ class EGW_SyncML_State extends Horde_SyncML_State
} }
/** /**
* Removes the locid<->guid mapping for the given locid. Returns * Used at session end to cleanup expired entries
* the guid that was removed or false if no mapping entry was * Removes all locid<->guid mappings for the given type,
* found. * that are marked as expired and older than $ts.
*/ *
function removeUID($type, $locid) * Returns always true.
{ */
function removeExpiredUID($type, $ts) {
$mapID = $this->_locName . $this->_sourceURI . $type; $mapID = $this->_locName . $this->_sourceURI . $type;
$where['map_id'] = $mapID;
$where['map_expired'] = true;
$where[] = "map_timestamp <= '".$GLOBALS['egw']->db->to_timestamp($ts)."'";
Horde::logMessage("SyncML: state->removeExpiredUID(type=$type)",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$GLOBALS['egw']->db->delete('egw_contentmap', $where,
__LINE__, __FILE__, 'syncml');
return true;
}
/**
* Check if an entry is already expired
*
* Returns true for expired mappings.
*/
function isExpiredUID($type, $locid) {
$mapID = $this->_locName . $this->_sourceURI . $type;
$expired = false;
$where = array(
'map_id' => $mapID,
'map_locuid' => $locid,
);
if (($expired = $GLOBALS['egw']->db->select('egw_contentmap', 'map_expired',
$where, __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
Horde::logMessage('SyncML: found LocID: '. $locid,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $expired;
}
/**
* Removes the locid<->guid mapping for the given locid. Returns
* the guid that was removed or false if no mapping entry was
* found.
*/
function removeUID($type, $locid) {
$mapID = $this->_locName . $this->_sourceURI . $type;
$where = array ( $where = array (
'map_id' => $mapID, 'map_id' => $mapID,
'map_locuid' => $locid 'map_locuid' => $locid
); );
if (!($guid = $GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', $where, __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) if (!($guid = $GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', $where,
{ __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid) : nothing to remove", __FILE__, __LINE__, PEAR_LOG_INFO); Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid)"
. " nothing to remove", __FILE__, __LINE__, PEAR_LOG_INFO);
return false; return false;
} }
Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid) : removing guid:$guid", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid): "
. "removing guid $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml'); $GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
return $guid; return $guid;
} }
/** /**
* Puts a given client $locid and Horde server $guid pair into the * Puts a given client $locid and Horde server $guid pair into the
* map table to allow mapping between the client's and server's * map table to allow mapping between the client's and server's
* IDs. Actually there are two maps: from the localid to the guid * IDs. Actually there are two maps: from the localid to the guid
* and vice versa. The localid is converted to a key as follows: * and vice versa. The localid is converted to a key as follows:
* this->_locName . $this->_sourceURI . $type . $locid so you can * this->_locName . $this->_sourceURI . $type . $locid so you can
* have different syncs with different devices. If an entry * have different syncs with different devices. If an entry
* already exists, it is overwritten. * already exists, it is overwritten.
*/ * Expired entries can be deleted at the next session start.
function setUID($type, $locid, $_guid, $ts=0) */
{ function setUID($type, $locid, $_guid, $ts=0, $expired=false) {
#Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts ", __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage("SyncML: setUID $type, $locid, $_guid, $ts ", __FILE__, __LINE__, PEAR_LOG_DEBUG);
#Horde::logMessage("SyncML: setUID ". $this->getUIDMapping($guid), __FILE__, __LINE__, PEAR_LOG_DEBUG);
// problem: entries created from client, come here with the (long) server guid,
// but getUIDMapping does not know them and can not map server-guid <--> client guid
$guid = $this->getUIDMapping($_guid);
if($guid === false)
{
// this message is not really usefull here because setUIDMapping is only called when adding content to the client,
// however setUID is called also when adding content from the client. So in all other conditions this
// message will be logged.
Horde::logMessage("SyncML: setUID $type, $locid, $guid something went wrong!!! Mapping not found.", __FILE__, __LINE__, PEAR_LOG_INFO);
$guid = $_guid;
//return false;
}
Horde::logMessage("SyncML: setUID $_guid => $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if(!$ts) $ts = time(); if (!strlen("$_guid")) {
// We can't handle this case otherwise
return;
}
Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts ", __FILE__, __LINE__, PEAR_LOG_DEBUG); // problem: entries created from client, come here with the (long) server guid,
// but getUIDMapping does not know them and can not map server-guid <--> client guid
$guid = $this->getUIDMapping($_guid);
if($guid === false) {
// this message is not really usefull here because setUIDMapping is only called when adding content to the client,
// however setUID is called also when adding content from the client. So in all other conditions this
// message will be logged.
//Horde::logMessage("SyncML: setUID $type, $locid, $guid something went wrong!!! Mapping not found.", __FILE__, __LINE__, PEAR_LOG_INFO);
$guid = $_guid;
//return false;
}
#Horde::logMessage("SyncML: setUID $_guid => $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$mapID = $this->_locName . $this->_sourceURI . $type; if(!$ts) $ts = time();
// delete all client id's Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts ",
$where = array( __FILE__, __LINE__, PEAR_LOG_DEBUG);
'map_id' => $mapID,
'map_locuid' => $locid,
);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
// delete all egw id's $mapID = $this->_locName . $this->_sourceURI . $type;
$where = array(
'map_id' => $mapID,
'map_guid' => $guid,
);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
$data = $where + array( // expire all client id's
'map_locuid' => $locid, $where = array(
'map_timestamp' => $ts, 'map_id' => $mapID,
'map_expired' => 0, 'map_locuid' => $locid,
); );
$GLOBALS['egw']->db->insert('egw_contentmap', $data, $where, __LINE__, __FILE__, 'syncml'); $data = array (
'map_expired' => true,
);
$GLOBALS['egw']->db->delete('egw_contentmap', $where,
__LINE__, __FILE__, 'syncml');
#Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts $mapID", __FILE__, __LINE__, PEAR_LOG_DEBUG); // delete all egw id's
} $where = array(
'map_id' => $mapID,
'map_guid' => $guid,
);
$GLOBALS['egw']->db->delete('egw_contentmap', $where,
__LINE__, __FILE__, 'syncml');
$data = $where + array(
'map_locuid' => $locid,
'map_timestamp' => $ts,
'map_expired' => ($expired ? true : false),
);
$GLOBALS['egw']->db->insert('egw_contentmap', $data, $where,
__LINE__, __FILE__, 'syncml');
}
/** /**
* writes clients deviceinfo into database * writes clients deviceinfo into database
*/ */
function writeClientDeviceInfo() function writeClientDeviceInfo() {
{ if (!isset($this->_clientDeviceInfo)
if (!isset($this->_clientDeviceInfo) || !is_array($this->_clientDeviceInfo)) || !is_array($this->_clientDeviceInfo)) {
{
return false; return false;
} }
if(!isset($this->size_dev_hwversion)) if(!isset($this->size_dev_hwversion)) {
{
$tableDefDevInfo = $GLOBALS['egw']->db->get_table_definitions('syncml',$this->table_devinfo); $tableDefDevInfo = $GLOBALS['egw']->db->get_table_definitions('syncml',$this->table_devinfo);
$this->size_dev_hwversion = $tableDefDevInfo['fd']['dev_hwversion']['precision']; $this->size_dev_hwversion = $tableDefDevInfo['fd']['dev_hwversion']['precision'];
unset($tableDefDevInfo); unset($tableDefDevInfo);
@ -437,77 +522,89 @@ class EGW_SyncML_State extends Horde_SyncML_State
'dev_fwversion' => $firmwareVersion, 'dev_fwversion' => $firmwareVersion,
); );
if (($deviceID = $GLOBALS['egw']->db->select('egw_syncmldevinfo', 'dev_id', $where, __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) if (($deviceID = $GLOBALS['egw']->db->select('egw_syncmldevinfo', 'dev_id', $where,
{ __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
$data = array ( $data = array (
'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']), 'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']),
); );
$GLOBALS['egw']->db->update('egw_syncmldevinfo', $data, $where, __LINE__, __FILE__, 'syncml'); $GLOBALS['egw']->db->update('egw_syncmldevinfo', $data, $where,
} __LINE__, __FILE__, 'syncml');
else } else {
{
$data = array ( $data = array (
'dev_dtdversion' => $this->_clientDeviceInfo['DTDVersion'], 'dev_dtdversion' => $this->_clientDeviceInfo['DTDVersion'],
'dev_numberofchanges' => $this->_clientDeviceInfo['supportNumberOfChanges'] ? true : false, 'dev_numberofchanges' => ($this->_clientDeviceInfo['supportNumberOfChanges'] ? true : false),
'dev_largeobjs' => $this->_clientDeviceInfo['supportLargeObjs'] ? true : false, 'dev_largeobjs' => ($this->_clientDeviceInfo['supportLargeObjs'] ? true : false),
'dev_utc' => $this->_clientDeviceInfo['UTC'] ? true : false, 'dev_utc' => ($this->_clientDeviceInfo['UTC'] ? true : false),
'dev_swversion' => $softwareVersion, 'dev_swversion' => $softwareVersion,
'dev_hwversion' => $hardwareVersion, 'dev_hwversion' => $hardwareVersion,
'dev_fwversion' => $firmwareVersion, 'dev_fwversion' => $firmwareVersion,
'dev_oem' => $this->_clientDeviceInfo['oem'], 'dev_oem' => $this->_clientDeviceInfo['oem'],
'dev_model' => $this->_clientDeviceInfo['model'], 'dev_model' => $this->_clientDeviceInfo['model'],
'dev_manufacturer' => $this->_clientDeviceInfo['manufacturer'], 'dev_manufacturer' => $this->_clientDeviceInfo['manufacturer'],
'dev_devicetype' => $this->_clientDeviceInfo['deviceType'], 'dev_devicetype' => $this->_clientDeviceInfo['deviceType'],
'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']), 'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']),
); );
$GLOBALS['egw']->db->insert('egw_syncmldevinfo', $data, $where, __LINE__, __FILE__, 'syncml'); $GLOBALS['egw']->db->insert('egw_syncmldevinfo', $data, $where, __LINE__, __FILE__, 'syncml');
$deviceID = $GLOBALS['egw']->db->get_last_insert_id('egw_syncmldevinfo', 'dev_id'); $deviceID = $GLOBALS['egw']->db->get_last_insert_id('egw_syncmldevinfo', 'dev_id');
} }
$data = array (
'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI,
'owner_devid' => $deviceID,
);
$where = array ( $where = array (
'owner_locname' => $this->_locName, 'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI, 'owner_deviceid' => $this->_sourceURI,
); );
$GLOBALS['egw']->db->insert('egw_syncmldeviceowner', $data, $where, __LINE__, __FILE__, 'syncml');
if ($GLOBALS['egw']->db->select('egw_syncmldeviceowner', 'owner_devid', $where,
__LINE__, __FILE__, false, '', 'syncml')->fetchColumn()) {
$data = array (
'owner_devid' => $deviceID,
);
$GLOBALS['egw']->db->update('egw_syncmldeviceowner', $data, $where,
__LINE__, __FILE__, 'syncml');
} else {
$data = array (
'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI,
'owner_devid' => $deviceID,
);
$GLOBALS['egw']->db->insert('egw_syncmldeviceowner', $data, $where,
__LINE__, __FILE__, 'syncml');
}
} }
/** /**
* After a successful sync, the client and server's Next Anchors * After a successful sync, the client and server's Next Anchors
* are written to the database so they can be used to negotiate * are written to the database so they can be used to negotiate
* upcoming syncs. * upcoming syncs.
*/ */
function writeSyncSummary() function writeSyncSummary() {
{ #parent::writeSyncSummary();
#parent::writeSyncSummary();
if (!isset($this->_serverAnchorNext) || !is_array($this->_serverAnchorNext)) if (!isset($this->_serverAnchorNext)
{ || !is_array($this->_serverAnchorNext)) {
return; return;
} }
$deviceID = $this->_locName . $this->_sourceURI; $deviceID = $this->_locName . $this->_sourceURI;
foreach((array)$this->_serverAnchorNext as $type => $a) foreach((array)$this->_serverAnchorNext as $type => $a) {
{ Horde::logMessage("SyncML: write SYNCSummary for $deviceID "
Horde::logMessage("SyncML: write SYNCSummary for $deviceID $type serverts: $a clients: ".$this->_clientAnchorNext[$type], __FILE__, __LINE__, PEAR_LOG_DEBUG); . "$type serverts: $a clients: "
. $this->_clientAnchorNext[$type],
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$where = array( $where = array(
'dev_id' => $deviceID, 'dev_id' => $deviceID,
'sync_path' => $type, 'sync_path' => $type,
); );
$data = array( $data = array(
'sync_serverts' => $a, 'sync_serverts' => $a,
'sync_clientts' => $this->_clientAnchorNext[$type] 'sync_clientts' => $this->_clientAnchorNext[$type]
); );
$GLOBALS['egw']->db->insert('egw_syncmlsummary', $data, $where, __LINE__, __FILE__, 'syncml'); $GLOBALS['egw']->db->insert('egw_syncmlsummary', $data, $where,
} __LINE__, __FILE__, 'syncml');
} }
}
} }

View File

@ -1,230 +1,466 @@
<?php <?php
/** /**
* $Horde: framework/SyncML/SyncML/Sync.php,v 1.7 2004/09/14 04:27:05 chuck Exp $ * eGroupWare - SyncML based on Horde 3
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
* *
* See the enclosed file COPYING for license information (LGPL). If you * Using the PEAR Log class (which need to be installed!)
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com> * @link http://www.egroupware.org
* @version $Revision$ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @since Horde 3.0 * @package api
* @package Horde_SyncML * @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
class Horde_SyncML_Sync { class Horde_SyncML_Sync {
/** /**
* Target, either contacts, notes, events, * Target, either contacts, notes, events,
*/ */
var $_targetLocURI; var $_targetLocURI;
var $_sourceLocURI; var $_sourceLocURI;
/** var $_locName;
* Return if all commands success.
*/
var $globalSuccess;
/**
* This is the content type to use to export data.
*/
var $preferedContentType;
/**
* Do have the sync data loaded from the database already?
*/
var $syncDataLoaded;
function &factory($alert)
{
Horde::logMessage('SyncML: new sync for alerttype ' . $alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch ($alert) {
case ALERT_TWO_WAY:
include_once 'Horde/SyncML/Sync/TwoWaySync.php';
return $sync = new Horde_SyncML_Sync_TwoWaySync();
case ALERT_SLOW_SYNC:
include_once 'Horde/SyncML/Sync/SlowSync.php';
return $sync = new Horde_SyncML_Sync_SlowSync();
case ALERT_ONE_WAY_FROM_CLIENT:
include_once 'Horde/SyncML/Sync/OneWayFromClientSync.php';
return $sync = new Horde_SyncML_Sync_OneWayFromClientSync();
case ALERT_REFRESH_FROM_CLIENT:
include_once 'Horde/SyncML/Sync/RefreshFromClientSync.php';
return $sync = new Horde_SyncML_Sync_RefreshFromClientSync();
case ALERT_ONE_WAY_FROM_SERVER:
include_once 'Horde/SyncML/Sync/OneWayFromServerSync.php';
return $sync = new Horde_SyncML_Sync_OneWayFromServerSync();
case ALERT_REFRESH_FROM_SERVER:
include_once 'Horde/SyncML/Sync/RefreshFromServerSync.php';
return $sync = new Horde_SyncML_Sync_RefreshFromServerSync();
}
require_once 'PEAR.php';
return PEAR::raiseError('Alert ' . $alert . ' not found.');
}
function nextSyncCommand($currentCmdID, &$syncCommand, &$output)
{
$result = $this->runSyncCommand($syncCommand);
return $syncCommand->output($currentCmdID, $output);
}
function startSync($currentCmdID, &$output)
{
return $currentCmdID;
}
function endSync($currentCmdID, &$output)
{
return $currentCmdID;
}
/** /**
* Here's where the actual processing of a client-sent Sync * The synchronization method, one of the ALERT_* constants.
* Command takes place. Entries are added, deleted or replaced *
* from the server database by using Horde API (Registry) calls. * @var integer
*/ */
var $_syncType;
/**
* Return if all commands success.
*/
var $globalSuccess;
/**
* This is the content type to use to export data.
*/
var $preferedContentType;
/**
* Optional filter expression for this content.
*
* @var string
*/
var $_filterExpression = '';
/**
* Do have the sync data loaded from the database already?
*/
var $syncDataLoaded;
function &factory($alert) {
Horde::logMessage('SyncML: new sync for alerttype ' . $alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch ($alert) {
case ALERT_TWO_WAY:
include_once 'Horde/SyncML/Sync/TwoWaySync.php';
return $sync = new Horde_SyncML_Sync_TwoWaySync();
case ALERT_SLOW_SYNC:
include_once 'Horde/SyncML/Sync/SlowSync.php';
return $sync = new Horde_SyncML_Sync_SlowSync();
case ALERT_ONE_WAY_FROM_CLIENT:
include_once 'Horde/SyncML/Sync/OneWayFromClientSync.php';
return $sync = new Horde_SyncML_Sync_OneWayFromClientSync();
case ALERT_REFRESH_FROM_CLIENT:
include_once 'Horde/SyncML/Sync/RefreshFromClientSync.php';
return $sync = new Horde_SyncML_Sync_RefreshFromClientSync();
case ALERT_ONE_WAY_FROM_SERVER:
include_once 'Horde/SyncML/Sync/OneWayFromServerSync.php';
return $sync = new Horde_SyncML_Sync_OneWayFromServerSync();
case ALERT_REFRESH_FROM_SERVER:
include_once 'Horde/SyncML/Sync/RefreshFromServerSync.php';
return $sync = new Horde_SyncML_Sync_RefreshFromServerSync();
}
require_once 'PEAR.php';
return PEAR::raiseError('Alert ' . $alert . ' not found.');
}
function nextSyncCommand($currentCmdID, &$syncCommand, &$output) {
$result = $this->runSyncCommand($syncCommand);
return $syncCommand->output($currentCmdID, $output);
}
function startSync($currentCmdID, &$output) {
return $currentCmdID;
}
function endSync($currentCmdID, &$output) {
return $currentCmdID;
}
/**
* Setter for property sourceURI.
*
* @param string $sourceURI New value of property sourceLocURI.
*/
function setSourceLocURI($sourceURI) {
$this->_sourceLocURI = $sourceURI;
}
/**
* Setter for property targetURI.
*
* @param string $targetURI New value of property targetLocURI.
*/
function setTargetLocURI($targetURI) {
$this->_targetLocURI = $targetURI;
}
/**
* Setter for property syncType.
*
* @param integer $syncType New value of property syncType.
*/
function setSyncType($syncType) {
$this->_syncType = $syncType;
}
/**
* Setter for property locName.
*
* @param string $locName New value of property locName.
*/
function setLocName($locName) {
$this->_locName = $locName;
}
/**
* Setter for property filterExpression.
*
* @param string $expression New value of property filterExpression.
*/
function setFilterExpression($expression) {
$this->_filterExpression = $expression;
}
/**
* Here's where the actual processing of a client-sent Sync
* Command takes place. Entries are added, deleted or replaced
* from the server database by using Horde API (Registry) calls.
*/
function runSyncCommand(&$command) { function runSyncCommand(&$command) {
#Horde::logMessage('SyncML: content type is ' . $command->getContentType() .' moreData '. $command->_moreData, __FILE__, __LINE__, PEAR_LOG_DEBUG);
global $registry; global $registry;
$history = $GLOBALS['egw']->contenthistory; $history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
if(isset($state->_moreData['luid'])) { if ($command->hasMoreData()) {
if(($command->_luid == $state->_moreData['luid'])) { Horde::logMessage('SyncML: moreData: TRUE', __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage('SyncML: got next moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG); $command->setStatus(RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED);
$lastChunks = implode('',$state->_moreData['chunks']); return true;
$command->_content = $lastChunks.$command->_content; }
$stringlen1 = strlen($lastChunks);
$stringlen2 = strlen($command->_content); $type = $this->_targetLocURI;
if(!$command->_moreData && strlen($command->_content) != $state->_moreData['contentSize']) { $syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
$command->_status = RESPONSE_SIZE_MISMATCH; if (isset($syncml_prefs[$type])) {
$state->_moreData = array(); $sync_conflicts = $syncml_prefs[$type];
} else {
return; $sync_conflicts = CONFLICT_SERVER_WINNING;
} elseif(!$command->_moreData && strlen($command->_content) == $state->_moreData['contentSize']) { }
$state->_moreData = array();
Horde::logMessage('SyncML: chunk ended successful type is ' . $command->getContentType() .' content is '. $command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG); $state->setLocName($this->_locName);
} $locName = $state->getLocName();
} else { $sourceURI = $state->getSourceURI();
// alert 223 needed too $hordeType = $state->getHordeType($type);
#$command->_status = ALERT_NO_END_OF_DATA; $refts = $state->getServerAnchorLast($type);
$state->setTargetURI($type);
$state->_moreData = array(); $changes = array();
// First we get all changes done after the previous sync start
return; foreach ($registry->call($hordeType. '/listBy',
array('action' => 'modify',
'timestamp' => $refts,
'type' => $type,
'filter' => $this->_filterExpression)) as $change) {
// now we have to remove the ones
// that came from the last sync with this client
$guid_ts = $state->getSyncTSforAction($change, 'modify');
$sync_ts = $state->getChangeTS($type, $change);
if ($sync_ts && $sync_ts == $guid_ts) {
// Change was done by us upon request of client.
Horde::logMessage("SyncML: change: $change ignored, " .
"came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue;
} }
$changes[] = $change;
} }
// don't add/replace the data currently, they are not yet complete Horde::logMessage('SyncML: runSyncCommand found ' . count($changes) .
if($command->_moreData == TRUE) { " possible conflicts for $type", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->_moreData['chunks'][] = $command->_content;
$state->_moreData['luid'] = $command->_luid;
// gets only set with the first chunk of data
if(isset($command->_contentSize))
$state->_moreData['contentSize'] = $command->_contentSize;
$command->_status = RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED;
Horde::logMessage('SyncML: added moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return;
}
$hordeType = $type = $this->_targetLocURI;
$hordeType = $state->getHordeType($hordeType);
if(!$contentType = $command->getContentType()) { if(!$contentType = $command->getContentType()) {
$contentType = $state->getPreferedContentType($type); $contentType = $state->getPreferedContentType($type);
} }
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar') if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($command->getContent(), 'BEGIN:VTODO') !== false) && strpos($command->getContent(), 'BEGIN:VTODO') !== false) {
{
$hordeType = 'tasks'; $hordeType = 'tasks';
} }
$syncElementItems = $command->getSyncElementItems(); $syncElementItems = $command->getSyncElementItems();
foreach($syncElementItems as $syncItem) { foreach($syncElementItems as $syncItem) {
$guid = false; $guid = false;
$contentSize = strlen($syncItem->_content);
if ((($size = $syncItem->getContentSize()) !== false) &&
($contentSize != $size) &&
($contentSize + 1 != $size)) {
Horde::logMessage('SyncML: content size missmatch for LocURI '
. $syncItem->getLocURI() . ": $contentSize ($size)",
__FILE__, __LINE__, PEAR_LOG_ERROR);
$command->setStatus(RESPONSE_SIZE_MISMATCH);
return false;
}
if (is_a($command, 'Horde_SyncML_Command_Sync_Add')) { if (is_a($command, 'Horde_SyncML_Command_Sync_Add')) {
if ($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) {
// We enforce the client not to change anything
if ($sync_conflicts > CONFLICT_CLIENT_CHANGES_IGNORED) {
// delete this item from client
Horde::logMessage('SyncML: Server RO! REMOVE '
. $syncItem->getLocURI() . ' from client',
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->addConflictItem($type, '!D' . $syncItem->getLocURI());
} else {
Horde::logMessage('SyncML: Server RO! '
. 'REJECT all client changes',
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->log('Client-AddReplaceIgnored');
}
continue;
}
$guid = $registry->call($hordeType . '/import', $guid = $registry->call($hordeType . '/import',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
if (!is_a($guid, 'PEAR_Error') && $guid != false) { if (!is_a($guid, 'PEAR_Error') && $guid != false) {
$ts = $state->getSyncTSforAction($guid, 'add'); $ts = $state->getSyncTSforAction($guid, 'add');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-Add"); $state->log('Client-Add');
Horde::logMessage('SyncML: added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: added client entry as '
. $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else { } else {
$state->log("Client-AddFailure"); $state->log('Client-AddFailure');
Horde::logMessage('SyncML: Error in adding client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR); Horde::logMessage('SyncML: Error in adding client entry: '
. $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
} }
} elseif (is_a($command, 'Horde_SyncML_Command_Sync_Delete')) { } elseif (is_a($command, 'Horde_SyncML_Command_Sync_Delete')) {
// We can't remove the mapping entry as we need to keep
// the timestamp information.
$guid = $state->removeUID($type, $syncItem->getLocURI()); $guid = $state->removeUID($type, $syncItem->getLocURI());
#$guid = $state->getGlobalUID($type, $syncItem->getLocURI()); if (!$guid) {
Horde::logMessage('SyncML: about to delete entry ' . $type .' / '. $guid . ' due to client request '.$syncItem->getLocURI(), __FILE__, __LINE__, PEAR_LOG_DEBUG); // the entry is no longer on the server
$state->log('Client-DeleteFailure');
Horde::logMessage('SyncML: Failure deleting client entry, '
. 'gone already on server!',
__FILE__, __LINE__, PEAR_LOG_ERR);
continue;
}
if ($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) {
// We enforce the client not to change anything
if ($sync_conflicts > CONFLICT_CLIENT_CHANGES_IGNORED) {
Horde::logMessage('SyncML: Server RO! ADD '
. $guid . ' to client again',
__FILE__, __LINE__, PEAR_LOG_WARNING);
// $state->addConflictItem($type, $guid);
} else {
Horde::logMessage('SyncML: '.
'Server RO! REJECT all client changes',
__FILE__, __LINE__, PEAR_LOG_WARNING);
}
$state->log('Client-DeleteIgnored');
continue;
}
elseif ($sync_conflicts == CONFLICT_RESOLVED_WITH_DUPLICATE &&
in_array($guid, $changes))
{
Horde::logMessage('SyncML: '.
'Server has updated version to keep',
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->log('Client-DeleteIgnored');
continue;
}
elseif ($sync_conflicts == CONFLICT_MERGE_DATA)
{
Horde::logMessage('SyncML: Server Merge Only: ADD '
. $guid . ' to client again',
__FILE__, __LINE__, PEAR_LOG_WARNING);
// $state->addConflictItem($type, $guid);
$state->log('Client-DeleteIgnored');
continue;
}
Horde::logMessage('SyncML: about to delete entry '
. $type .' / '. $guid . ' due to client request '
. $syncItem->getLocURI(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!is_a($guid, 'PEAR_Error') && $guid != false) { if (!is_a($guid, 'PEAR_Error') && $guid != false) {
$registry->call($hordeType . '/delete', array($guid)); $registry->call($hordeType . '/delete', array($guid));
#$ts = $state->getSyncTSforAction($guid, 'delete'); $ts = $state->getSyncTSforAction($guid, 'delete');
#$state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts, 1);
$state->log("Client-Delete"); $state->log('Client-Delete');
Horde::logMessage('SyncML: deleted entry ' . $guid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage('SyncML: deleted entry '
. $guid . ' due to client request',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
} else { } else {
$state->log("Client-DeleteFailure"); $state->log('Client-DeleteFailure');
Horde::logMessage('SyncML: Failure deleting client entry, maybe gone already on server. msg:'. $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR); Horde::logMessage('SyncML: Failure deleting client entry, '
. 'maybe gone already on server. msg: '
. $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
} }
} elseif (is_a($command, 'Horde_SyncML_Command_Sync_Replace')) { } elseif (is_a($command, 'Horde_SyncML_Command_Sync_Replace')) {
$guid = $state->getGlobalUID($type, $syncItem->getLocURI()); $guid = $state->getGlobalUID($type, $syncItem->getLocURI());
$replace = true;
$ok = false; $ok = false;
if ($guid) { $merge = false;
Horde::logMessage('SyncML: locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_ERR); if ($guid)
// Entry exists: replace current one. {
$ok = $registry->call($hordeType . '/replace', Horde::logMessage('SyncML: locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_DEBUG);
array($guid, $state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); if (($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) || in_array($guid, $changes))
if (!is_a($ok, 'PEAR_Error')) { {
$ts = $state->getSyncTSforAction($guid, 'modify'); Horde::logMessage('SyncML: CONFLICT for locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING);
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts); switch ($sync_conflicts)
Horde::logMessage('SyncML: replaced entry due to client request guid: ' .$guid. ' ts: ' .$ts, __FILE__, __LINE__, PEAR_LOG_DEBUG); {
$state->log("Client-Replace"); case CONFLICT_CLIENT_WINNING:
$ok = true; $command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING);
} else { break;
// Entry may have been deleted; try adding it. case CONFLICT_SERVER_WINNING:
$ok = false; Horde::logMessage('SyncML: REJECT client change for locuri ' .
$syncItem->getLocURI() . ' guid ' . $guid ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
$ok = true;
$replace = false;
$state->log('Client-AddReplaceIgnored');
break;
case CONFLICT_MERGE_DATA:
Horde::logMessage('SyncML: Merge server and client data for locuri ' .
$syncItem->getLocURI() . ' guid ' . $guid ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
$merge = true;
break;
case CONFLICT_RESOLVED_WITH_DUPLICATE:
$replace = false;
break;
case CONFLICT_CLIENT_CHANGES_IGNORED:
Horde::logMessage('SyncML: Server RO! REJECT client change for locuri ' .
$syncItem->getLocURI() . ' guid ' . $guid ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
$ok = true;
$replace = false;
$ts = $state->getSyncTSforAction($guid, 'modify');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log('Client-AddReplaceIgnored');
break;
default: // We enforce our data on client
Horde::logMessage('SyncML: Server RO! UNDO client change for locuri ' .
$syncItem->getLocURI() . ' guid ' . $guid ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->addConflictItem($type, $guid);
$ok = true;
$replace = false;
}
}
elseif ($sync_conflicts == CONFLICT_MERGE_DATA)
{
Horde::logMessage('SyncML: Merge server and client data for locuri ' .
$syncItem->getLocURI() . ' guid ' . $guid ,
__FILE__, __LINE__, PEAR_LOG_WARNING);
$merge = true;
}
if ($replace)
{
// Entry exists: replace/merge with current one.
$ok = $registry->call($hordeType . '/replace',
array($guid, $state->convertClient2Server($syncItem->getContent(),
$contentType), $contentType, $merge));
if (!is_a($ok, 'PEAR_Error') && $ok != false)
{
$ts = $state->getSyncTSforAction($guid, 'modify');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
if ($merge)
{
Horde::logMessage('SyncML: Merged entry due to client request guid: ' .
$guid . ' ts: ' . $ts, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
else
{
Horde::logMessage('SyncML: replaced entry due to client request guid: ' .
$guid . ' ts: ' . $ts, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
$state->log('Client-Replace');
$ok = true;
}
else
{
// Entry may have been deleted; try adding it.
$ok = false;
}
} }
} }
if (!$ok) { if (!$ok) {
// Entry does not exist in map or database: add a new one. // Entry does either not exist in map or database, or should be added due to a conflict
Horde::logMessage('SyncML: try to add contentype ' . $contentType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) {
// We enforce the client not to change anything
if ($sync_conflicts > CONFLICT_CLIENT_CHANGES_IGNORED) {
// delete this item from client
Horde::logMessage('SyncML: Server RO! REMOVE ' . $syncItem->getLocURI() . ' from client',
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->addConflictItem($type, '!D' . $syncItem->getLocURI());
} else {
Horde::logMessage('SyncML: Server RO! REJECT all client changes',
__FILE__, __LINE__, PEAR_LOG_WARNING);
}
continue;
}
Horde::logMessage('SyncML: try to add contentype '
. $contentType . ' for locuri ' . $syncItem->getLocURI(),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
//continue;
$oguid = $guid;
$guid = $registry->call($hordeType . '/import', $guid = $registry->call($hordeType . '/import',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $guid));
if (!is_a($guid, 'PEAR_Error')) { if (!is_a($guid, 'PEAR_Error')) {
$ts = $state->getSyncTSforAction($guid, 'add'); if ($oguid != $guid) {
// We add a new entry
$ts = $state->getSyncTSforAction($guid, 'add');
Horde::logMessage('SyncML: added entry '
. $guid . ' from client',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->log('Client-Add');
} else {
// We replaced an entry
$ts = $state->getSyncTSforAction($guid, 'modify');
Horde::logMessage('SyncML: replaced entry '
. $guid . ' from client',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->log('Client-Replace');
}
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-AddReplace");
Horde::logMessage('SyncML: r/ added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else { } else {
Horde::logMessage('SyncML: Error in replacing/add client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR); Horde::logMessage('SyncML: Error in replacing/'
$state->log("Client-AddFailure"); . 'add client entry:' . $guid->message,
__FILE__, __LINE__, PEAR_LOG_ERR);
$state->log('Client-AddFailure');
} }
} }
} }
} }
return $guid; return $guid;
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
include_once 'Horde/SyncML/Sync.php'; include_once 'Horde/SyncML/Sync/SlowSync.php';
/** /**
* $Horde: framework/SyncML/SyncML/Sync/RefreshFromClientSync.php,v 1.8 2004/09/14 04:27:06 chuck Exp $ * $Horde: framework/SyncML/SyncML/Sync/RefreshFromClientSync.php,v 1.8 2004/09/14 04:27:06 chuck Exp $
@ -15,20 +15,9 @@ include_once 'Horde/SyncML/Sync.php';
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_SyncML * @package Horde_SyncML
*/ */
class Horde_SyncML_Sync_RefreshFromClientSync extends Horde_SyncML_Sync { class Horde_SyncML_Sync_RefreshFromClientSync extends Horde_SyncML_Sync_SlowSync {
/** /**
* We need to erase the current server contents, then we can add * We needed to erase the current server contents, then we can add
* the client's contents. * the client's contents.
*/ */
function startSync($currentCmdID, &$output)
{
$deletes = $registry->call($this->targetLocURI, '/list', array());
foreach ($delete as $deletes) {
$registry->call($this->targetLocURI . '/delete', array($delete));
}
return $currentCmdID;
}
} }

View File

@ -1,87 +1,167 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Sync.php'; include_once 'Horde/SyncML/Sync.php';
/**
* $Horde: framework/SyncML/SyncML/Sync/RefreshFromServerSync.php,v 1.9 2004/07/03 15:21:15 chuck Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySync { class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySync {
function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) { function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) {
global $registry; global $registry;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$maxMsgSize = $state->getMaxMsgSizeClient();
$adds = &$state->getAddedItems($hordeType); $deviceInfo = $state->getClientDeviceInfo();
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG); if (isset($deviceInfo['maxEntries'])) {
$maxEntries = $deviceInfo['maxEntries'];
if (!$maxMsgSize && !$maxEntries) {
// fallback to default
$maxEntries = MAX_ENTRIES;
}
} else {
$maxEntries = MAX_ENTRIES;
}
$serverAnchorNext = $state->getServerAnchorNext($syncType); $serverAnchorNext = $state->getServerAnchorNext($syncType);
$counter = 0;
if (isset($state->curSyncItem)) {
// Finish the pending sync item
$cmd = &$state->curSyncItem;
unset($state->curSyncItem);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Sync');
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = &$cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
}
$adds = &$state->getAddedItems($syncType);
Horde::logMessage("SyncML: ".count($adds).
' added items found for '.$syncType ,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
if(is_array($adds)) { if(is_array($adds)) {
while($guid = array_shift($adds)) { while($guid = array_shift($adds)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries && ($state->getNumberOfElements() >= $maxEntries)
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
|| ($maxMsgSize
&& (($currentSize + MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$adds[] = $guid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
if ($locID = $state->getLocID($syncType, $guid)) { if ($locID = $state->getLocID($syncType, $guid)) {
Horde::logMessage("SyncML: RefreshFromServerSync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: RefreshFromServerSync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
$guid_ts = $state->getSyncTSforAction($guid, 'add');
if ($guid_ts > $serverAnchorNext) {
// Change was made after we started this sync.
// Don't sent this now to the client.
Horde::logMessage("SyncML: RefreshFromServerSync add $guid is in our future", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue;
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType)); $c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
Horde::logMessage("SyncML: slowsync add $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); if (is_a($c, 'PEAR_Error')) {
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);
$cmd->setContent($c); $state->log("Server-ExportFailed");
$cmd->setContentType($contentType['ContentType']); continue;
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// return if we have to much data
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
} }
$size = strlen($c);
// return if we have to much data
if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
if (($size + MIN_MSG_LEFT * 2) > $maxMsgSize) {
Horde::logMessage("SyncML: refresh failed to export guid $guid due to size $size", __FILE__, __LINE__, PEAR_LOG_ERROR);
$state->log("Server-ExportFailed");
continue;
}
if (($currentSize + $size + MIN_MSG_LEFT * 2) > $maxMsgSize) {
// put the item back in the queue
$adds[] = $guid;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
}
Horde::logMessage("SyncML: refresh add $guid to client\n$c", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat'])) {
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setGUID($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// moreData split; put the guid back in the list and return
if ($cmd->hasMoreData()) {
$state->curSyncItem = &$cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
} }
} }
#Horde::logMessage("SyncML: handling sync ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: All items handled for sync $syncType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->removeExpiredUID($syncType, $serverAnchorNext);
$state->clearSync($syncType); $state->clearSync($syncType);
return $currentCmdID; return $currentCmdID;
} }
function loadData() { function loadData() {
global $registry; global $registry;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$state->setTargetURI($syncType);
$future = $state->getServerAnchorNext($syncType);
$delta_add = 0;
Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array())); $state->setAddedItems($syncType, $registry->call($hordeType. '/listBy',
$adds = &$state->getAddedItems($hordeType); array('action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression)));
$delta_add = count($state->getAddedItems($hordeType));
$state->setAddedItems($syncType, $registry->call($hordeType. '/list', array('filter' => $this->_filterExpression)));
$this->_syncDataLoaded = TRUE; $this->_syncDataLoaded = TRUE;
return count($state->getAddedItems($hordeType)); return count($state->getAddedItems($syncType)) - $delta_add;
} }
} }

View File

@ -1,194 +1,282 @@
<?php <?php
include_once 'Horde/SyncML/Sync/TwoWaySync.php';
/** /**
* eGroupWare - SyncML based on Horde 3
*
* Slow sync may just work; I think most of the work is going to be * Slow sync may just work; I think most of the work is going to be
* done by the API. * done by the API.
* *
* $Horde: framework/SyncML/SyncML/Sync/SlowSync.php,v 1.7 2004/05/26 17:32:50 chuck Exp $
* *
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com> * Using the PEAR Log class (which need to be installed!)
* *
* See the enclosed file COPYING for license information (LGPL). If you * @link http://www.egroupware.org
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* * @package api
* @author Anthony Mills <amills@pyramid6.com> * @subpackage horde
* @version $Revision$ * @author Anthony Mills <amills@pyramid6.com>
* @since Horde 3.0 * @author Joerg Lehrke <jlehrke@noc.de>
* @package Horde_SyncML * @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/ */
include_once 'Horde/SyncML/Sync/TwoWaySync.php';
class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync { class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) { function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) {
global $registry; global $registry;
$history = $GLOBALS['egw']->contenthistory; $history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$maxMsgSize = $state->getMaxMsgSizeClient();
$deviceInfo = $state->getClientDeviceInfo();
if (isset($deviceInfo['maxEntries'])) {
$maxEntries = $deviceInfo['maxEntries'];
if (!$maxMsgSize && !$maxEntries) {
// fallback to default
$maxEntries = MAX_ENTRIES;
}
} else {
$maxEntries = MAX_ENTRIES;
}
$serverAnchorNext = $state->getServerAnchorNext($syncType); $serverAnchorNext = $state->getServerAnchorNext($syncType);
// now we remove all UID from contentmap that have not been verified in this slowsync // now we remove all UID from contentmap that have not been verified in this slowsync
$state->removeOldUID($syncType, $serverAnchorNext); $state->removeOldUID($syncType, $serverAnchorNext);
$adds = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG); if (isset($state->curSyncItem)) {
$counter = 0; // Finish the pending sync item
$cmd = &$state->curSyncItem;
unset($state->curSyncItem);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Sync');
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = &$cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
}
$adds = &$state->getAddedItems($syncType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$syncType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if(is_array($adds)) { if(is_array($adds)) {
while($guid = array_shift($adds)) { while($guid = array_shift($adds)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries && ($state->getNumberOfElements() >= $maxEntries)
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment']) ||
($maxMsgSize && (($currentSize + MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$adds[] = $guid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
if ($locID = $state->getLocID($syncType, $guid)) { if ($locID = $state->getLocID($syncType, $guid)) {
Horde::logMessage("SyncML: slowsync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: slowsync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
$guid_ts = $state->getSyncTSforAction($guid, 'add');
if ($guid_ts > $serverAnchorNext) {
// Change was made after we started this sync.
// Don't sent this now to the client.
Horde::logMessage("SyncML: slowsync add $guid is in our future", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue;
}
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType)); $c = $registry->call($hordeType . '/export', array('guid' => $guid, 'contentType' => $contentType));
#Horde::logMessage("SyncML: slowsync add guid $guid to client ". print_r($c, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); if (is_a($c, 'PEAR_Error')) {
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);
$cmd->setContent($c); continue;
$cmd->setContentType($contentType['ContentType']); }
if (isset($contentType['ContentFormat']))
{ $size = strlen($c);
$cmd->setContentFormat($contentType['ContentFormat']); // return if we have to much data
if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
if (($size + MIN_MSG_LEFT * 2) > $maxMsgSize) {
Horde::logMessage("SyncML: slowsync failed to export guid $guid due to size $size", __FILE__, __LINE__, PEAR_LOG_ERROR);
continue;
} }
if (($currentSize + $size + MIN_MSG_LEFT * 2) > $maxMsgSize) {
$cmd->setSourceURI($guid); // put the item back in the queue
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add'); $adds[] = $guid;
$state->log('Server-Add');
// return if we have to much data
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
} }
Horde::logMessage("SyncML: slowsync add guid $guid to client\n$c", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat'])) {
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setGUID($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = &$cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
} }
} }
#Horde::logMessage("SyncML: handling sync ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: All items handled for sync $syncType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->removeExpiredUID($syncType, $serverAnchorNext);
$state->clearSync($syncType); $state->clearSync($syncType);
return $currentCmdID; return $currentCmdID;
} }
/** /**
* Here's where the actual processing of a client-sent Sync * Here's where the actual processing of a client-sent Sync
* Command takes place. Entries are added or replaced * Command takes place. Entries are added or replaced
* from the server database by using Horde API (Registry) calls. * from the server database by using Horde API (Registry) calls.
*/ */
function runSyncCommand(&$command) { function runSyncCommand(&$command) {
#Horde::logMessage('SyncML: content type is ' . $command->getContentType() .' moreData '. $command->_moreData, __FILE__, __LINE__, PEAR_LOG_DEBUG);
global $registry; global $registry;
$history = $GLOBALS['egw']->contenthistory; $history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
if(isset($state->_moreData['luid'])) { if ($command->hasMoreData()) {
if(($command->_luid == $state->_moreData['luid'])) { Horde::logMessage('SyncML: moreData: TRUE', __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage('SyncML: got next moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG); $command->setStatus(RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED);
$lastChunks = implode('',$state->_moreData['chunks']); return true;
$command->_content = $lastChunks.$command->_content; }
$stringlen1 = strlen($lastChunks);
$stringlen2 = strlen($command->_content); $type = $this->_targetLocURI;
if(!$command->_moreData && strlen($command->_content) != $state->_moreData['contentSize']) { $syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
$command->_status = RESPONSE_SIZE_MISMATCH; if (isset($syncml_prefs[$type])) {
$state->_moreData = array(); $sync_conflicts = $syncml_prefs[$type];
} else {
return; $sync_conflicts = CONFLICT_SERVER_WINNING;
} elseif(!$command->_moreData && strlen($command->_content) == $state->_moreData['contentSize']) { }
$state->_moreData = array();
Horde::logMessage('SyncML: chunk ended successful type is ' . $command->getContentType() .' content is '. $command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG); $hordeType = $state->getHordeType($type);
}
} else { $syncElementItems = $command->getSyncElementItems();
// alert 223 needed too
#$command->_status = ALERT_NO_END_OF_DATA; foreach($syncElementItems as $syncItem) {
$state->_moreData = array(); $contentSize = strlen($syncItem->_content);
if ((($size = $syncItem->getContentSize()) !== false) &&
($contentSize != $size) &&
($contentSize + 1 != $size)) {
Horde::logMessage('SyncML: content size missmatch for LocURI ' . $syncItem->_luid .
": $contentSize ($size)", __FILE__, __LINE__, PEAR_LOG_ERROR);
$command->setStatus(RESPONSE_SIZE_MISMATCH);
return; return;
} }
}
// don't add/replace the data currently, they are not yet complete
if($command->_moreData == TRUE) {
$state->_moreData['chunks'][] = $command->_content;
$state->_moreData['luid'] = $command->_luid;
// gets only set with the first chunk of data
if(isset($command->_contentSize))
$state->_moreData['contentSize'] = $command->_contentSize;
$command->_status = RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED;
Horde::logMessage('SyncML: added moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return;
}
$type = $this->_targetLocURI;
$hordeType = $state->getHordeType($type);
$syncElementItems = $command->getSyncElementItems();
foreach($syncElementItems as $syncItem) {
if(!$contentType = $syncItem->getContentType()) { if(!$contentType = $syncItem->getContentType()) {
$contentType = $state->getPreferedContentType($type); $contentType = $state->getPreferedContentType($type);
} }
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar') if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false) && strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false) {
{
$hordeType = 'tasks'; $hordeType = 'tasks';
} }
$guid = false; $guid = false;
$guid = $registry->call($hordeType . '/search', $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()) ));
if ($guid) { if ($guid) {
# entry exists in database already. Just update the mapping // Check if the found entry came from the client
Horde::logMessage('SyncML: adding mapping for locuri:'. $syncItem->getLocURI() . ' and guid:' . $guid , __FILE__, __LINE__, PEAR_LOG_DEBUG); $guid_ts = $state->getSyncTSforAction($guid, 'add');
$state->setUID($type, $syncItem->getLocURI(), $guid, mktime()); $sync_ts = $state->getChangeTS($type, $guid);
$state->log("Client-Replace"); if ($sync_ts && $sync_ts == $guid_ts) {
} else { // Entry came from the client, so we get a duplicate here
# Entry does not exist in database: add a new one. Horde::logMessage('SyncML: CONFLICT for locuri ' . $syncItem->getLocURI()
$state->removeUID($type, $syncItem->getLocURI()); . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING);
Horde::logMessage('SyncML: try to add contentype ' . $contentType .' to '. $hordeType, __FILE__, __LINE__, PEAR_LOG_DEBUG); if ($sync_conflicts != CONFLICT_RESOLVED_WITH_DUPLICATE) {
$guid = $registry->call($hordeType . '/import', $state->log("Client-AddReplaceIgnored");
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); continue;
if (!is_a($guid, 'PEAR_Error') && $guid != false) { }
$ts = $state->getSyncTSforAction($guid, 'add');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-AddReplace");
Horde::logMessage('SyncML: r/ added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else { } else {
Horde::logMessage('SyncML: Error in replacing/add client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR); # Entry exists in database already. Just update the mapping
$state->log("Client-AddFailure"); Horde::logMessage('SyncML: adding mapping for locuri:'
. $syncItem->getLocURI() . ' and guid:' . $guid,
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setUID($type, $syncItem->getLocURI(), $guid, mktime());
$state->log("Client-Map");
continue;
} }
} }
if ($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) {
// We enforce the client not to change anything
if ($sync_conflicts > CONFLICT_CLIENT_CHANGES_IGNORED) {
// delete this item from client
Horde::logMessage('SyncML: Server RO! REMOVE ' . $syncItem->getLocURI()
. ' from client', __FILE__, __LINE__, PEAR_LOG_WARNING);
$state->addConflictItem($type, '!D' . $syncItem->getLocURI());
} else {
Horde::logMessage('SyncML: Server RO! REJECT all client changes',
__FILE__, __LINE__, PEAR_LOG_WARNING);
$state->log("Client-AddReplaceIgnored");
}
$command->setStatus(RESPONSE_NO_EXECUTED);
continue;
}
// Add entry to the database.
$state->removeUID($type, $syncItem->getLocURI());
Horde::logMessage('SyncML: try to add contentype ' . $contentType .' to '. $hordeType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$guid = $registry->call($hordeType . '/import',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
if (!is_a($guid, 'PEAR_Error') && $guid != false) {
$ts = $state->getSyncTSforAction($guid, 'modify');
if (!$ts) {
$ts = $state->getSyncTSforAction($guid, 'add');
}
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-AddReplace");
Horde::logMessage('SyncML: r/ added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
Horde::logMessage('SyncML: Error in replacing/add client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
$state->log("Client-AddFailure");
}
} }
return true; return true;
} }
function loadData() { function loadData() {
global $registry; global $registry;
$state = &$_SESSION['SyncML.state']; $state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$state->setTargetURI($syncType);
$future = $state->getServerAnchorNext($syncType);
$delta_add = 0;
Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array())); $delta_add = count($registry->call($hordeType. '/listBy',
$adds = &$state->getAddedItems($hordeType); array('action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression)));
$state->setAddedItems($syncType, $registry->call($hordeType. '/list', array('filter' => $this->_filterExpression)));
$this->_syncDataLoaded = TRUE; $this->_syncDataLoaded = TRUE;
return count($state->getAddedItems($hordeType)); return count($state->getAddedItems($syncType)) - $delta_add;
} }
} }

View File

@ -1,175 +1,329 @@
<?php <?php
/**
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage horde
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/Sync.php'; include_once 'Horde/SyncML/Sync.php';
include_once 'Horde/SyncML/Command/Sync/ContentSyncElement.php'; include_once 'Horde/SyncML/Command/Sync/ContentSyncElement.php';
/**
* $Horde: framework/SyncML/SyncML/Sync/TwoWaySync.php,v 1.12 2004/07/26 09:24:38 jan Exp $
*
* Copyright 2003-2004 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @author Karsten Fourmont <fourmont@gmx.de>
*
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
*/
class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync { class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
function endSync($currentCmdID, &$output) { function endSync($currentCmdID, & $output) {
global $registry; global $registry;
$state = &$_SESSION['SyncML.state']; $state = & $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType); $refts = $state->getServerAnchorLast($syncType);
$currentCmdID = $this->handleSync($currentCmdID, $currentCmdID = $this->handleSync($currentCmdID, $hordeType, $syncType, $output, $refts);
$hordeType,
$syncType,
$output,
$refts);
return $currentCmdID; return $currentCmdID;
} }
function handleSync($currentCmdID, $hordeType, $syncType,&$output, $refts) { function handleSync($currentCmdID, $hordeType, $syncType, & $output, $refts) {
global $registry; global $registry;
// array of Items which got modified, but got never send to the client before // array of Items which got modified, but got never send to the client before
$missedAdds = array(); $missedAdds = array ();
// array of Items which the client wanted to add, but must be deleted due to
// user's sync policy
$remoteDeletes = array ();
$history = $GLOBALS['egw']->contenthistory; $history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state']; $state = & $_SESSION['SyncML.state'];
$counter = 0; $maxMsgSize = $state->getMaxMsgSizeClient();
$deviceInfo = $state->getClientDeviceInfo();
$changes = &$state->getChangedItems($hordeType); if (isset($deviceInfo['maxEntries'])) {
$deletes = &$state->getDeletedItems($hordeType); $maxEntries = $deviceInfo['maxEntries'];
$adds = &$state->getAddedItems($hordeType); if (!$maxMsgSize && !$maxEntries) {
// fallback to default
$maxEntries = MAX_ENTRIES;
}
} else {
$maxEntries = MAX_ENTRIES;
}
Horde::logMessage("SyncML: ".count($changes).' changed items found for '.$hordeType, __FILE__, __LINE__, PEAR_LOG_DEBUG); $serverAnchorNext = $state->getServerAnchorNext($syncType);
Horde::logMessage("SyncML: ".count($deletes).' deleted items found for '.$hordeType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (isset ($state->curSyncItem)) {
// Finish the pending sync item
$cmd = & $state->curSyncItem;
unset ($state->curSyncItem);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Sync');
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = & $cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
}
$changes = & $state->getChangedItems($syncType);
$deletes = & $state->getDeletedItems($syncType);
$adds = & $state->getAddedItems($syncType);
$conflicts = & $state->getConflictItems($syncType);
// manage the conflict items
foreach ( $conflicts as $refresh) {
if (preg_match('/^!D(.*)/', $refresh, $matches)) {
// delete locuri
$remoteDeletes[] = $matches[1];
} else {
$adds[] = $refresh;
}
}
Horde :: logMessage('SyncML: ' . count($changes) . ' changed items found for ' . $syncType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde :: logMessage('SyncML: ' . count($deletes) . ' deleted items found for ' . $syncType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde :: logMessage('SyncML: ' . count($remoteDeletes) . ' items to delete on client found for ' . $syncType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde :: logMessage('SyncML: ' . count($adds) . ' added items (' . count($refresh) . ' refreshs) found for ' . $syncType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// handle changes // handle changes
if(is_array($changes)) { if (is_array($changes)) {
while($guid = array_shift($changes)) { while ($guid = array_shift($changes)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries
&& ($state->getNumberOfElements() >= $maxEntries)
&& isset ($contentType['mayFragment'])
&& $contentType['mayFragment'])
|| ($maxMsgSize
&& (($currentSize +MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$changes[] = $guid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$guid_ts = $state->getSyncTSforAction($guid, 'modify'); $guid_ts = $state->getSyncTSforAction($guid, 'modify');
$sync_ts = $state->getChangeTS($syncType, $guid); $sync_ts = $state->getChangeTS($syncType, $guid);
Horde::logMessage("SyncML: timestamp modify guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: timestamp modify $guid guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync_ts && $sync_ts == $guid_ts) { if ($sync_ts && $sync_ts == $guid_ts) {
// Change was done by us upon request of client. // Change was done by us upon request of client.
// Don't mirror that back to the client. // Don't mirror that back to the client.
Horde::logMessage("SyncML: change: $guid ignored, came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: change: $guid ignored, came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue;
}
if ($guid_ts > $serverAnchorNext) {
// Change was made after we started this sync.
// Don't sent this now to the client.
Horde :: logMessage("SyncML: change $guid is in our future: $serverAnchorNext", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
Horde::logMessage("SyncML: change $guid hs_ts:$guid_ts dt_ts:" . $state->getChangeTS($syncType, $guid), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$locid = $state->getLocID($syncType, $guid); $locid = $state->getLocID($syncType, $guid);
if (!$locid) { if (!$locid) {
// somehow we missed to add, lets store the uid, so we add this entry later // somehow we missed to add, lets store the uid, so we add this entry later
$missedAdds[] = $guid; $missedAdds[] = $guid;
Horde::logMessage("SyncML: unable to create change for $guid: locid not found in map", __FILE__, __LINE__, PEAR_LOG_WARNING); Horde :: logMessage("SyncML: unable to create change for $guid: locid not found in map", __FILE__, __LINE__, PEAR_LOG_WARNING);
continue; continue;
} }
// Create a replace request for client. // Create a replace request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType. '/export', $c = $registry->call($hordeType . '/export', array (
array('guid' => $guid, 'contentType' => $contentType)); 'guid' => $guid,
if (!is_a($c, 'PEAR_Error')) { 'contentType' => $contentType
));
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen. // Item in history but not in database. Strange, but can happen.
Horde::logMessage("SyncML: change: $guid export content: $c", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: change: export of guid $guid failed:\n" . print_r($c, true), __FILE__, __LINE__, PEAR_LOG_WARNING);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement(); continue;
# LK $cmd->setContent($state->convertServer2Client($c, $contentType)); }
$cmd->setContent($c);
$cmd->setSourceURI($guid); $size = strlen($c);
$cmd->setTargetURI($locid); // return if we have to much data
$cmd->setContentType($contentType['ContentType']); if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
if (isset($contentType['ContentFormat'])) if (($size +MIN_MSG_LEFT * 2) > $maxMsgSize) {
{ Horde :: logMessage("SyncML: change: export of guid $guid failed due to size $size", __FILE__, __LINE__, PEAR_LOG_ERROR);
$cmd->setContentFormat($contentType['ContentFormat']); $state->log('Server-ExportFailed');
continue;
} }
if (($currentSize + $size +MIN_MSG_LEFT * 2) > $maxMsgSize) {
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace'); // put the item back in the queue
$state->log('Server-Replace'); $changes[] = $guid;
// return if we have to much data
if (++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
} }
Horde :: logMessage("SyncML: change: export guid $guid, content:\n$c", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
# LK $cmd->setContent($state->convertServer2Client($c, $contentType));
$cmd->setContent($c);
$cmd->setLocURI($locid);
$cmd->setContentType($contentType['ContentType']);
if (isset ($contentType['ContentFormat'])) {
$cmd->setContentFormat($contentType['ContentFormat']);
}
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace');
$state->log('Server-Replace');
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = & $cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
} }
} }
Horde::logMessage("SyncML: handling sync (changes done) ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: handling sync (changes done) " . $currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// handle deletes // handle deletes
if(is_array($deletes)) { if (is_array($deletes)) {
while($guid = array_shift($deletes)) { while ($guid = array_shift($deletes)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries && ($state->getNumberOfElements() >= $maxEntries)
&& isset ($contentType['mayFragment'])
&& $contentType['mayFragment'])
|| ($maxMsgSize
&& (($currentSize +MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$deletes[] = $guid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$guid_ts = $state->getSyncTSforAction($guid, 'delete'); $guid_ts = $state->getSyncTSforAction($guid, 'delete');
$sync_ts = $state->getChangeTS($syncType, $guid); $sync_ts = $state->getChangeTS($syncType, $guid);
Horde::logMessage("SyncML: timestamp delete guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: timestamp delete guid_ts: $guid_ts sync_ts: $sync_ts",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync_ts && $sync_ts == $guid_ts) { if ($sync_ts && $sync_ts == $guid_ts) {
// Change was done by us upon request of client. // Change was done by us upon request of client.
// Don't mirror that back to the client. // Don't mirror that back to the client.
Horde::logMessage("SyncML: delete $guid ignored, came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: delete $guid ignored, came from client",
__FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync_ts < $serverAnchorNext
&& ($locid = $state->getLocID($syncType, $guid))) {
// Now we can remove the past
$state->removeUID($syncType, $locid);
}
continue;
}
if ($guid_ts > $serverAnchorNext) {
// Change was made after we started this sync.
// Don't sent this now to the client.
Horde :: logMessage("SyncML: delete $guid is in our future: $serverAnchorNext", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
$locid = $state->getLocID($syncType, $guid); $locid = $state->getLocID($syncType, $guid);
if (!$locid) { if (!$locid) {
Horde::logMessage("SyncML: unable to create delete for $guid: locid not found in map", __FILE__, __LINE__, PEAR_LOG_WARNING); Horde :: logMessage("SyncML: unable to delete $guid: locid not found in map", __FILE__, __LINE__, PEAR_LOG_INFO);
$state->log("Server-DeleteFailure");
continue; continue;
} }
Horde::logMessage("SyncML: delete: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: delete: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Create a Delete request for client. // Create a Delete request for client.
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement(); $cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setTargetURI($locid); $cmd->setLocURI($locid);
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Delete'); $currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Delete');
$state->log('Server-Delete'); $state->log('Server-Delete');
$state->removeUID($syncType, $locid); $state->removeUID($syncType, $locid);
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI); // moreData split; save in session state and end current message
// return if we have to much data if ($cmd->hasMoreData()) {
if(++$counter >= MAX_ENTRIES $state->curSyncItem = & $cmd;
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
$state->incNumberOfElements();
}
}
// handle remote deletes due to conflicts
if (count($remoteDeletes) > 0) {
while ($locid = array_shift($remoteDeletes)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries && ($state->getNumberOfElements() >= $maxEntries)
&& isset ($contentType['mayFragment'])
&& $contentType['mayFragment'])
|| ($maxMsgSize
&& (($currentSize +MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$remoteDeletes[] = $locid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
Horde :: logMessage("SyncML: delete client locid: $locid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Create a Delete request for client.
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setLocURI($locid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Delete');
$state->log('Server-DeletedConflicts');
$state->removeUID($syncType, $locid);
// moreData split; save in session state and end current message
if ($cmd->hasMoreData()) {
$state->curSyncItem = & $cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
} }
} }
#Horde::logMessage("SyncML: handling sync ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG); #Horde::logMessage("SyncML: handling sync ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// handle missing adds. // handle missing adds.
if(count($missedAdds) > 0) { if (count($missedAdds) > 0) {
Horde::logMessage("SyncML: add missed changes as adds ".count($adds).' / '.$missedAdds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: add missed changes as adds " . count($adds) . ' / ' . $missedAdds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, array_merge($adds, $missedAdds)); $adds = array_merge($adds, $missedAdds);
$adds = &$state->getAddedItems($hordeType); Horde :: logMessage("SyncML: merged adds counter " . count($adds) . ' / ' . $adds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage("SyncML: merged adds counter ".count($adds).' / '.$adds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
} }
if(is_array($adds)) { if (is_array($adds)) {
while($guid = array_shift($adds)) { while ($guid = array_shift($adds)) {
$currentSize = $output->getOutputSize();
// return if we have to much data
if (($maxEntries && ($state->getNumberOfElements() >= $maxEntries)
&& isset ($contentType['mayFragment'])
&& $contentType['mayFragment'])
|| ($maxMsgSize
&& (($currentSize +MIN_MSG_LEFT * 2) > $maxMsgSize))) {
// put the item back in the queue
$adds[] = $guid;
$state->maxNumberOfElements();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$guid_ts = $state->getSyncTSforAction($guid, 'add'); $guid_ts = $state->getSyncTSforAction($guid, 'add');
$sync_ts = $state->getChangeTS($syncType, $guid); $sync_ts = $state->getChangeTS($syncType, $guid);
Horde::logMessage("SyncML: timestamp add $guid guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: timestamp add $guid guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync_ts && $sync_ts == $guid_ts) { if ($sync_ts && $sync_ts == $guid_ts) {
// Change was done by us upon request of client. // Change was done by us upon request of client.
// Don't mirror that back to the client. // Don't mirror that back to the client.
Horde::logMessage("SyncML: add: $guid ignored, came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: add: $guid ignored, came from client", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue;
}
if ($guid_ts > $serverAnchorNext && !in_array($guid, $conflicts)) {
// Change was made after we started this sync.
// Don't sent this now to the client.
Horde :: logMessage("SyncML: add $guid is in our future: $serverAnchorNext", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
@ -179,47 +333,65 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
// For slow sync (ts=0): do not add data for which we // For slow sync (ts=0): do not add data for which we
// have a locid again. This is a heuristic to avoid // have a locid again. This is a heuristic to avoid
// duplication of entries. // duplication of entries.
Horde::logMessage("SyncML: skipping add of guid $guid as there already is a locid $locid", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: skipping add of guid $guid as there already is a locid $locid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
continue; continue;
} }
Horde::logMessage("SyncML: add: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: add: $guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Create an Add request for client. // Create an Add request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI); $contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement(); $c = $registry->call($hordeType . '/export', array (
$c = $registry->call($hordeType . '/export', 'guid' => $guid,
array( 'contentType' => $contentType,
'guid' => $guid ,
'contentType' => $contentType ,
)
);
if (!is_a($c, 'PEAR_Error')) { ));
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen. // Item in history but not in database. Strange, but can happen.
$cmd->setContent($c); Horde :: logMessage("SyncML: add: export of guid $guid failed:\n" . print_r($c, true), __FILE__, __LINE__, PEAR_LOG_WARNING);
$cmd->setContentType($contentType['ContentType']); continue;
if (isset($contentType['ContentFormat'])) }
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// return if we have to much data $size = strlen($c);
if(++$counter >= MAX_ENTRIES // return if we have to much data
&& isset($contentType['mayFragment']) if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
&& $contentType['mayFragment']) if (($size +MIN_MSG_LEFT * 2) > $maxMsgSize) {
{ Horde :: logMessage("SyncML: add: export of guid $guid failed due to size $size", __FILE__, __LINE__, PEAR_LOG_ERROR);
$state->log("Server-ExportFailed");
continue;
}
if (($currentSize + $size +MIN_MSG_LEFT * 2) > $maxMsgSize) {
// put the item back in the queue
$adds[] = $guid;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING); $state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID; return $currentCmdID;
} }
} }
Horde :: logMessage("SyncML: add guid $guid to client\n$c", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
if (isset ($contentType['ContentFormat'])) {
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setGUID($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
// moreData split; put the guid back in the list and return
if ($cmd->hasMoreData()) {
$state->curSyncItem = & $cmd;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
return $currentCmdID;
}
$state->incNumberOfElements();
} }
} }
#Horde::logMessage("SyncML: handling sync ".$currentCmdID, __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde::logMessage("SyncML: All items handled for sync $syncType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->removeExpiredUID($syncType, time());
$state->clearSync($syncType); $state->clearSync($syncType);
return $currentCmdID; return $currentCmdID;
@ -228,24 +400,53 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
function loadData() { function loadData() {
global $registry; global $registry;
$state = &$_SESSION['SyncML.state']; $state = & $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI; $syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType); $hordeType = $state->getHordeType($syncType);
$state->setTargetURI($syncType);
$refts = $state->getServerAnchorLast($syncType); $refts = $state->getServerAnchorLast($syncType);
$future = $state->getServerAnchorNext($syncType);
$delta_mod = 0;
$delta_add = 0;
Horde::logMessage("SyncML: reading changed items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: reading changed items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setChangedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'modify', 'timestamp' => $refts))); $delta_mod = count($registry->call($hordeType . '/listBy', array (
'action' => 'modify',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
$state->setChangedItems($syncType, $registry->call($hordeType . '/listBy', array (
'action' => 'modify',
'timestamp' => $refts,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
Horde::logMessage("SyncML: reading deleted items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: reading deleted items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setDeletedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'delete', 'timestamp' => $refts))); $state->setDeletedItems($syncType, $registry->call($hordeType . '/listBy', array (
'action' => 'delete',
'timestamp' => $refts,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
Horde::logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG); Horde :: logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'add', 'timestamp' => $refts))); $delta_add = count($registry->call($hordeType . '/listBy', array (
'action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
$state->setAddedItems($syncType, $registry->call($hordeType . '/listBy', array (
'action' => 'add',
'timestamp' => $refts,
'type' => $syncType,
'filter' => $this->_filterExpression
)));
$this->_syncDataLoaded = TRUE; $this->_syncDataLoaded = TRUE;
return count($state->getChangedItems($hordeType)) + return count($state->getChangedItems($syncType)) - $delta_mod +count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) - $delta_add +count($state->getConflictItems($syncType));
count($state->getDeletedItems($hordeType)) +
count($state->getAddedItems($hordeType));
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,14 @@
/** /**
* Class representing vAlarms. * Class representing vAlarms.
* *
* $Horde: framework/iCalendar/iCalendar/valarm.php,v 1.8 2004/08/13 19:11:35 karsten Exp $ * $Horde: framework/iCalendar/iCalendar/valarm.php,v 1.8.10.8 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
@ -21,11 +20,6 @@ class Horde_iCalendar_valarm extends Horde_iCalendar {
return 'vAlarm'; return 'vAlarm';
} }
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VALARM');
}
function exportvCalendar() function exportvCalendar()
{ {
return parent::_exportvData('VALARM'); return parent::_exportvData('VALARM');

View File

@ -2,7 +2,6 @@
require_once EGW_API_INC.'/horde/Horde/iCalendar.php'; require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
// The following were shamelessly yoinked from Contact_Vcard_Build // The following were shamelessly yoinked from Contact_Vcard_Build
// Part numbers for N components. // Part numbers for N components.
define('VCARD_N_FAMILY', 0); define('VCARD_N_FAMILY', 0);
@ -27,49 +26,47 @@ define('VCARD_GEO_LON', 1);
/** /**
* Class representing vCard entries. * Class representing vCard entries.
* *
* $Horde: framework/iCalendar/iCalendar/vcard.php,v 1.2 2004/08/18 03:16:24 chuck Exp $ * $Horde: framework/iCalendar/iCalendar/vcard.php,v 1.3.10.16 2008/09/22 04:16:30 chuck Exp $
* *
* Copyright 2003-2004 Karsten Fourmont (karsten@horde.org) * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Karsten Fourmont <karsten@horde.org> * @author Karsten Fourmont <karsten@horde.org>
* @version $Revision$
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
class Horde_iCalendar_vcard extends Horde_iCalendar { class Horde_iCalendar_vcard extends Horde_iCalendar {
function Horde_iCalendar_vcard($version = '2.1')
{
return parent::Horde_iCalendar($version);
}
function getType() function getType()
{ {
return 'vcard'; return 'vcard';
} }
function parsevCalendar($data)
{
return parent::parsevCalendar($data, 'vcard');
}
/** /**
* Unlike vevent and vtodo, a vcard is normally not enclosed in an * Unlike vevent and vtodo, a vcard is normally not enclosed in an
* iCalendar container. (BEGIN..END) * iCalendar container. (BEGIN..END)
*/ */
function exportvCalendar() function exportvCalendar()
{ {
#$requiredAttributes['BODY'] = ''; $requiredAttributes['VERSION'] = $this->_version;
$requiredAttributes['VERSION'] = '2.1'; $requiredAttributes['N'] = ';;;;;;';
if ($this->_version == '3.0') {
$requiredAttributes['FN'] = '';
}
foreach ($requiredAttributes as $name => $default_value) { foreach ($requiredAttributes as $name => $default_value) {
if (is_a($this->getattribute($name), 'PEAR_Error')) { if (is_a($this->getAttribute($name), 'PEAR_Error')) {
$this->setAttribute($name, $default_value); $this->setAttribute($name, $default_value);
} }
} }
//error_log(__METHOD__.":requiredAttributes->".print_r($requiredAttributes,true));
//njv:$buffcontent = ob_get_clean();
#error_log(__METHOD__.":".print_r($buffcontent,true));
#ob_end_clean();
return $this->_exportvData('VCARD') . $this->_newline; return $this->_exportvData('VCARD');
} }
/** /**
@ -80,8 +77,7 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
* to * to
* "Professor Dagobert T Duck Sen" * "Professor Dagobert T Duck Sen"
* *
* @return string Full name of vcard "N" tag * @return string Full name of vcard "N" tag or null if no N tag.
* or null if no N tag.
*/ */
function printableName() function printableName()
{ {
@ -90,6 +86,8 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
return null; return null;
} }
$name_arr = array();
if (!empty($name_parts[VCARD_N_PREFIX])) { if (!empty($name_parts[VCARD_N_PREFIX])) {
$name_arr[] = $name_parts[VCARD_N_PREFIX]; $name_arr[] = $name_parts[VCARD_N_PREFIX];
} }
@ -118,6 +116,11 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
*/ */
function getBareEmail($address) function getBareEmail($address)
{ {
// Empty values are still empty.
if (!$address) {
return $address;
}
require_once 'Mail/RFC822.php'; require_once 'Mail/RFC822.php';
require_once 'Horde/MIME.php'; require_once 'Horde/MIME.php';
@ -126,8 +129,9 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
$rfc822 = new Mail_RFC822(); $rfc822 = new Mail_RFC822();
} }
$rfc822->validateMailbox($address); if (!$rfc822->validateMailbox($address)) {
return $address;
}
return MIME::rfc822WriteAddress($address->mailbox, $address->host); return MIME::rfc822WriteAddress($address->mailbox, $address->host);
} }

View File

@ -2,15 +2,14 @@
/** /**
* Class representing vEvents. * Class representing vEvents.
* *
* $Horde: framework/iCalendar/iCalendar/vevent.php,v 1.31 2004/08/18 03:16:24 chuck Exp $ * $Horde: framework/iCalendar/iCalendar/vevent.php,v 1.31.10.15 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
@ -21,32 +20,28 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
return 'vEvent'; return 'vEvent';
} }
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VEVENT');
}
function exportvCalendar() function exportvCalendar()
{ {
// Default values. // Default values.
$requiredAttributes = array(); $requiredAttributes = array();
$requiredAttributes['DTSTAMP'] = time(); $requiredAttributes['DTSTAMP'] = time();
#$requiredAttributes['ORGANIZER'] = 'Unknown Organizer'; $requiredAttributes['UID'] = $this->_exportDateTime(time())
$requiredAttributes['UID'] = $this->_exportDateTime(time()) . '@' . $_SERVER['SERVER_NAME']; . substr(str_pad(base_convert(microtime(), 10, 36), 16, uniqid(mt_rand()), STR_PAD_LEFT), -16)
. '@' . (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
$method = !empty($this->_container) ? $method = !empty($this->_container) ?
$this->_container->getAttribute('METHOD') : 'PUBLISH'; $this->_container->getAttribute('METHOD') : 'PUBLISH';
switch ($method) { switch ($method) {
case 'PUBLISH': case 'PUBLISH':
$requiredAttributes['DTSTART'] = time(); $requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = ''; $requiredAttributes['SUMMARY'] = '';
break; break;
case 'REQUEST': case 'REQUEST':
$requiredAttributes['ATTENDEE'] = ''; $requiredAttributes['ATTENDEE'] = '';
$requiredAttributes['DTSTART'] = time(); $requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = ''; $requiredAttributes['SUMMARY'] = '';
break; break;
case 'REPLY': case 'REPLY':
@ -54,9 +49,9 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
break; break;
case 'ADD': case 'ADD':
$requiredAttributes['DTSTART'] = time(); $requiredAttributes['DTSTART'] = time();
$requiredAttributes['SEQUENCE'] = 1; $requiredAttributes['SEQUENCE'] = 1;
$requiredAttributes['SUMMARY'] = ''; $requiredAttributes['SUMMARY'] = '';
break; break;
case 'CANCEL': case 'CANCEL':
@ -88,7 +83,8 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
function updateAttendee($email, $status, $fullname = '') function updateAttendee($email, $status, $fullname = '')
{ {
foreach ($this->_attributes as $key => $attribute) { foreach ($this->_attributes as $key => $attribute) {
if ($attribute['name'] == 'ATTENDEE' && $attribute['value'] == 'MAILTO:' . $email) { if ($attribute['name'] == 'ATTENDEE' &&
$attribute['value'] == 'mailto:' . $email) {
$this->_attributes[$key]['params']['PARTSTAT'] = $status; $this->_attributes[$key]['params']['PARTSTAT'] = $status;
if (!empty($fullname)) { if (!empty($fullname)) {
$this->_attributes[$key]['params']['CN'] = $fullname; $this->_attributes[$key]['params']['CN'] = $fullname;
@ -101,7 +97,7 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
if (!empty($fullname)) { if (!empty($fullname)) {
$params['CN'] = $fullname; $params['CN'] = $fullname;
} }
$this->setAttribute('ATTENDEE', 'MAILTO:' . $email, $params); $this->setAttribute('ATTENDEE', 'mailto:' . $email, $params);
} }
/** /**
@ -113,7 +109,7 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
{ {
$organizer = $this->getAttribute('ORGANIZER', true); $organizer = $this->getAttribute('ORGANIZER', true);
if (is_a($organizer, 'PEAR_Error')) { if (is_a($organizer, 'PEAR_Error')) {
return null; return _("An unknown person");
} }
if (isset($organizer[0]['CN'])) { if (isset($organizer[0]['CN'])) {
@ -128,7 +124,7 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
/** /**
* Update this event with details from another event. * Update this event with details from another event.
* *
* @param object Horde_iCalendar_vEvent $vevent The vEvent with latest details. * @param Horde_iCalendar_vEvent $vevent The vEvent with latest details.
*/ */
function updateFromvEvent($vevent) function updateFromvEvent($vevent)
{ {
@ -137,7 +133,9 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
$currentValue = $this->getAttribute($newAttribute['name']); $currentValue = $this->getAttribute($newAttribute['name']);
if (is_a($currentValue, 'PEAR_error')) { if (is_a($currentValue, 'PEAR_error')) {
// Already exists so just add it. // Already exists so just add it.
$this->setAttribute($newAttribute['name'], $newAttribute['value'], $newAttribute['params']); $this->setAttribute($newAttribute['name'],
$newAttribute['value'],
$newAttribute['params']);
} else { } else {
// Already exists so locate and modify. // Already exists so locate and modify.
$found = false; $found = false;
@ -178,19 +176,21 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
* Update just the attendess of event with details from another * Update just the attendess of event with details from another
* event. * event.
* *
* @param object Horde_iCalendar_vEvent $vevent The vEvent with latest details * @param Horde_iCalendar_vEvent $vevent The vEvent with latest details
*/ */
function updateAttendeesFromvEvent($vevent) function updateAttendeesFromvEvent($vevent)
{ {
$newAttributes = $vevent->getAllAttributes(); $newAttributes = $vevent->getAllAttributes();
foreach ($newAttributes as $newAttribute) { foreach ($newAttributes as $newAttribute) {
if (!$newAttribute['name'] == 'ATTENDEE') { if ($newAttribute['name'] != 'ATTENDEE') {
continue; continue;
} }
$currentValue = $this->getAttribute($newAttribute['name']); $currentValue = $this->getAttribute($newAttribute['name']);
if (is_a($currentValue, 'PEAR_error')) { if (is_a($currentValue, 'PEAR_error')) {
// Already exists so just add it. // Already exists so just add it.
$this->setAttribute($newAttribute['name'], $newAttribute['value'], $newAttribute['params']); $this->setAttribute($newAttribute['name'],
$newAttribute['value'],
$newAttribute['params']);
} else { } else {
// Already exists so locate and modify. // Already exists so locate and modify.
$found = false; $found = false;

View File

@ -1,52 +1,78 @@
<?php <?php
/** /**
* Class representing vFreebusys. * Class representing vFreebusy components.
* *
* $Horde: framework/iCalendar/iCalendar/vfreebusy.php,v 1.16 2004/08/18 03:16:24 chuck Exp $ * $Horde: framework/iCalendar/iCalendar/vfreebusy.php,v 1.16.10.17 2008/09/17 08:46:57 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @todo Don't use timestamps
*
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
class Horde_iCalendar_vfreebusy extends Horde_iCalendar { class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
var $_busyPeriods = array(); var $_busyPeriods = array();
var $_extraParams = array();
/**
* Returns the type of this calendar component.
*
* @return string The type of this component.
*/
function getType() function getType()
{ {
return 'vFreebusy'; return 'vFreebusy';
} }
function parsevCalendar($data) /**
* Parses a string containing vFreebusy data.
*
* @param string $data The data to parse.
*/
function parsevCalendar($data, $type = null, $charset = null)
{ {
parent::parsevCalendar($data, 'VFREEBUSY'); parent::parsevCalendar($data, 'VFREEBUSY', $charset);
// Do something with all the busy periods. // Do something with all the busy periods.
foreach ($this->_attributes as $key => $attribute) { foreach ($this->_attributes as $key => $attribute) {
if ($attribute['name'] == 'FREEBUSY') { if ($attribute['name'] != 'FREEBUSY') {
foreach ($attribute['value'] as $value) { continue;
if (array_key_exists('duration', $attribute['value'])) {
$this->addBusyPeriod('BUSY', $value['start'], null, $value['duration']);
} else {
$this->addBusyPeriod('BUSY', $value['start'], $value['end']);
}
}
unset($this->_attributes[$key]);
} }
foreach ($attribute['values'] as $value) {
$params = isset($attribute['params'])
? $attribute['params']
: array();
if (isset($value['duration'])) {
$this->addBusyPeriod('BUSY', $value['start'], null,
$value['duration'], $params);
} else {
$this->addBusyPeriod('BUSY', $value['start'],
$value['end'], null, $params);
}
}
unset($this->_attributes[$key]);
} }
} }
/**
* Returns the component exported as string.
*
* @return string The exported vFreeBusy information according to the
* iCalender format specification.
*/
function exportvCalendar() function exportvCalendar()
{ {
foreach ($this->_busyPeriods as $start => $end) { foreach ($this->_busyPeriods as $start => $end) {
$periods = array(array('start' => $start, 'end' => $end)); $periods = array(array('start' => $start, 'end' => $end));
$this->setAttribute('FREEBUSY', $periods); $this->setAttribute('FREEBUSY', $periods,
isset($this->_extraParams[$start])
? $this->_extraParams[$start] : array());
} }
$res = parent::_exportvData('VFREEBUSY'); $res = parent::_exportvData('VFREEBUSY');
@ -61,7 +87,9 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
/** /**
* Get a display name for this object. * Returns a display name for this object.
*
* @return string A clear text name for displaying this object.
*/ */
function getName() function getName()
{ {
@ -71,12 +99,12 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') { if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') {
$attr = 'ORGANIZER'; $attr = 'ORGANIZER';
} else if ($method == 'REPLY') { } elseif ($method == 'REPLY') {
$attr = 'ATTENDEE'; $attr = 'ATTENDEE';
} }
$name = $this->getAttribute($attr, true); $name = $this->getAttribute($attr, true);
if (array_key_exists('CN', $name[0])) { if (!is_a($name, 'PEAR_Error') && isset($name[0]['CN'])) {
return $name[0]['CN']; return $name[0]['CN'];
} }
@ -90,7 +118,9 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
/** /**
* Get the email address for this object. * Returns the email address for this object.
*
* @return string The email address of this object's owner.
*/ */
function getEmail() function getEmail()
{ {
@ -100,7 +130,7 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') { if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') {
$attr = 'ORGANIZER'; $attr = 'ORGANIZER';
} else if ($method == 'REPLY') { } elseif ($method == 'REPLY') {
$attr = 'ATTENDEE'; $attr = 'ATTENDEE';
} }
@ -113,13 +143,34 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
} }
/**
* Returns the busy periods.
*
* @return array All busy periods.
*/
function getBusyPeriods() function getBusyPeriods()
{ {
return $this->_busyPeriods; return $this->_busyPeriods;
} }
/** /**
* Return all the free periods of time in a given period. * Returns any additional freebusy parameters.
*
* @return array Additional parameters of the freebusy periods.
*/
function getExtraParams()
{
return $this->_extraParams;
}
/**
* Returns all the free periods of time in a given period.
*
* @param integer $startStamp The start timestamp.
* @param integer $endStamp The end timestamp.
*
* @return array A hash with free time periods, the start times as the
* keys and the end times as the values.
*/ */
function getFreePeriods($startStamp, $endStamp) function getFreePeriods($startStamp, $endStamp)
{ {
@ -131,21 +182,21 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
return $periods; return $periods;
} }
// Locate the first time in the requested period we have data // Locate the first time in the requested period we have data for.
// for.
$nextstart = max($startStamp, $this->getStart()); $nextstart = max($startStamp, $this->getStart());
// Check each busy period and add free periods in between. // Check each busy period and add free periods in between.
foreach ($this->_busyPeriods as $start => $end) { foreach ($this->_busyPeriods as $start => $end) {
if ($start <= $endStamp && $end >= $nextstart) { if ($start <= $endStamp && $end >= $nextstart) {
$periods[$nextstart] = min($start, $endStamp); if ($nextstart <= $start) {
$periods[$nextstart] = min($start, $endStamp);
}
$nextstart = min($end, $endStamp); $nextstart = min($end, $endStamp);
} }
} }
// If we didn't read the end of the requested period but still // If we didn't read the end of the requested period but still have
// have data then mark as free to the end of the period or // data then mark as free to the end of the period or available data.
// available data.
if ($nextstart < $endStamp && $nextstart < $this->getEnd()) { if ($nextstart < $endStamp && $nextstart < $this->getEnd()) {
$periods[$nextstart] = min($this->getEnd(), $endStamp); $periods[$nextstart] = min($this->getEnd(), $endStamp);
} }
@ -154,16 +205,29 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
/** /**
* Add a busy period to the info. * Adds a busy period to the info.
*
* This function may throw away data in case you add a period with a start
* date that already exists. The longer of the two periods will be chosen
* (and all information associated with the shorter one will be removed).
*
* @param string $type The type of the period. Either 'FREE' or
* 'BUSY'; only 'BUSY' supported at the moment.
* @param integer $start The start timestamp of the period.
* @param integer $end The end timestamp of the period.
* @param integer $duration The duration of the period. If specified, the
* $end parameter will be ignored.
* @param array $extra Additional parameters for this busy period.
*/ */
function addBusyPeriod($type, $start, $end = null, $duration = null) function addBusyPeriod($type, $start, $end = null, $duration = null,
$extra = array())
{ {
if ($type == "FREE") { if ($type == 'FREE') {
// Make sure this period is not marked as busy. // Make sure this period is not marked as busy.
return false; return false;
} }
// Calculate the end time is duration was specified. // Calculate the end time if duration was specified.
$tempEnd = is_null($duration) ? $end : $start + $duration; $tempEnd = is_null($duration) ? $end : $start + $duration;
// Make sure the period length is always positive. // Make sure the period length is always positive.
@ -171,26 +235,35 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
$start = min($start, $tempEnd); $start = min($start, $tempEnd);
if (isset($this->_busyPeriods[$start])) { if (isset($this->_busyPeriods[$start])) {
// Already a period starting at this time. Extend to the // Already a period starting at this time. Change the current
// length of the longest of the two. // period only if the new one is longer. This might be a problem
$this->_busyPeriods[$start] = max($end, $this->_busyPeriods[$start]); // if the callee assumes that there is no simplification going
// on. But since the periods are stored using the start time of
// the busy periods we have to throw away data here.
if ($end > $this->_busyPeriods[$start]) {
$this->_busyPeriods[$start] = $end;
$this->_extraParams[$start] = $extra;
}
} else { } else {
// Add a new busy period. // Add a new busy period.
$this->_busyPeriods[$start] = $end; $this->_busyPeriods[$start] = $end;
$this->_extraParams[$start] = $extra;
} }
return true; return true;
} }
/** /**
* Get the timestamp of the start of the time period this free * Returns the timestamp of the start of the time period this free busy
* busy information covers. * information covers.
*
* @return integer A timestamp.
*/ */
function getStart() function getStart()
{ {
if (!is_a($this->getAttribute('DTSTART'), 'PEAR_Error')) { if (!is_a($this->getAttribute('DTSTART'), 'PEAR_Error')) {
return $this->getAttribute('DTSTART'); return $this->getAttribute('DTSTART');
} else if (count($this->_busyPeriods)) { } elseif (count($this->_busyPeriods)) {
return min(array_keys($this->_busyPeriods)); return min(array_keys($this->_busyPeriods));
} else { } else {
return false; return false;
@ -198,14 +271,16 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
/** /**
* Get the timestamp of the end of the time period this free busy * Returns the timestamp of the end of the time period this free busy
* information covers. * information covers.
*
* @return integer A timestamp.
*/ */
function getEnd() function getEnd()
{ {
if (!is_a($this->getAttribute('DTEND'), 'PEAR_Error')) { if (!is_a($this->getAttribute('DTEND'), 'PEAR_Error')) {
return $this->getAttribute('DTEND'); return $this->getAttribute('DTEND');
} else if (count($this->_busyPeriods)) { } elseif (count($this->_busyPeriods)) {
return max(array_values($this->_busyPeriods)); return max(array_values($this->_busyPeriods));
} else { } else {
return false; return false;
@ -213,7 +288,16 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
} }
/** /**
* Merge the busy periods of another VFreebusy into this one. * Merges the busy periods of another Horde_iCalendar_vfreebusy object
* into this one.
*
* This might lead to simplification no matter what you specify for the
* "simplify" flag since periods with the same start date will lead to the
* shorter period being removed (see addBusyPeriod).
*
* @param Horde_iCalendar_vfreebusy $freebusy A freebusy object.
* @param boolean $simplify If true, simplify() will
* called after the merge.
*/ */
function merge($freebusy, $simplify = true) function merge($freebusy, $simplify = true)
{ {
@ -221,70 +305,155 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
return false; return false;
} }
$extra = $freebusy->getExtraParams();
foreach ($freebusy->getBusyPeriods() as $start => $end) { foreach ($freebusy->getBusyPeriods() as $start => $end) {
$this->addBusyPeriod('BUSY', $start, $end); // This might simplify the busy periods without taking the
// "simplify" flag into account.
$this->addBusyPeriod('BUSY', $start, $end, null,
isset($extra[$start])
? $extra[$start] : array());
} }
$thisattr = $this->getAttribute('DTSTART'); $thisattr = $this->getAttribute('DTSTART');
$thatattr = $freebusy->getAttribute('DTSTART'); $thatattr = $freebusy->getAttribute('DTSTART');
if (is_a($thisattr, 'PEAR_Error') && !is_a($thatattr, 'PEAR_Error')) { if (is_a($thisattr, 'PEAR_Error') && !is_a($thatattr, 'PEAR_Error')) {
$this->setAttribute('DTSTART', $thatattr); $this->setAttribute('DTSTART', $thatattr, array(), false);
} elseif (!is_a($thatattr, 'PEAR_Error')) { } elseif (!is_a($thatattr, 'PEAR_Error')) {
if ($thatattr > $thisattr) { if ($thatattr < $thisattr) {
$this->setAttribute('DTSTART', $thatattr); $this->setAttribute('DTSTART', $thatattr, array(), false);
} }
} }
$thisattr = $this->getAttribute('DTEND'); $thisattr = $this->getAttribute('DTEND');
$thatattr = $freebusy->getAttribute('DTEND'); $thatattr = $freebusy->getAttribute('DTEND');
if (is_a($thisattr, 'PEAR_Error') && !is_a($thatattr, 'PEAR_Error')) { if (is_a($thisattr, 'PEAR_Error') && !is_a($thatattr, 'PEAR_Error')) {
$this->setAttribute('DTEND', $thatattr); $this->setAttribute('DTEND', $thatattr, array(), false);
} elseif (!is_a($thatattr, 'PEAR_Error')) { } elseif (!is_a($thatattr, 'PEAR_Error')) {
if ($thatattr < $thisattr) { if ($thatattr > $thisattr) {
$this->setAttribute('DTEND', $thatattr); $this->setAttribute('DTEND', $thatattr, array(), false);
} }
} }
if ($simplify) { if ($simplify) {
$this->simplify(); $this->simplify();
} }
return true; return true;
} }
/** /**
* Remove all overlaps and simplify the busy periods array as much * Removes all overlaps and simplifies the busy periods array as much as
* as possible. * possible.
*/ */
function simplify() function simplify()
{
$clean = false;
$busy = array($this->_busyPeriods, $this->_extraParams);
while (!$clean) {
$result = $this->_simplify($busy[0], $busy[1]);
$clean = $result === $busy;
$busy = $result;
}
ksort($result[1], SORT_NUMERIC);
$this->_extraParams = $result[1];
ksort($result[0], SORT_NUMERIC);
$this->_busyPeriods = $result[0];
}
function _simplify($busyPeriods, $extraParams = array())
{ {
$checked = array(); $checked = array();
$checkedExtra = array();
$checkedEmpty = true; $checkedEmpty = true;
foreach ($this->_busyPeriods as $start => $end) {
foreach ($busyPeriods as $start => $end) {
if ($checkedEmpty) { if ($checkedEmpty) {
$checked[$start] = $end; $checked[$start] = $end;
$checkedExtra[$start] = isset($extraParams[$start])
? $extraParams[$start] : array();
$checkedEmpty = false; $checkedEmpty = false;
} else { } else {
$added = false; $added = false;
foreach ($checked as $testStart => $testEnd) { foreach ($checked as $testStart => $testEnd) {
if ($start == $testStart) { // Replace old period if the new period lies around the
$checked[$testStart] = max($testEnd, $end); // old period.
$added = true; if ($start <= $testStart && $end >= $testEnd) {
} else if ($end <= $testEnd && $end >= $testStart) { // Remove old period entry.
unset($checked[$testStart]); unset($checked[$testStart]);
$checked[min($testStart, $start)] = max($testEnd, $end); unset($checkedExtra[$testStart]);
// Add replacing entry.
$checked[$start] = $end;
$checkedExtra[$start] = isset($extraParams[$start])
? $extraParams[$start] : array();
$added = true;
} elseif ($start >= $testStart && $end <= $testEnd) {
// The new period lies fully within the old
// period. Just forget about it.
$added = true;
} elseif (($end <= $testEnd && $end >= $testStart) ||
($start >= $testStart && $start <= $testEnd)) {
// Now we are in trouble: Overlapping time periods. If
// we allow for additional parameters we cannot simply
// choose one of the two parameter sets. It's better
// to leave two separated time periods.
$extra = isset($extraParams[$start])
? $extraParams[$start] : array();
$testExtra = isset($checkedExtra[$testStart])
? $checkedExtra[$testStart] : array();
// Remove old period entry.
unset($checked[$testStart]);
unset($checkedExtra[$testStart]);
// We have two periods overlapping. Are their
// additional parameters the same or different?
$newStart = min($start, $testStart);
$newEnd = max($end, $testEnd);
if ($extra === $testExtra) {
// Both periods have the same information. So we
// can just merge.
$checked[$newStart] = $newEnd;
$checkedExtra[$newStart] = $extra;
} else {
// Extra parameters are different. Create one
// period at the beginning with the params of the
// first period and create a trailing period with
// the params of the second period. The break
// point will be the end of the first period.
$break = min($end, $testEnd);
$checked[$newStart] = $break;
$checkedExtra[$newStart] =
isset($extraParams[$newStart])
? $extraParams[$newStart] : array();
$checked[$break] = $newEnd;
$highStart = max($start, $testStart);
$checkedExtra[$break] =
isset($extraParams[$highStart])
? $extraParams[$highStart] : array();
// Ensure we also have the extra data in the
// extraParams.
$extraParams[$break] =
isset($extraParams[$highStart])
? $extraParams[$highStart] : array();
}
$added = true; $added = true;
} }
if ($added) { if ($added) {
break; break;
} }
} }
if (!$added) { if (!$added) {
$checked[$start] = $end; $checked[$start] = $end;
$checkedExtra[$start] = isset($extraParams[$start])
? $extraParams[$start] : array();
} }
} }
} }
ksort($checked, SORT_NUMERIC);
$this->_busyPeriods = $checked; return array($checked, $checkedExtra);
} }
} }

View File

@ -2,15 +2,14 @@
/** /**
* Class representing vJournals. * Class representing vJournals.
* *
* $Horde: framework/iCalendar/iCalendar/vjournal.php,v 1.8 2004/08/13 19:11:35 karsten Exp $ * $Horde: framework/iCalendar/iCalendar/vjournal.php,v 1.8.10.8 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
@ -21,11 +20,6 @@ class Horde_iCalendar_vjournal extends Horde_iCalendar {
return 'vJournal'; return 'vJournal';
} }
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VJOURNAL');
}
function exportvCalendar() function exportvCalendar()
{ {
return parent::_exportvData('VJOURNAL'); return parent::_exportvData('VJOURNAL');

View File

@ -5,29 +5,29 @@ require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
/** /**
* Class representing vNotes. * Class representing vNotes.
* *
* $Horde: framework/iCalendar/iCalendar/vnote.php,v 1.2 2004/08/13 19:11:35 karsten Exp $ * $Horde: framework/iCalendar/iCalendar/vnote.php,v 1.3.10.9 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @author Karsten Fourmont <fourmont@gmx.de> * @author Karsten Fourmont <fourmont@gmx.de>
* @version $Revision$
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
class Horde_iCalendar_vnote extends Horde_iCalendar { class Horde_iCalendar_vnote extends Horde_iCalendar {
function Horde_iCalendar_vnote($version = '1.1')
{
return parent::Horde_iCalendar($version);
}
function getType() function getType()
{ {
return 'vNote'; return 'vNote';
} }
function parsevCalendar($data)
{
return parent::parsevCalendar($data, 'VNOTE');
}
/** /**
* Unlike vevent and vtodo, a vnote is normally not enclosed in an * Unlike vevent and vtodo, a vnote is normally not enclosed in an
* iCalendar container. (BEGIN..END) * iCalendar container. (BEGIN..END)
@ -43,7 +43,7 @@ class Horde_iCalendar_vnote extends Horde_iCalendar {
} }
} }
return $this->_exportvData('VNOTE') . $this->_newline; return $this->_exportvData('VNOTE');
} }
} }

View File

@ -2,15 +2,14 @@
/** /**
* Class representing vTimezones. * Class representing vTimezones.
* *
* $Horde: framework/iCalendar/iCalendar/vtimezone.php,v 1.8 2004/08/13 19:11:35 karsten Exp $ * $Horde: framework/iCalendar/iCalendar/vtimezone.php,v 1.8.10.9 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
@ -21,16 +20,145 @@ class Horde_iCalendar_vtimezone extends Horde_iCalendar {
return 'vTimeZone'; return 'vTimeZone';
} }
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VTIMEZONE');
}
function exportvCalendar() function exportvCalendar()
{ {
return parent::_exportvData('VTIMEZONE'); return parent::_exportvData('VTIMEZONE');
} }
/**
* Parse child components of the vTimezone component. Returns an
* array with the exact time of the time change as well as the
* 'from' and 'to' offsets around the change. Time is arbitrarily
* based on UTC for comparison.
*/
function parseChild(&$child, $year)
{
// Make sure 'time' key is first for sort().
$result['time'] = 0;
$t = $child->getAttribute('TZOFFSETFROM');
if (is_a($t, 'PEAR_Error')) {
return false;
}
$result['from'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);
$t = $child->getAttribute('TZOFFSETTO');
if (is_a($t, 'PEAR_Error')) {
return false;
}
$result['to'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);
$switch_time = $child->getAttribute('DTSTART');
if (is_a($switch_time, 'PEAR_Error')) {
return false;
}
$rrules = $child->getAttribute('RRULE');
if (is_a($rrules, 'PEAR_Error')) {
if (!is_int($switch_time)) {
return false;
}
// Convert this timestamp from local time to UTC for
// comparison (All dates are compared as if they are UTC).
$t = getdate($switch_time);
$result['time'] = @gmmktime($t['hours'], $t['minutes'], $t['seconds'],
$t['mon'], $t['mday'], $t['year']);
return $result;
}
$rrules = explode(';', $rrules);
foreach ($rrules as $rrule) {
$t = explode('=', $rrule);
switch ($t[0]) {
case 'FREQ':
if ($t[1] != 'YEARLY') {
return false;
}
break;
case 'INTERVAL':
if ($t[1] != '1') {
return false;
}
break;
case 'BYMONTH':
$month = intval($t[1]);
break;
case 'BYDAY':
$len = strspn($t[1], '1234567890-+');
if ($len == 0) {
return false;
}
$weekday = substr($t[1], $len);
$weekdays = array(
'SU' => 0,
'MO' => 1,
'TU' => 2,
'WE' => 3,
'TH' => 4,
'FR' => 5,
'SA' => 6
);
$weekday = $weekdays[$weekday];
$which = intval(substr($t[1], 0, $len));
break;
case 'UNTIL':
if (intval($year) > intval(substr($t[1], 0, 4))) {
return false;
}
break;
}
}
if (empty($month) || !isset($weekday)) {
return false;
}
if (is_int($switch_time)) {
// Was stored as localtime.
$switch_time = strftime('%H:%M:%S', $switch_time);
$switch_time = explode(':', $switch_time);
} else {
$switch_time = explode('T', $switch_time);
if (count($switch_time) != 2) {
return false;
}
$switch_time[0] = substr($switch_time[1], 0, 2);
$switch_time[2] = substr($switch_time[1], 4, 2);
$switch_time[1] = substr($switch_time[1], 2, 2);
}
// Get the timestamp for the first day of $month.
$when = gmmktime($switch_time[0], $switch_time[1], $switch_time[2],
$month, 1, $year);
// Get the day of the week for the first day of $month.
$first_of_month_weekday = intval(gmstrftime('%w', $when));
// Go to the first $weekday before first day of $month.
if ($weekday >= $first_of_month_weekday) {
$weekday -= 7;
}
$when -= ($first_of_month_weekday - $weekday) * 60 * 60 * 24;
// If going backwards go to the first $weekday after last day
// of $month.
if ($which < 0) {
do {
$when += 60*60*24*7;
} while (intval(gmstrftime('%m', $when)) == $month);
}
// Calculate $weekday number $which.
$when += $which * 60 * 60 * 24 * 7;
$result['time'] = $when;
return $result;
}
} }
/** /**

View File

@ -2,15 +2,14 @@
/** /**
* Class representing vTodos. * Class representing vTodos.
* *
* $Horde: framework/iCalendar/iCalendar/vtodo.php,v 1.13 2004/08/13 19:11:35 karsten Exp $ * $Horde: framework/iCalendar/iCalendar/vtodo.php,v 1.13.10.8 2008/07/03 08:42:58 jan Exp $
* *
* Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
* *
* See the enclosed file COPYING for license information (LGPL). If you * See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Mike Cochrane <mike@graftonhall.co.nz> * @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0 * @since Horde 3.0
* @package Horde_iCalendar * @package Horde_iCalendar
*/ */
@ -21,11 +20,6 @@ class Horde_iCalendar_vtodo extends Horde_iCalendar {
return 'vTodo'; return 'vTodo';
} }
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VTODO');
}
function exportvCalendar() function exportvCalendar()
{ {
return parent::_exportvData('VTODO'); return parent::_exportvData('VTODO');

View File

@ -1,8 +1,8 @@
<?php <?php
/** /**
* Constants are from Binary XML Content Format Specification Version * Constants are from Binary XML Content Format Specification Version 1.3, 25
* 1.3, 25 July 2001 found at http://www.wapforum.org * July 2001 found at http://www.wapforum.org
*/ */
/** /**
@ -49,8 +49,16 @@ define('DPI_DTD_WML_1_3', '-//WAPFORUM//DTD WML 1.3//EN');
define('DPI_DTD_PROV_1_0', '-//WAPFORUM//DTD PROV 1.0//EN'); define('DPI_DTD_PROV_1_0', '-//WAPFORUM//DTD PROV 1.0//EN');
define('DPI_DTD_WTA_WML_1_2', '-//WAPFORUM//DTD WTA-WML 1.2//EN'); define('DPI_DTD_WTA_WML_1_2', '-//WAPFORUM//DTD WTA-WML 1.2//EN');
define('DPI_DTD_CHANNEL_1_2', '-//WAPFORUM//DTD CHANNEL 1.2//EN'); define('DPI_DTD_CHANNEL_1_2', '-//WAPFORUM//DTD CHANNEL 1.2//EN');
define('DPI_DTD_SYNCML_1_0', '-//SYNCML//DTD SyncML 1.0//EN');
define('DPI_DTD_DEVINF_1_0', '-//SYNCML//DTD DevInf 1.0//EN');
define('DPI_DTD_METINF_1_0', '-//SYNCML//DTD MetInf 1.0//EN');
define('DPI_DTD_SYNCML_1_1', '-//SYNCML//DTD SyncML 1.1//EN'); define('DPI_DTD_SYNCML_1_1', '-//SYNCML//DTD SyncML 1.1//EN');
define('DPI_DTD_DEVINF_1_1', '-//SYNCML//DTD DevInf 1.1//EN'); define('DPI_DTD_DEVINF_1_1', '-//SYNCML//DTD DevInf 1.1//EN');
define('DPI_DTD_METINF_1_1', '-//SYNCML//DTD MetInf 1.1//EN');
define('DPI_DTD_SYNCML_1_2', '-//SYNCML//DTD SyncML 1.2//EN');
define('DPI_DTD_DEVINF_1_2', '-//SYNCML//DTD DevInf 1.2//EN');
define('DPI_DTD_METINF_1_2', '-//SYNCML//DTD MetInf 1.2//EN');
/** /**
* Only default character encodings from J2SE are currently supported. * Only default character encodings from J2SE are currently supported.
@ -63,13 +71,14 @@ define('CHARSET_UTF_16LE', 'UTF-16LE');
define('CHARSET_UTF_16', 'UTF-16'); define('CHARSET_UTF_16', 'UTF-16');
/** /**
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML.php,v 1.13.12.11 2008/01/02 11:31:02 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* $Horde: framework/XML_WBXML/WBXML.php,v 1.18 2006/01/01 21:10:25 jan Exp $ * @author Anthony Mills <amills@pyramid6.com>
*
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML { class XML_WBXML {
@ -173,8 +182,17 @@ class XML_WBXML {
// Not all SyncML clients know this, so we // Not all SyncML clients know this, so we
// should use the string table. // should use the string table.
// 0xFD1 => DPI_DTD_SYNCML_1_1, // 0xFD1 => DPI_DTD_SYNCML_1_1,
4051 => DPI_DTD_SYNCML_1_1, // These codes are taken from libwbxml wbxml_tables.h:
4052 => DPI_DTD_DEVINF_1_1, 4049 => DPI_DTD_SYNCML_1_0, // 0x0fd1
4050 => DPI_DTD_DEVINF_1_0, // 0x0fd2
4051 => DPI_DTD_SYNCML_1_1, // 0x0fd3
4052 => DPI_DTD_DEVINF_1_1, // 0x0fd4
4609 => DPI_DTD_SYNCML_1_2, // 0x1201
//@todo: verify this:
4611 => DPI_DTD_DEVINF_1_2 // 0x1203
// taken from libxml but might be wrong:
// 4610 => DPI_DTD_DEVINF_1_2, // 0x1202
// 4611 => DPI_DTD_METINF_1_2 // 0x1203
); );
return isset($DPIString[$i]) ? $DPIString[$i] : null; return isset($DPIString[$i]) ? $DPIString[$i] : null;
} }
@ -197,8 +215,18 @@ class XML_WBXML {
DPI_DTD_WTA_WML_1_2 => 12, DPI_DTD_WTA_WML_1_2 => 12,
DPI_DTD_CHANNEL_1_2 => 13, DPI_DTD_CHANNEL_1_2 => 13,
// Not all SyncML clients know this, so we // Not all SyncML clients know this, so maybe we
// should use the string table. // should use the string table.
// These codes are taken from libwbxml wbxml_tables.h:
DPI_DTD_SYNCML_1_0 => 4049,
DPI_DTD_DEVINF_1_0 => 4050,
DPI_DTD_SYNCML_1_1 => 4051,
DPI_DTD_DEVINF_1_1 => 4052,
DPI_DTD_SYNCML_1_2 => 4609, // 0x1201
// DPI_DTD_DEVINF_1_2 => 4610, // 0x1202
// DPI_DTD_METINF_1_2 => 4611 // 0x1203
//@todo: verify this
DPI_DTD_DEVINF_1_2 => 4611 // 0x1203
// DPI_DTD_SYNCML_1_1 => 0xFD1, // DPI_DTD_SYNCML_1_1 => 0xFD1,
// DPI_DTD_DEVINF_1_1 => 0xFD2, // DPI_DTD_DEVINF_1_1 => 0xFD2,
); );

View File

@ -1,15 +1,16 @@
<?php <?php
/** /**
* $Horde: framework/XML_WBXML/WBXML/ContentHandler.php,v 1.15 2006/01/01 21:10:25 jan Exp $
*
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com>
*
* See the enclosed file COPYING for license information (LGPL). If you did
* not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* From Binary XML Content Format Specification Version 1.3, 25 July 2001 * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org * found at http://www.wapforum.org
* *
* $Horde: framework/XML_WBXML/WBXML/ContentHandler.php,v 1.9.10.11 2008/08/26 15:41:13 jan Exp $
*
* Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_ContentHandler { class XML_WBXML_ContentHandler {
@ -35,9 +36,13 @@ class XML_WBXML_ContentHandler {
$this->_currentUri = new XML_WBXML_LifoQueue(); $this->_currentUri = new XML_WBXML_LifoQueue();
} }
/**
*/
function raiseError($error) function raiseError($error)
{ {
include_once 'PEAR.php'; if (!class_exists('PEAR')) {
require 'PEAR.php';
}
return PEAR::raiseError($error); return PEAR::raiseError($error);
} }
@ -71,7 +76,7 @@ class XML_WBXML_ContentHandler {
return strlen($this->_output); return strlen($this->_output);
} }
function startElement($uri, $element, $attrs) function startElement($uri, $element, $attrs = array())
{ {
$this->_output .= '<' . $element; $this->_output .= '<' . $element;
@ -104,12 +109,7 @@ class XML_WBXML_ContentHandler {
function opaque($o) function opaque($o)
{ {
// I can check the first chanracter and see if it is WBXML. $this->_output .= $o;
if (ord($o[0]) < 10) {
// Should decode this, I really need a call back function.
} else {
$this->_output .= $o;
}
} }
function setOpaqueHandler($opaqueHandler) function setOpaqueHandler($opaqueHandler)
@ -122,6 +122,15 @@ class XML_WBXML_ContentHandler {
unset($this->_opaqueHandler); unset($this->_opaqueHandler);
} }
function createSubHandler()
{
$name = get_class($this); // clone current class
$sh = new $name();
$sh->setCharset($this->getCharsetStr());
$sh->setVersion($this->getVersion());
return $sh;
}
} }
class XML_WBXML_LifoQueue { class XML_WBXML_LifoQueue {

View File

@ -1,15 +1,16 @@
<?php <?php
/** /**
* $Horde: framework/XML_WBXML/WBXML/DTD.php,v 1.8 2006/01/01 21:10:25 jan Exp $
*
* From Binary XML Content Format Specification Version 1.3, 25 July 2001 * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org * found at http://www.wapforum.org
* *
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML/DTD.php,v 1.6.12.8 2008/01/02 11:31:02 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_DTD { class XML_WBXML_DTD {
@ -88,6 +89,11 @@ class XML_WBXML_DTD {
function toCodePageURI($uri) function toCodePageURI($uri)
{ {
$uri = strtolower($uri); $uri = strtolower($uri);
if (!isset($this->strCodePagesURI[$uri])) {
//Horde::logMessage("WBXML unable to find codepage for $uri!", __FILE__, __LINE__, PEAR_LOG_DEBUG);
//die("unable to find codepage for $uri!\n");
}
$ret = isset($this->strCodePagesURI[$uri]) ? $this->strCodePagesURI[$uri] : false; $ret = isset($this->strCodePagesURI[$uri]) ? $this->strCodePagesURI[$uri] : false;
return $ret; return $ret;

View File

@ -3,16 +3,17 @@
include_once 'XML/WBXML/DTD.php'; include_once 'XML/WBXML/DTD.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/DTD/SyncML.php,v 1.11 2006/01/01 21:10:26 jan Exp $
*
* From Binary XML Content Format Specification Version 1.3, 25 July 2001 * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org * found at http://www.wapforum.org
* *
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML/DTD/SyncML.php,v 1.6.12.8 2008/01/02 11:31:03 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_DTD_SyncML extends XML_WBXML_DTD { class XML_WBXML_DTD_SyncML extends XML_WBXML_DTD {
@ -72,17 +73,29 @@ class XML_WBXML_DTD_SyncML extends XML_WBXML_DTD {
$this->setTag(0x30, "Reserved for future use"); // 0x00 $this->setTag(0x30, "Reserved for future use"); // 0x00
$this->setTag(0x31, "VerDTD"); // 0x00 $this->setTag(0x31, "VerDTD"); // 0x00
$this->setTag(0x32, "VerProto"); // 0x00 $this->setTag(0x32, "VerProto"); // 0x00
$this->setTag(0x33, "NumberOfChanged"); // 0x00 $this->setTag(0x33, "NumberOfChanges"); // 0x00
$this->setTag(0x34, "MoreData"); // 0x00 $this->setTag(0x34, "MoreData"); // 0x00
$this->setTag(0x35, "Field"); // 0x00
$this->setTag(0x36, "Filter"); // 0x00
$this->setTag(0x37, "Record"); // 0x00
$this->setTag(0x38, "FilterType"); // 0x00
$this->setTag(0x39, "SourceParent"); // 0x00
$this->setTag(0x3a, "TargetParent"); // 0x00
$this->setTag(0x3b, "Move"); // 0x00
$this->setTag(0x3c, "Correlator"); // 0x00
if ($this->version == 0) { if ($this->version == 1) {
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:syncml1.0'); $this->setCodePage(0, DPI_DTD_SYNCML_1_1, 'syncml:syncml1.1');
$this->setCodePage(1, '-//SYNCML//DTD MetInf 1.0//EN', 'syncml:metinf'); $this->setCodePage(1, DPI_DTD_METINF_1_1, 'syncml:metinf1.1');
$this->setURI('syncml:syncml1.0');
} else {
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.1//EN', 'syncml:syncml1.1');
$this->setCodePage(1, '-//SYNCML//DTD MetInf 1.1//EN', 'syncml:metinf1.1');
$this->setURI('syncml:syncml1.1'); $this->setURI('syncml:syncml1.1');
} elseif ($this->version == 2) {
$this->setCodePage(0, DPI_DTD_SYNCML_1_2, 'syncml:syncml1.2');
$this->setCodePage(1, DPI_DTD_METINF_1_2, 'syncml:metinf1.2');
$this->setURI('syncml:syncml1.2');
} else {
$this->setCodePage(0, DPI_DTD_SYNCML_1_0, 'syncml:syncml1.0');
$this->setCodePage(1, DPI_DTD_METINF_1_0, 'syncml:metinf1.0');
$this->setURI('syncml:syncml1.0');
} }
} }

View File

@ -3,16 +3,17 @@
include_once 'XML/WBXML/DTD.php'; include_once 'XML/WBXML/DTD.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/DTD/SyncMLDevInf.php,v 1.11 2006/01/01 21:10:26 jan Exp $
*
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com>
*
* From Binary XML Content Format Specification Version 1.3, 25 July 2001 * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org * found at http://www.wapforum.org
* *
* See the enclosed file COPYING for license information (LGPL). If you * $Horde: framework/XML_WBXML/WBXML/DTD/SyncMLDevInf.php,v 1.4.12.8 2008/01/02 11:31:03 jan Exp $
*
* Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_DTD_SyncMLDevInf extends XML_WBXML_DTD { class XML_WBXML_DTD_SyncMLDevInf extends XML_WBXML_DTD {
@ -26,6 +27,8 @@ class XML_WBXML_DTD_SyncMLDevInf extends XML_WBXML_DTD {
* | sed -e 's#^.*\"\([^\"]*\)\", *\(0x..\), \(0x..\) },.*$# \$this->setTag\(\3, \"\1\"\); // \2#g' * | sed -e 's#^.*\"\([^\"]*\)\", *\(0x..\), \(0x..\) },.*$# \$this->setTag\(\3, \"\1\"\); // \2#g'
*/ */
#Horde::logMessage("XML_WBXML_DTD_SyncMLDevInf version=" . $this->version, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->setTag(0x05, "CTCap"); // 0x00 $this->setTag(0x05, "CTCap"); // 0x00
$this->setTag(0x06, "CTType"); // 0x00 $this->setTag(0x06, "CTType"); // 0x00
$this->setTag(0x07, "DataStore"); // 0x00 $this->setTag(0x07, "DataStore"); // 0x00
@ -64,13 +67,25 @@ class XML_WBXML_DTD_SyncMLDevInf extends XML_WBXML_DTD {
$this->setTag(0x28, "UTC"); // 0x00 $this->setTag(0x28, "UTC"); // 0x00
$this->setTag(0x29, "SupportNumberOfChanges"); // 0x00 $this->setTag(0x29, "SupportNumberOfChanges"); // 0x00
$this->setTag(0x2a, "SupportLargeObjs"); // 0x00 $this->setTag(0x2a, "SupportLargeObjs"); // 0x00
$this->setTag(0x2b, "Property"); // 0x00
$this->setTag(0x2c, "PropParam"); // 0x00
$this->setTag(0x2d, "MaxOccur"); // 0x00
$this->setTag(0x2e, "NoTruncate"); // 0x00
$this->setTag(0x30, "Filter-Rx"); // 0x00
$this->setTag(0x31, "FilterCap"); // 0x00
$this->setTag(0x32, "FilterKeyword"); // 0x00
$this->setTag(0x33, "FieldLevel"); // 0x00
$this->setTag(0x34, "SupportHierarchicalSync"); // 0x00
if ($this->version == 0) { if ($this->version == 1) {
$this->setCodePage(0, '-//SYNCML//DTD DevInf 1.0//EN', 'syncml:devinf'); $this->setCodePage(0, DPI_DTD_DEVINF_1_1, 'syncml:devinf1.1');
$this->setURI('syncml:devinf');
} else {
$this->setCodePage(0, '-//SYNCML//DTD DevInf 1.1//EN', 'syncml:devinf1.1');
$this->setURI('syncml:devinf1.1'); $this->setURI('syncml:devinf1.1');
} elseif ($this->version == 2) {
$this->setCodePage(0, DPI_DTD_DEVINF_1_2, 'syncml:devinf1.2');
$this->setURI('syncml:devinf1.2');
} else {
$this->setCodePage(0, DPI_DTD_DEVINF_1_0, 'syncml:devinf1.0');
$this->setURI('syncml:devinf1.0');
} }
} }

View File

@ -3,16 +3,17 @@
include_once 'XML/WBXML/DTD.php'; include_once 'XML/WBXML/DTD.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/DTD/SyncMLMetInf.php,v 1.9 2006/01/01 21:10:26 jan Exp $
*
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com>
*
* From Binary XML Content Format Specification Version 1.3, 25 July 2001 * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org * found at http://www.wapforum.org
* *
* See the enclosed file COPYING for license information (LGPL). If you * $Horde: framework/XML_WBXML/WBXML/DTD/SyncMLMetInf.php,v 1.4.12.8 2008/01/02 11:31:03 jan Exp $
*
* Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_DTD_SyncMLMetInf extends XML_WBXML_DTD { class XML_WBXML_DTD_SyncMLMetInf extends XML_WBXML_DTD {
@ -43,17 +44,22 @@ class XML_WBXML_DTD_SyncMLMetInf extends XML_WBXML_DTD {
$this->setTag(0x12, "Size"); // 0x01 $this->setTag(0x12, "Size"); // 0x01
$this->setTag(0x13, "Type"); // 0x01 $this->setTag(0x13, "Type"); // 0x01
$this->setTag(0x14, "Version"); // 0x01 $this->setTag(0x14, "Version"); // 0x01
$this->setTag(0x15, "MaxObjSize"); // 0x01
$this->setTag(0x16, "FieldLevel"); // 0x01
if ($this->version == 0) { if ($this->version == 1) {
#$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:SYNCML1.0'); $this->setCodePage(0, DPI_DTD_SYNCML_1_1, 'syncml:syncml1.1');
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:syncml1.0'); $this->setCodePage(1, DPI_DTD_METINF_1_1, 'syncml:metinf1.1');
$this->setCodePage(1, '-//SYNCML//DTD MetInf 1.0//EN', 'syncml:metinf');
$this->setURI('syncml:metinf');
} else {
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.1//EN', 'syncml:syncml1.1');
$this->setCodePage(1, '-//SYNCML//DTD MetInf 1.1//EN', 'syncml:metinf1.1');
$this->setURI('syncml:metinf1.1'); $this->setURI('syncml:metinf1.1');
//$this->setURI('syncml:metinf'); // for some funny reason, libwbxml produces no :metinf1.1 here //$this->setURI('syncml:metinf'); // for some funny reason, libwbxml produces no :metinf1.1 here
} elseif ($this->version == 2) {
$this->setCodePage(0, DPI_DTD_SYNCML_1_2, 'syncml:syncml1.2');
$this->setCodePage(1, DPI_DTD_METINF_1_2, 'syncml:metinf1.2');
$this->setURI('syncml:metinf1.2');
} else {
$this->setCodePage(0, DPI_DTD_SYNCML_1_0, 'syncml:syncml1.0');
$this->setCodePage(1, DPI_DTD_METINF_1_0, 'syncml:metinf1.0');
$this->setURI('syncml:metinf1.0');
} }
} }

View File

@ -5,50 +5,94 @@ include_once 'XML/WBXML/DTD/SyncMLMetInf.php';
include_once 'XML/WBXML/DTD/SyncMLDevInf.php'; include_once 'XML/WBXML/DTD/SyncMLDevInf.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/DTDManager.php,v 1.7 2006/01/01 21:10:25 jan Exp $ * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org
* *
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML/DTDManager.php,v 1.3.12.14 2008/01/02 11:31:02 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* From Binary XML Content Format Specification Version 1.3, 25 July * @author Anthony Mills <amills@pyramid6.com>
* 2001 found at http://www.wapforum.org
*
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_DTDManager { class XML_WBXML_DTDManager {
/**
* @var array
*/
var $_strDTD = array(); var $_strDTD = array();
/**
* @var array
*/
var $_strDTDURI = array(); var $_strDTDURI = array();
/**
*/
function XML_WBXML_DTDManager() function XML_WBXML_DTDManager()
{ {
$this->registerDTD('-//SYNCML//DTD SyncML 1.0//EN', 'syncml:syncml1.0', new XML_WBXML_DTD_SyncML(0)); $this->registerDTD(DPI_DTD_SYNCML_1_0, 'syncml:syncml1.0', new XML_WBXML_DTD_SyncML(0));
$this->registerDTD('-//SYNCML//DTD SyncML 1.1//EN', 'syncml:syncml1.1', new XML_WBXML_DTD_SyncML(1)); $this->registerDTD(DPI_DTD_SYNCML_1_1, 'syncml:syncml1.1', new XML_WBXML_DTD_SyncML(1));
$this->registerDTD(DPI_DTD_SYNCML_1_2, 'syncml:syncml1.2', new XML_WBXML_DTD_SyncML(2));
$this->registerDTD('-//SYNCML//DTD MetInf 1.0//EN', 'syncml:metinf', new XML_WBXML_DTD_SyncMLMetInf(0)); $this->registerDTD(DPI_DTD_METINF_1_0, 'syncml:metinf1.0', new XML_WBXML_DTD_SyncMLMetInf(0));
$this->registerDTD('-//SYNCML//DTD MetInf 1.1//EN', 'syncml:metinf1.1', new XML_WBXML_DTD_SyncMLMetInf(1)); $this->registerDTD(DPI_DTD_METINF_1_1, 'syncml:metinf1.1', new XML_WBXML_DTD_SyncMLMetInf(1));
$this->registerDTD(DPI_DTD_METINF_1_2, 'syncml:metinf1.2', new XML_WBXML_DTD_SyncMLMetInf(2));
$this->registerDTD('-//SYNCML//DTD DevInf 1.0//EN', 'syncml:devinf', new XML_WBXML_DTD_SyncMLDevInf(0)); $this->registerDTD(DPI_DTD_DEVINF_1_0, 'syncml:devinf1.0', new XML_WBXML_DTD_SyncMLDevInf(0));
$this->registerDTD('-//SYNCML//DTD DevInf 1.1//EN', 'syncml:devinf1.1', new XML_WBXML_DTD_SyncMLDevInf(1)); $this->registerDTD(DPI_DTD_DEVINF_1_1, 'syncml:devinf1.1', new XML_WBXML_DTD_SyncMLDevInf(1));
$this->registerDTD(DPI_DTD_DEVINF_1_2, 'syncml:devinf1.2', new XML_WBXML_DTD_SyncMLDevInf(2));
} }
function getInstance($publicIdentifier) /**
*/
function &getInstance($publicIdentifier)
{ {
return isset($this->_strDTD[$publicIdentifier]) ? $this->_strDTD[$publicIdentifier] : null; $publicIdentifier = strtolower($publicIdentifier);
if (isset($this->_strDTD[$publicIdentifier])) {
$dtd = &$this->_strDTD[$publicIdentifier];
} else {
$dtd = null;
}
return $dtd;
} }
function getInstanceURI($uri) /**
*/
function &getInstanceURI($uri)
{ {
$uri = strtolower($uri); $uri = strtolower($uri);
return isset($this->_strDTDURI[$uri]) ? $this->_strDTDURI[$uri] : null;
// some manual hacks:
if ($uri == 'syncml:syncml') {
$uri = 'syncml:syncml1.0';
}
if ($uri == 'syncml:metinf') {
$uri = 'syncml:metinf1.0';
}
if ($uri == 'syncml:devinf') {
$uri = 'syncml:devinf1.0';
}
if (isset($this->_strDTDURI[$uri])) {
$dtd = &$this->_strDTDURI[$uri];
} else {
$dtd = null;
}
return $dtd;
} }
/**
*/
function registerDTD($publicIdentifier, $uri, &$dtd) function registerDTD($publicIdentifier, $uri, &$dtd)
{ {
$dtd->setDPI($publicIdentifier); $dtd->setDPI($publicIdentifier);
$publicIdentifier = strtolower($publicIdentifier);
$this->_strDTD[$publicIdentifier] = $dtd; $this->_strDTD[$publicIdentifier] = $dtd;
$this->_strDTDURI[strtolower($uri)] = $dtd; $this->_strDTDURI[strtolower($uri)] = $dtd;
} }

View File

@ -5,16 +5,17 @@ include_once 'XML/WBXML/DTDManager.php';
include_once 'XML/WBXML/ContentHandler.php'; include_once 'XML/WBXML/ContentHandler.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/Decoder.php,v 1.36 2006/01/01 21:10:25 jan Exp $ * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org
* *
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML/Decoder.php,v 1.22.10.11 2008/01/02 11:31:02 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* From Binary XML Content Format Specification Version 1.3, 25 July * @author Anthony Mills <amills@pyramid6.com>
* 2001 found at http://www.wapforum.org
*
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_Decoder extends XML_WBXML_ContentHandler { class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
@ -84,7 +85,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
* *
* @param XML_WBXML_ContentHandler $ch The contentHandler * @param XML_WBXML_ContentHandler $ch The contentHandler
*/ */
function setContentHandler(&$ch) { function setContentHandler(&$ch)
{
$this->_ch = &$ch; $this->_ch = &$ch;
} }
/** /**
@ -96,7 +98,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
{ {
$value = $input{$this->_strpos++}; $value = $input{$this->_strpos++};
$value = ord($value); $value = ord($value);
return $value; return $value;
} }
@ -133,12 +135,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
*/ */
function decode($wbxml) function decode($wbxml)
{ {
// fix for Nokia Series 60 which seem to send empty data block sometimes
if(strlen($wbxml) == 0) {
return true;
}
$this->_error = false; // reset state $this->_error = false; // reset state
$this->_strpos = 0; $this->_strpos = 0;
if (empty($this->_ch)) { if (empty($this->_ch)) {
@ -149,6 +147,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// version = u_int8 // version = u_int8
// currently 1, 2 or 3 // currently 1, 2 or 3
$this->_wbxmlVersion = $this->getVersionNumber($wbxml); $this->_wbxmlVersion = $this->getVersionNumber($wbxml);
#Horde::logMessage("WBXML[" . $this->_strpos . "] version " . $this->_wbxmlVersion, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Get Document Public Idetifier from Section 5.5 // Get Document Public Idetifier from Section 5.5
// publicid = mb_u_int32 | (zero index) // publicid = mb_u_int32 | (zero index)
@ -156,9 +155,11 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// Containing the value zero (0) // Containing the value zero (0)
// The actual DPI is determined after the String Table is read. // The actual DPI is determined after the String Table is read.
$dpiStruct = $this->getDocumentPublicIdentifier($wbxml); $dpiStruct = $this->getDocumentPublicIdentifier($wbxml);
// Get Charset from 5.6 // Get Charset from 5.6
// charset = mb_u_int32 // charset = mb_u_int32
$this->_charset = $this->getCharset($wbxml); $this->_charset = $this->getCharset($wbxml);
#Horde::logMessage("WBXML[" . $this->_strpos . "] charset " . $this->_charset, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Get String Table from 5.7 // Get String Table from 5.7
// strb1 = length *byte // strb1 = length *byte
@ -166,8 +167,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// Get Document Public Idetifier from Section 5.5. // Get Document Public Idetifier from Section 5.5.
$this->_dpi = $this->getDocumentPublicIdentifierImpl($dpiStruct['dpiType'], $this->_dpi = $this->getDocumentPublicIdentifierImpl($dpiStruct['dpiType'],
$dpiStruct['dpiNumber'], $dpiStruct['dpiNumber']);
$this->_stringTable); #$this->_stringTable);
// Now the real fun begins. // Now the real fun begins.
// From Sections 5.2 and 5.8 // From Sections 5.2 and 5.8
@ -180,8 +181,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
$this->_tagDTD = $this->_dtdManager->getInstance($this->_dpi); $this->_tagDTD = $this->_dtdManager->getInstance($this->_dpi);
if (!$this->_tagDTD) { if (!$this->_tagDTD) {
return $this->raiseError('No DTD found for ' return $this->raiseError('No DTD found for '
. $this->_dpi . '/' . $this->_dpi . '/'
. $dpiStruct['dpiNumber']); . $dpiStruct['dpiNumber']);
} }
@ -204,6 +205,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
function getDocumentPublicIdentifier($input) function getDocumentPublicIdentifier($input)
{ {
$i = XML_WBXML::MBUInt32ToInt($input, $this->_strpos); $i = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
if ($i == 0) { if ($i == 0) {
return array('dpiType' => 2, return array('dpiType' => 2,
'dpiNumber' => $this->getByte($input)); 'dpiNumber' => $this->getByte($input));
@ -218,6 +220,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
if ($dpiType == 1) { if ($dpiType == 1) {
return XML_WBXML::getDPIString($dpiNumber); return XML_WBXML::getDPIString($dpiNumber);
} else { } else {
#Horde::logMessage("WBXML string table $dpiNumber:\n" . print_r($this->_stringTable, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $this->getStringTableEntry($dpiNumber); return $this->getStringTableEntry($dpiNumber);
} }
} }
@ -235,7 +238,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
} }
/** /**
* Retrieves the string table. * Retrieves the string table.
* The string table consists of an mb_u_int32 length * The string table consists of an mb_u_int32 length
* and then length bytes forming the table. * and then length bytes forming the table.
* References to the string table refer to the * References to the string table refer to the
@ -254,10 +257,10 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
{ {
if ($index >= strlen($this->_stringTable)) { if ($index >= strlen($this->_stringTable)) {
$this->_error = $this->_error =
$this->_ch->raiseError('Invalid offset ' . $index $this->raiseError('Invalid offset ' . $index
. ' value encountered around position ' . ' value encountered around position '
. $this->_strpos . $this->_strpos
. '. Broken wbxml?'); . '. Broken wbxml?');
return ''; return '';
} }
@ -270,17 +273,17 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
if (ord($ch) == 0) { if (ord($ch) == 0) {
return ''; // don't return '#' return ''; // don't return '#'
} }
while (ord($ch) != 0) { while (ord($ch) != 0) {
$str[$i++] = $ch; $str[$i++] = $ch;
if ($index >= strlen($this->_stringTable)) { if ($index >= strlen($this->_stringTable)) {
break; break;
} }
$ch = $this->_stringTable[$index++]; $ch = $this->_stringTable[$index++];
} }
// print "string table entry: $str\n"; // print "string table entry: $str\n";
return $str; return $str;
} }
function _decode($input) function _decode($input)
@ -288,7 +291,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
$token = $this->getByte($input); $token = $this->getByte($input);
$str = ''; $str = '';
#print "position: " . $this->_strpos . " token: " . $token . " str10: " . substr($input, $this->_strpos, 10) . "\n"; // @todo: remove debug output // print "position: " . $this->_strpos . " token: " . $token . " str10: " . substr($input, $this->_strpos, 10) . "\n"; // @todo: remove debug output
switch ($token) { switch ($token) {
case XML_WBXML_GLOBAL_TOKEN_STR_I: case XML_WBXML_GLOBAL_TOKEN_STR_I:
@ -370,34 +373,36 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
case XML_WBXML_GLOBAL_TOKEN_OPAQUE: case XML_WBXML_GLOBAL_TOKEN_OPAQUE:
// Section 5.8.4.6 // Section 5.8.4.6
$size = XML_WBXML::MBUInt32ToInt($input, $this->_strpos); $size = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
// print "opaque of size $size\n"; // @todo remove debug if ($size > 0) {
$b = $this->_substr($input, $this->_strpos, $size); #Horde::logMessage("WBXML opaque document size=$size, next=" . ord($input{$this->_strpos}), __FILE__, __LINE__, PEAR_LOG_DEBUG);
#$b = mb_substr($input, $this->_strpos, $size, 'ISO-8859-1'); $b = $this->_substr($input, $this->_strpos, $size);
$this->_strpos += $size; // print "opaque of size $size: ($b)\n"; // @todo remove debug
$this->_strpos += $size;
// opaque data inside a <data> element may or may not be
// a nested wbxml document (for example devinf data).
// We find out by checking the first byte of the data: if it's
// 1, 2 or 3 we expect it to be the version number of a wbxml
// document and thus start a new wbxml decoder instance on it.
// opaque data inside a <data> element may or may not be if ($this->_isData && ord($b) < 10) {
// a nested wbxml document (for example devinf data). #Horde::logMessage("WBXML opaque document size=$size, \$b[0]=" . ord($b), __FILE__, __LINE__, PEAR_LOG_DEBUG);
// We find out by checking the first byte of the data: if it's $decoder = new XML_WBXML_Decoder(true);
// 1, 2 or 3 we expect it to be the version number of a wbxml $decoder->setContentHandler($this->_ch);
// document and thus start a new wbxml decoder instance on it. $s = $decoder->decode($b);
// /* // @todo: FIXME currently we can't decode Nokia
if ($this->_isData && ord($b) <= 10) { // DevInf data. So ignore error for the time beeing.
$decoder = new XML_WBXML_Decoder(true); if (is_a($s, 'PEAR_Error')) {
$decoder->setContentHandler($this->_ch); $this->_error = $s;
$s = $decoder->decode($b); return;
// /* // @todo: FIXME currently we can't decode Nokia }
// DevInf data. So ignore error for the time beeing. // */
if (is_a($s, 'PEAR_Error')) { // $this->_ch->characters($s);
$this->_error = $s; } else {
return; /* normal opaque behaviour: just copy the raw data: */
// print "opaque handled as string=$b\n"; // @todo remove debug
$this->_ch->characters($b);
} }
// */
// $this->_ch->characters($s);
} else {
/* normal opaque behaviour: just copy the raw data: */
$this->_ch->characters( $b);
} }
// old approach to deal with opaque data inside ContentHandler: // old approach to deal with opaque data inside ContentHandler:
// FIXME Opaque is used by SYNCML. Opaque data that depends on the context // FIXME Opaque is used by SYNCML. Opaque data that depends on the context
// if (contentHandler instanceof OpaqueContentHandler) { // if (contentHandler instanceof OpaqueContentHandler) {
@ -650,7 +655,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
*/ */
function termstr($input) function termstr($input)
{ {
$str = '#'; // must start with nonempty string to allow array access $str = '#'; // must start with nonempty string to allow array access
$i = 0; $i = 0;
$ch = $input[$this->_strpos++]; $ch = $input[$this->_strpos++];
if (ord($ch) == 0) { if (ord($ch) == 0) {
@ -679,6 +684,5 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
} }
return $ret; return $ret;
} }
} }

View File

@ -6,16 +6,17 @@ include_once 'XML/WBXML/DTDManager.php';
include_once 'Horde/String.php'; include_once 'Horde/String.php';
/** /**
* $Horde: framework/XML_WBXML/WBXML/Encoder.php,v 1.39 2006/01/01 21:10:25 jan Exp $ * From Binary XML Content Format Specification Version 1.3, 25 July 2001
* found at http://www.wapforum.org
* *
* Copyright 2003-2006 Anthony Mills <amills@pyramid6.com> * $Horde: framework/XML_WBXML/WBXML/Encoder.php,v 1.25.10.17 2008/08/26 15:41:21 jan Exp $
* *
* See the enclosed file COPYING for license information (LGPL). If you * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* *
* From Binary XML Content Format Specification Version 1.3, 25 July * @author Anthony Mills <amills@pyramid6.com>
* 2001 found at http://www.wapforum.org
*
* @package XML_WBXML * @package XML_WBXML
*/ */
class XML_WBXML_Encoder extends XML_WBXML_ContentHandler { class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
@ -38,7 +39,7 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
var $_subParser = null; var $_subParser = null;
var $_subParserStack = 0; var $_subParserStack = 0;
/** /**
* The XML parser. * The XML parser.
* *
@ -97,7 +98,10 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function writeHeader($uri) function writeHeader($uri)
{ {
$this->_dtd = &$this->_dtdManager->getInstanceURI($uri); $this->_dtd = &$this->_dtdManager->getInstanceURI($uri);
if (!$this->_dtd) {
// TODO: proper error handling
die('Unable to find dtd for ' . $uri);
}
$dpiString = $this->_dtd->getDPI(); $dpiString = $this->_dtd->getDPI();
// Set Version Number from Section 5.4 // Set Version Number from Section 5.4
@ -132,7 +136,11 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function writeDocumentPublicIdentifier($dpiString, &$strings) function writeDocumentPublicIdentifier($dpiString, &$strings)
{ {
$i = XML_WBXML::getDPIInt($dpiString); $i = 0;
// The OMA test suite doesn't like DPI as integer code.
// So don't try lookup and always send full DPI string.
// $i = XML_WBXML::getDPIInt($dpiString);
if ($i == 0) { if ($i == 0) {
$strings[0] = $dpiString; $strings[0] = $dpiString;
@ -191,7 +199,7 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function _getBytes($string, $cs) function _getBytes($string, $cs)
{ {
$string = String::convertCharset($string, $cs, 'utf-8'); $string = String::convertCharset($string, $cs, 'utf-8');
$nbytes = strlen($string); $nbytes = strlen($string);
$bytes = array(); $bytes = array();
@ -210,8 +218,10 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
return array($uri, $name); return array($uri, $name);
} }
function startElement($uri, $name, $attributes) function startElement($uri, $name, $attributes = array())
{ {
#Horde::logMessage("WBXML Encoder $uri, " . ($this->_hasWrittenHeader ? 'true' : 'false'), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($this->_subParser == null) { if ($this->_subParser == null) {
if (!$this->_hasWrittenHeader) { if (!$this->_hasWrittenHeader) {
$this->writeHeader($uri); $this->writeHeader($uri);
@ -222,11 +232,11 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
if ($this->_subParser == null) { if ($this->_subParser == null) {
$this->writeTag($name, $attributes, true, $this->_charset); $this->writeTag($name, $attributes, true, $this->_charset);
} else { } else {
$this->_subParser->startElement($uri,$name, $attributes); $this->_subParser->startElement($uri, $name, $attributes);
} }
} else { } else {
$this->_subParserStack++; $this->_subParserStack++;
$this->_subParser->startElement($uri,$name,$attributes); $this->_subParser->startElement($uri, $name, $attributes);
} }
} }
@ -237,12 +247,12 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
$this->startElement($uri, $name, $attributes); $this->startElement($uri, $name, $attributes);
} }
function opaque($bytes) function opaque($o)
{ {
if ($this->_subParser == null) { if ($this->_subParser == null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE); $this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
XML_WBXML::intToMBUInt32($this->_output, count($bytes)); XML_WBXML::intToMBUInt32($this->_output, strlen($o));
$this->_output .= $bytes; $this->_output .= $o;
} }
} }
@ -274,7 +284,6 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function writeTag($name, $attrs, $hasContent, $cs) function writeTag($name, $attrs, $hasContent, $cs)
{ {
if ($attrs != null && !count($attrs)) { if ($attrs != null && !count($attrs)) {
$attrs = null; $attrs = null;
} }
@ -309,7 +318,7 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
} }
} }
if ($attrs != null && is_array($attrs) && count($attrs) > 0 ) { if ($attrs != null && is_array($attrs) && count($attrs) > 0) {
$this->writeAttributes($attrs, $cs); $this->writeAttributes($attrs, $cs);
} }
} }
@ -376,11 +385,16 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function changecodepage($uri) function changecodepage($uri)
{ {
// @todo: this is a hack! if ($this->_dtd->getVersion() == 2 && !preg_match('/1\.2$/', $uri)) {
// what's the reason for this hack???? Lars $uri .= '1.2';
if ($uri != 'syncml:devinf' && $uri != 'syncml:metinf' && $uri != 'syncml:syncml1.0' && !preg_match('/1\.1$/', $uri)) { }
if ($this->_dtd->getVersion() == 1 && !preg_match('/1\.1$/', $uri)) {
$uri .= '1.1'; $uri .= '1.1';
} }
if ($this->_dtd->getVersion() == 0 && !preg_match('/1\.0$/', $uri)) {
$uri .= '1.0';
}
$cp = $this->_dtd->toCodePageURI($uri); $cp = $this->_dtd->toCodePageURI($uri);
if (strlen($cp)) { if (strlen($cp)) {
$this->_dtd = &$this->_dtdManager->getInstanceURI($uri); $this->_dtd = &$this->_dtdManager->getInstanceURI($uri);

View File

@ -21,7 +21,7 @@ $conf['auth']['driver'] = 'auto';
$conf['log']['priority'] = PEAR_LOG_DEBUG; $conf['log']['priority'] = PEAR_LOG_DEBUG;
$conf['log']['ident'] = 'EGWSYNC'; $conf['log']['ident'] = 'EGWSYNC';
$conf['log']['params'] = array(); $conf['log']['params'] = array();
$conf['log']['name'] = '/tmp/egroupware_syncml.log'; $conf['log']['name'] = '/tmp/egroupware_syncml-1.6.log';
$conf['log']['params']['append'] = true; $conf['log']['params']['append'] = true;
$conf['log']['type'] = 'error_log'; $conf['log']['type'] = 'error_log';
$conf['log']['enabled'] = true; $conf['log']['enabled'] = true;

View File

@ -21,8 +21,16 @@ ini_set('magic_quotes_runtime', 0);
* include_path, you must add an ini_set() call here to add their location to * include_path, you must add an ini_set() call here to add their location to
* the include_path. */ * the include_path. */
// ini_set('include_path', dirname(__FILE__) . PATH_SEPARATOR . ini_get('include_path')); // ini_set('include_path', dirname(__FILE__) . PATH_SEPARATOR . ini_get('include_path'));
set_include_path(dirname(__FILE__). '/../../horde/' . PATH_SEPARATOR . dirname(__FILE__). '/../../../../egw-pear/' . PATH_SEPARATOR . get_include_path()); //set_include_path(dirname(__FILE__). '/../../horde/' . PATH_SEPARATOR . dirname(__FILE__). '/../../../../egw-pear/' . PATH_SEPARATOR . get_include_path());
@define('EGW_BASE', dirname(dirname(__FILE__) . '/../../../../rpc.php'));
// Check for a prior definition of HORDE_BASE (perhaps by an
// auto_prepend_file definition for site customization).
if (!defined('HORDE_BASE')) {
@define('HORDE_BASE', EGW_BASE . '/phpgwapi/inc/horde/');
}
set_include_path(HORDE_BASE . PATH_SEPARATOR . EGW_BASE . '/egw-pear/' . PATH_SEPARATOR . get_include_path());
/* PEAR base class. */ /* PEAR base class. */
include_once 'PEAR.php'; include_once 'PEAR.php';
@ -30,14 +38,17 @@ include_once 'PEAR.php';
include_once 'Horde.php'; include_once 'Horde.php';
include_once 'Horde/Registry.php'; include_once 'Horde/Registry.php';
#include_once 'Horde/DataTree.php'; #include_once 'Horde/DataTree.php';
#include_once 'Horde/String.php'; include_once 'Horde/String.php';
include_once 'Horde/Date.php';
include_once 'Horde/NLS.php'; include_once 'Horde/NLS.php';
#include_once 'Horde/Notification.php'; include_once 'Horde/iCalendar.php';
#include_once 'Horde/Auth.php'; //include_once 'Horde/Notification.php';
#include_once 'Horde/Browser.php'; //include_once 'Horde/Auth.php';
#include_once 'Horde/Perms.php'; //include_once 'Horde/Browser.php';
//include_once 'Horde/Perms.php';
#/* Browser detection object. */ /* Browser detection object. *
#if (class_exists('Browser')) { if (class_exists('Browser')) {
# $browser = &Browser::singleton(); $browser = &Browser::singleton();
#} }
*/