- 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 int $_id the internal egwapp content id
* @return bool
* @return boolean
*/
function expireMapping($_appName, $_id)
{
@ -163,7 +163,7 @@ class contenthistory
// now update the time stamp
$newData = array (
'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__);
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. */
$errorLogging = ob_get_clean();
if (!empty($errorLogging)) {
#Horde::logMessage('SyncML: caught output=' .
# $errorLogging, __FILE__, __LINE__, PEAR_LOG_DEBUG);
Horde::logMessage('SyncML: caught output=' .
$errorLogging, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $response;

View File

@ -1,16 +1,14 @@
<?php
require_once 'Horde/Util.php';
$GLOBALS['_HORDE_STRING_CHARSET'] = 'iso-8859-1';
/**
* The String:: class provides static methods for charset and locale safe
* 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
* 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 {
/**
* 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
* explicitely specified.
* explicitly specified.
*
* @param string $charset The charset to use as the default one.
*/
function setDefaultCharset($charset)
{
$GLOBALS['_HORDE_STRING_CHARSET'] = $charset;
if (Util::extensionExists('mbstring') &&
if (String::extensionExists('mbstring') &&
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
* of the extensions were available.
*
* @param mixed $input The data to be converted. If $input is an an
* array, the array's values get converted
* recursively.
* @param string $from The string's current charset.
* @param string $to The charset to convert the string to. If not
* specified, the global variable
* $_HORDE_STRING_CHARSET will be used.
* @param bool $recursion Internally used.
* @param mixed $input The data to be converted. If $input is an an array,
* the array's values get converted recursively.
* @param string $from The string's current charset.
* @param string $to The charset to convert the string to. If not
* specified, the global variable
* $_HORDE_STRING_CHARSET will be 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. */
if (is_null($to)) {
$to = $GLOBALS['_HORDE_STRING_CHARSET'];
}
/* If the from and to character sets are identical, return now. */
if (!$recursion) {
$from = String::lower($from);
$to = String::lower($to);
}
$from = String::lower($from);
$to = String::lower($to);
if ($from == $to) {
return $input;
}
if (is_array($input)) {
$tmp = array();
foreach ($input as $key => $val) {
$tmp[String::convertCharset($key, $from, $to, true)] = String::convertCharset($val, $from, $to, true);
while (list($key, $val) = each($input)) {
$tmp[String::_convertCharset($key, $from, $to)] = String::convertCharset($val, $from, $to);
}
return $tmp;
}
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);
foreach ($vars as $key => $val) {
$input->$key = String::convertCharset($val, $from, $to, true);
while (list($key, $val) = each($vars)) {
$input->$key = String::convertCharset($val, $from, $to);
}
return $input;
}
@ -90,43 +119,62 @@ class String {
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'));
if ($from_check && ($to == 'utf-8')) {
return utf8_encode($input);
}
$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. */
if ($from != 'utf7-imap' &&
$to != 'utf7-imap' &&
Util::extensionExists('iconv')) {
ini_set('track_errors', 1);
/* We need to tack an extra character temporarily because
* of a bug in iconv() if the last character is not a 7
* bit ASCII character. */
if (($from != 'utf7-imap') &&
($to != 'utf7-imap') &&
String::extensionExists('iconv')) {
/* We need to tack an extra character temporarily because of a bug
* in iconv() if the last character is not a 7 bit ASCII
* character. */
$oldTrackErrors = ini_set('track_errors', 1);
unset($php_errormsg);
$output = @iconv($from, $to . '//TRANSLIT', $input . 'x');
if (isset($php_errormsg)) {
$output = false;
} else {
$output = String::substr($output, 0, -1, $to);
}
ini_restore('track_errors');
$output = (isset($php_errormsg)) ? false : String::substr($output, 0, -1, $to);
ini_set('track_errors', $oldTrackErrors);
}
/* Next try mbstring. */
if (!$output && Util::extensionExists('mbstring')) {
$output = @mb_convert_encoding($input, $to, $from);
if (!$output && String::extensionExists('mbstring')) {
$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. */
if (!$output && Util::extensionExists('imap')) {
if (!$output && String::extensionExists('imap')) {
if ($from_check && ($to == 'utf7-imap')) {
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) {
/* The existence of mb_strtolower() depends on the platform. */
if (Util::extensionExists('mbstring') &&
if (String::extensionExists('mbstring') &&
function_exists('mb_strtolower')) {
if (is_null($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)) {
return $ret;
}
@ -173,7 +223,7 @@ class String {
}
if (!isset($lowers[$string])) {
$language = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'en_US');
setlocale(LC_CTYPE, 'C');
$lowers[$string] = strtolower($string);
setlocale(LC_CTYPE, $language);
}
@ -203,7 +253,9 @@ class String {
if (is_null($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)) {
return $ret;
}
@ -216,7 +268,7 @@ class String {
}
if (!isset($uppers[$string])) {
$language = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'en_US');
setlocale(LC_CTYPE, 'C');
$uppers[$string] = strtoupper($string);
setlocale(LC_CTYPE, $language);
}
@ -251,31 +303,34 @@ class String {
/**
* Returns part of a string.
*
* @param string $string The string to be converted.
* @param int $start The part's start position, zero based.
* @param int $length The part's length.
* @param string $charset The charset to use when calculating the part's
* position and length, defaults to current charset.
* @param string $string The string to be converted.
* @param integer $start The part's start position, zero based.
* @param integer $length The part's length.
* @param string $charset The charset to use when calculating the part's
* position and length, defaults to current
* charset.
*
* @return string The string's part.
*/
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)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET'];
}
if (is_null($length)) {
$length = String::length($string, $charset);
}
$ret = @mb_substr($string, $start, $length, $charset);
$old_error = error_reporting(0);
$ret = mb_substr($string, $start, $length, String::_mbstringCharset($charset));
error_reporting($old_error);
if (!empty($ret)) {
return $ret;
}
}
if (is_null($length)) {
$length = String::length($string);
}
return substr($string, $start, $length);
}
@ -290,11 +345,17 @@ class String {
*/
function length($string, $charset = null)
{
if (Util::extensionExists('mbstring')) {
if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET'];
}
$ret = @mb_strlen($string, $charset);
if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET'];
}
$charset = String::lower($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)) {
return $ret;
}
@ -308,22 +369,24 @@ class String {
*
* @param string $haystack The string to search through.
* @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.
* @param string $charset The charset to use when searching for the
* $needle string.
*
* @return int The position of first occurrence.
* @return integer The position of first occurrence.
*/
function pos($haystack, $needle, $offset = 0, $charset = null)
{
if (Util::extensionExists('mbstring')) {
if (String::extensionExists('mbstring')) {
if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET'];
}
ini_set('track_errors', 1);
$ret = @mb_strpos($haystack, $needle, $offset, $charset);
ini_restore('track_errors');
$track_errors = ini_set('track_errors', 1);
$old_error = error_reporting(0);
$ret = mb_strpos($haystack, $needle, $offset, String::_mbstringCharset($charset));
error_reporting($old_error);
ini_set('track_errors', $track_errors);
if (!isset($php_errormsg)) {
return $ret;
}
@ -337,7 +400,7 @@ class String {
* This method behaves exactly like str_pad but is multibyte safe.
*
* @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
* be in the same charset like the input string.
* @param const $type The padding type. One of STR_PAD_LEFT,
@ -388,22 +451,89 @@ class String {
/**
* 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.
* @param optional integer $length Wrap $text at this number of
* characters.
* @param optional string $break_char Character(s) to use when breaking
* lines.
* @param optional string $charset Character set to use when breaking
* lines.
* @param optional 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.
*/
function wordwrap($string, $width = 75, $break = "\n", $cut = false,
$charset = null, $line_folding = false)
{
/* Get the user's default character set if none passed in. */
if (is_null($charset)) {
$charset = $GLOBALS['_HORDE_STRING_CHARSET'];
}
$charset = String::_mbstringCharset($charset);
$string = String::convertCharset($string, $charset, 'utf-8');
$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.
*/
@ -422,7 +552,7 @@ class String {
if ($input != '-- ') {
$input = rtrim($input);
}
$line = wordwrap($input, $length, $break_char);
$line = String::wordwrap($input, $length, $break_char, false, $charset);
}
$paragraphs[] = $line;
@ -432,9 +562,8 @@ class String {
}
/**
* Returns true if the every character in the parameter is an
* alphabetic character. This method doesn't work with any charset
* other than the current charset yet.
* Returns true if the every character in the parameter is an alphabetic
* character.
*
* @param $string The string to test.
* @param $charset The charset to use when testing the string.
@ -443,26 +572,30 @@ class String {
*/
function isAlpha($string, $charset = null)
{
if (Util::extensionExists('mbstring')) {
$old_charset = mb_regex_encoding();
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;
if (!String::extensionExists('mbstring')) {
return ctype_alpha($string);
}
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
* letter in the current locale.
*
* @access public
* Returns true if ever character in the parameter is a lowercase letter in
* the current locale.
*
* @param $string The string to test.
* @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
* uppercase letter in the current locale.
*
* @access public
* Returns true if every character in the parameter is an uppercase letter
* in the current locale.
*
* @param string $string The string to test.
* @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
* 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.
* Performs a multibyte safe regex match search on the text provided.
*
* @access public
* @since Horde 3.1
*
* @param string $text The text to search.
* @param array $regex The regular expressions to use. These
* expressions should conform to ereg() rules -
* extended perl rules are NOT supported.
* Additionally, do NOT add perl regex delimiters
* (e.g. '/' or '|') to the beginning/end.
* @param array $regex The regular expressions to use, without perl
* regex delimiters (e.g. '/' or '|').
* @param string $charset The character set of the text.
*
* @return array The matches array from the first regex that matches.
*/
function regexMatch($text, $regex, $charset = null)
{
static $mbregex;
if (!isset($mbregex)) {
$mbregex = function_exists('mb_ereg');
}
$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;
if (!empty($charset)) {
$regex = String::convertCharset($regex, $charset, 'utf-8');
$text = String::convertCharset($text, $charset, 'utf-8');
}
$matches = array();
foreach ($regex as $val) {
if ($use_mb) {
if (mb_ereg($val, $text, $matches)) {
break;
}
} else {
if (preg_match('/' . $val . '/u', $text, $matches)) {
break;
}
if (preg_match('/' . $val . '/u', $text, $matches)) {
break;
}
}
if (isset($old_charset)) {
@mb_regex_encoding($old_charset);
if (!empty($charset)) {
$matches = String::convertCharset($matches, 'utf-8', $charset);
}
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
/**
* 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/Status.php';
include_once 'Horde/SyncML/Command/Alert.php';
include_once 'Horde/SyncML/Command/Final.php';
include_once 'Horde/SyncML/Command/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 {
/**
@ -144,41 +144,52 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
var $_credType;
var $_maxMsgSize;
function getStateFromSession($sourceURI, $locName, $sessionID)
{
// Remove any existing session since we'll be contructing a
// custom session id.
session_regenerate_id();
session_destroy();
// we need to (re-)load the eGW session-handler, as session_destroy unloads custom session-handlers
if (function_exists('init_session_handler'))
{
init_session_handler();
if (function_exists('init_session_handler')) {
init_session_handler();
}
// Reload the Horde SessionHandler if necessary.
// Reload the Horde SessionHandler if necessary.
Horde::setupSessionHandler();
// It would seem multisync does not send the user name once it
// has been authorized. Make sure we have a valid session id.
if(!empty($_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 {
#session_id('syncml' . preg_replace('/[^a-zA-Z0-9]/', '', $sourceURI . $sessionID));
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();
if (!isset($_SESSION['SyncML.state'])) {
// 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);
}
#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'];
}
@ -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);
$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->setMsgID($this->_msgID);
$state->setTargetURI($this->_targetURI);
$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->setLocName($this->_locName);
}
if (isset($this->_maxMsgSize)) {
$state->setMaxMsgSize($this->_maxMsgSize);
}
#$str = 'authorized=' . $state->isAuthorized();
#$str .= ' version=' . $state->getVersion();
#$str .= ' msgid=' . $state->getMsgID();
@ -246,11 +265,17 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
case 3:
if ($element == 'VerProto') {
// </VerProto></SyncHdr></SyncML>
if (trim($this->_chars) == 'SyncML/1.1') {
$this->_version = 1;
} else {
$this->_version = 0;
}
switch (strtolower(trim($this->_chars))) {
case 'syncml/1.2':
$this->_version = 2;
break;
case 'syncml/1.1':
$this->_version = 1;
break;
default:
$this->_version = 0;
break;
}
} elseif ($element == 'SessionID') {
// </SessionID></SyncHdr></SyncML>
$this->_sessionID = trim($this->_chars);
@ -271,8 +296,7 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
$tmp = explode(':', $this->_credData, 2);
// set only if not set by LocName already
if(!isset($this->_locName))
{
if(!isset($this->_locName)) {
$this->_locName = $tmp[0];
}
$this->_credData = $tmp[1];
@ -282,26 +306,34 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
break;
case 4:
if ($element == 'LocURI') {
if ($this->_isSource) {
// </LocURI></Source></SyncHdr></SyncML>
$this->_sourceURI = trim($this->_chars);
} else {
// </LocURI></Target></SyncHdr></SyncML>
$this->_targetURI = trim($this->_chars);
}
} elseif ($element == 'LocName') {
if ($this->_isSource) {
// </LocName></Source></SyncHdr></SyncML>
$this->_locName = trim($this->_chars);
}
} elseif ($element == 'Data') {
// </Data></Cred></SyncHdr></SyncML>
if ($this->_isCred) {
$this->_credData = trim($this->_chars);
}
}
break;
switch ($element) {
case 'LocURI':
if ($this->_isSource) {
// </LocURI></Source></SyncHdr></SyncML>
$this->_sourceURI = trim($this->_chars);
} else {
// </LocURI></Target></SyncHdr></SyncML>
$this->_targetURI = trim($this->_chars);
}
break;
case 'LocName':
if ($this->_isSource) {
// </LocName></Source></SyncHdr></SyncML>
$this->_locName = trim($this->_chars);
}
break;
case 'Data':
// </Data></Cred></SyncHdr></SyncML>
if ($this->_isCred) {
$this->_credData = trim($this->_chars);
}
break;
case 'MaxMsgSize':
//</MaxMsgSize></Meta></SyncHdr></SyncML>
$this->_maxMsgSize = trim($this->_chars);
break;
}
break;
case 5:
if ($this->_isCred) {
@ -323,19 +355,31 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
{
$attrs = array();
$state = $_SESSION['SyncML.state'];
$state = &$_SESSION['SyncML.state'];
$uri = $state->getURI();
$uriMeta = $state->getURIMeta();
$output->startElement($uri, 'SyncHdr', $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->endElement($uri, 'VerDTD');
$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->endElement($uri, 'VerProto');
@ -359,37 +403,40 @@ class Horde_SyncML_SyncMLHdr extends Horde_SyncML_ContentHandler {
$output->endElement($uri, 'LocURI');
$output->endElement($uri, 'Source');
if(session_id() != '' && !strpos($this->_targetURI,'syncml_sessionid')) {
$output->startElement($uri, 'RespURI', $attrs);
if (session_id() != '' && !strpos($this->_targetURI,'syncml_sessionid')) {
$output->startElement($uri, 'RespURI', $attrs);
// some clients don't send the whole URL as targetURI
if (strpos($this->_targetURI,$_SERVER['PHP_SELF']) === false) {
$output->characters($this->_targetURI . $_SERVER['PHP_SELF'] . '?syncml_sessionid=' . session_id());
} else {
$output->characters($this->_targetURI . '?syncml_sessionid=' . session_id());
// some clients don't send the whole URL as targetURI
if (strpos($this->_targetURI,$_SERVER['PHP_SELF']) === false) {
$output->characters($this->_targetURI . $_SERVER['PHP_SELF'] . '?syncml_sessionid=' . session_id());
} else {
$output->characters($this->_targetURI . '?syncml_sessionid=' . session_id());
}
$output->endElement($uri, 'RespURI');
}
$output->endElement($uri, 'RespURI');
}
/*
$output->startElement($uri, 'Meta', $attrs);
// Dummy Max MsqSize, this is just put in to make the packet
// work, it is not a real value.
if (!($maxMsgSize = $state->getMaxMsgSizeClient())) {
// 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);
$chars = '50000';
$output->characters($chars);
$output->characters($maxMsgSize);
$output->endElement($uriMeta, 'MaxMsgSize');
// Dummy MaxObjSize, this is just put in to make the packet
// work, it is not a real value.
$output->startElement($uriMeta, 'MaxObjSize', $attrs);
$chars = '4000000';
$output->characters($chars);
$output->endElement($uriMeta, 'MaxObjSize');
#// Dummy MaxObjSize, this is just put in to make the packet
#// work, it is not our real value.
#if ($this->_version > 0) {
# // Don't send this to old devices
# $output->startElement($uriMeta, 'MaxObjSize', $attrs);
# $output->characters('4000000');
# $output->endElement($uriMeta, 'MaxObjSize');
#}
$output->endElement($uri, 'Meta');
*/
$output->endElement($uri, 'SyncHdr');
}
@ -448,26 +495,27 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
function startElement($uri, $element, $attrs)
{
parent::startElement($uri, $element, $attrs);
$state = &$_SESSION['SyncML.state'];
switch ($this->_xmlStack) {
case 2:
$state = & $_SESSION['SyncML.state'];
$this->_actionCommands = false; // so far, we have not seen commands that require action from our side
$state->_sendFinal = false;
// <SyncML><SyncBody>
$this->_output->startElement($uri, $element, $attrs);
if($state->getLocName())
{
// Right our status about the header.
$status = new Horde_SyncML_Command_Status(($state->isAuthorized()) ?
RESPONSE_AUTHENTICATION_ACCEPTED : RESPONSE_INVALID_CREDENTIALS, 'SyncHdr');
}
else
{
if($state->getLocName()) {
if($state->isAuthConfirmed()) {
// Right our status about the header
$status = new Horde_SyncML_Command_Status(($state->isAuthorized()) ?
RESPONSE_OK : RESPONSE_INVALID_CREDENTIALS, 'SyncHdr');
} 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
$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->setTargetRef($state->getTargetURI());
$status->setCmdRef(0);
$state->clearNumberOfElements();
/*$str = 'authorized=' . $state->isAuthorized();
$str .= ' version=' . $state->getVersion();
@ -483,11 +532,12 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
$str .= ' target=' . $state->getTargetURI();
*/
$this->_currentCmdID = $status->output($this->_currentCmdID, $this->_output);
if ($state->isAuthorized()) {
$state->AuthConfirmed();
}
break;
case 3:
$state = & $_SESSION['SyncML.state'];
// <SyncML><SyncBody><[Command]>
#Horde::logMessage('SyncML['. session_id() ."]: found command $element ", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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
// packet.
$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)
@ -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) {
case 2:
// </SyncBody></SyncML>
$state = & $_SESSION['SyncML.state'];
Horde::logMessage('SyncML['. session_id() .']: package ----------------------- done', __FILE__, __LINE__, PEAR_LOG_DEBUG);
if($state->getSyncStatus() == CLIENT_SYNC_FINNISHED && $state->getAlert222Received() == true) {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
if ($state->getAlert222Received() == true) {
// the Funambol specialty
if ($state->getSyncStatus() == CLIENT_SYNC_FINNISHED) {
$state->setSyncStatus(CLIENT_SYNC_ACKNOWLEDGED);
}
$state->setAlert222Received(false);
}
// send the sync reply
// we do still have some data to send OR
// 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();
$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);
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) {
// this packet did not contain any real actions, just status and map.
// This means, we're through! The session can be closed and
// 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);
$state->writeSyncSummary();
$log = $state->getLog();
@ -560,6 +616,9 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
foreach($log as $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() .']: destroying sync session '.session_id(), __FILE__, __LINE__, PEAR_LOG_INFO);
# // 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 means, we're through! The session can be closed and
// 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);
$state->writeSyncSummary();
$log = $state->getLog();
@ -579,6 +637,9 @@ class Horde_SyncML_SyncMLBody extends Horde_SyncML_ContentHandler {
foreach($log as $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() .']: 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_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;
case 3:
// </[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);
switch($element) {
case 'Final':
if($state->getSyncStatus() == CLIENT_SYNC_STARTED) {
$state->setSyncStatus(CLIENT_SYNC_FINNISHED);
Horde::logMessage('SyncML['. session_id() .']: syncStatus(client sync finnished) ' . $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
case 'Final':
$this->_actionCommands = false;
$deviceInfo = $state->getClientDeviceInfo();
if($state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
$state->setSyncStatus(SERVER_SYNC_ACKNOWLEDGED);
Horde::logMessage('SyncML['. session_id() .']: syncStatus(server sync acknowledged) ' . $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
if ($state->getSyncStatus() == CLIENT_SYNC_STARTED) {
if (strtolower($deviceInfo['manufacturer']) == 'funambol'
&& 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;
#Horde::logMessage('SyncML['. session_id() .']: Sync _syncTag = '. $state->getSyncStatus(), __FILE__, __LINE__, PEAR_LOG_INFO);
if ($state->getSyncStatus() == SERVER_SYNC_FINNISHED) {
$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:
$this->_currentCmdID = $this->_currentCommand->output($this->_currentCmdID, $this->_output);
break;
}
default:
$this->_currentCmdID = $this->_currentCommand->output($this->_currentCmdID, $this->_output);
break;
}
unset($this->_currentCommand);
break;

View File

@ -1,80 +1,166 @@
<?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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* The SyncML_Command objects are hooked into the XML parser of the
* 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$
* @since Horde 3.0
* @package Horde_SyncML
*
* 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 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 {
/**
* 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 $_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';
$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++;
$this->_stack[] = $element;
}
/**
* 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)
{
switch ($this->_xmlStack) {
case 2:
if ($element == 'CmdID') {
$this->_cmdID = intval(trim($this->_chars));
}
break;
if (count($this->_stack) == 2 &&
$element == 'CmdID') {
$this->_cmdID = intval(trim($this->_chars));
}
if (isset($this->_chars)) {
unset($this->_chars);
if (strlen($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)
{
$tempValue = trim($str);
if(empty($tempValue)) return;
if (isset($this->_chars)) {
$this->_chars = $this->_chars . $str;
$this->_chars .= $str;
} else {
$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
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 Alert command as defined in SyncML Representation Protocol,
* 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
* 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
* @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_egw.php';
include_once 'Horde/SyncML/Command.php';
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 string $_sourceURI
* Source database of the Alert command.
*
* @var string
*/
var $_sourceLocURI;
/**
* @var string $_targetURI
* Target database of the Alert command.
*
* @var string
*/
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 integer $_metaAnchorLast
* The last time when synchronization happened, from the <Meta><Last>
* element.
*
* @var integer
*/
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.
@ -64,6 +95,8 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
function output($currentCmdID, &$output)
{
global $registry;
$attrs = array();
$state = &$_SESSION['SyncML.state'];
@ -76,323 +109,389 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
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
// this is then written to persistence for negotiation of
// further syncs.
$state->setClientAnchorNext($type, $this->_metaAnchorNext);
// Check if anchor sent from client matches our own stored
// data.
if ($clientlast == $this->_metaAnchorLast) {
// 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);
#Horde::logMessage("SyncML: Anchor match, TwoWaySync sinceee " . $clientlast, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (is_a($info, 'DataTreeObject')) {
$x = $info->get('ClientAnchor');
$clientlast = $x[$type];
$x = $info->get('ServerAnchor');
$state->setServerAnchorLast($type, $x[$type]);
} elseif (is_array($info)) {
$clientlast = $info['ClientAnchor'];
$state->setServerAnchorLast($type, $info['ServerAnchor']);
} else {
$clientlast = false;
$state->setServerAnchorLast($type, 0);
}
Horde::logMessage('SyncML: checking anchor targetLocURI: clientlast: ' . $clientlast .' / '. $this->_metaAnchorLast, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Set Server Anchor for this sync to current time.
$state->setServerAnchorNext($type,time());
if ($clientlast !== false && $clientlast == $this->_metaAnchorLast) {
// Last Sync Anchor matches, TwoWaySync will do.
$code = RESPONSE_OK;
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);
// Mismatch, enforce slow sync. 508=RESPONSE_REFRESH_REQUIRED 201=ALERT_SLOW_SYNC
$this->_alert = 201;
$code = 508;
// create new synctype
$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);
// 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);
}
// Determine sync type and status response code.
Horde::logMessage("SyncML: Alert " . $this->_alert, __FILE__, __LINE__, PEAR_LOG_DEBUG);
switch ($this->_alert) {
case ALERT_NEXT_MESSAGE:
$state->setAlert222Received(true);
case ALERT_RESULT_ALERT:
case ALERT_NO_END_OF_DATA:
// Nothing to do on our side
$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));
}
if ($this->_alert == ALERT_NEXT_MESSAGE) {
if ($this->_sourceLocURI != null) {
$status->setItemSourceLocURI($this->_sourceLocURI);
}
if ($this->_targetLocURI != null) {
$status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
}
}
$currentCmdID = $status->output($currentCmdID, $output);
return $currentCmdID;
case ALERT_TWO_WAY:
if ($anchormatch) {
$synctype = ALERT_TWO_WAY;
$response = RESPONSE_OK;
} else {
$synctype = ALERT_SLOW_SYNC;
$response = RESPONSE_REFRESH_REQUIRED;
}
break;
$status = new Horde_SyncML_Command_Status($code, '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));
}
case ALERT_SLOW_SYNC:
$synctype = ALERT_SLOW_SYNC;
$response = $anchormatch ? RESPONSE_OK : RESPONSE_REFRESH_REQUIRED;
break;
// Mirror Next Anchor from client back to client.
if (isset($this->_metaAnchorNext)) {
$status->setItemDataAnchorNext($this->_metaAnchorNext);
}
case ALERT_ONE_WAY_FROM_CLIENT:
if ($anchormatch) {
$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.
if (isset($this->_metaAnchorLast)) {
$status->setItemDataAnchorLast($this->_metaAnchorLast);
}
case ALERT_REFRESH_FROM_CLIENT:
$synctype = ALERT_REFRESH_FROM_CLIENT;
$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()) {
$output->startElement($state->getURI(), 'Alert', $attrs);
$hordeType = $state->getHordeType($this->_targetLocURI);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID;
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdID');
$state->setTargetURI($this->_targetLocURI);
$deletes = $state->getClientItems();
if (is_array($deletes)) {
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);
$chars = $this->_alert;
$output->characters($chars);
$output->endElement($state->getURI(), 'Data');
case ALERT_ONE_WAY_FROM_SERVER:
if ($anchormatch) {
$synctype = ALERT_ONE_WAY_FROM_SERVER;
$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) {
$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');
}
case ALERT_RESUME:
// @TODO: Suspend and Resume is not supported yet
$synctype = ALERT_SLOW_SYNC;
$response = RESPONSE_REFRESH_REQUIRED;
break;
$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);
$chars = $state->getServerAnchorLast($type);
$output->characters($chars);
$output->endElement($state->getURIMeta(), 'Last');
// Now set interval to retrieve server changes from, defined by
// ServerAnchor [Last,Next]
if ($synctype != ALERT_TWO_WAY &&
$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);
$chars = $state->getServerAnchorNext($type);
$output->characters($chars);
$output->endElement($state->getURIMeta(), 'Next');
$status = new Horde_SyncML_Command_Status($response, '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));
}
$output->endElement($state->getURIMeta(), 'Anchor');
$output->endElement($state->getURI(), 'Meta');
$output->endElement($state->getURI(), 'Item');
$output->endElement($state->getURI(), 'Alert');
// Mirror Next Anchor from client back to client.
if (isset($this->_metaAnchorNext)) {
$status->setItemDataAnchorNext($this->_metaAnchorNext);
}
// still needed? lars
$state->_sendFinal = true;
$currentCmdID++;
// Mirror Last Anchor from client back to client.
if (isset($this->_metaAnchorLast)) {
$status->setItemDataAnchorLast($this->_metaAnchorLast);
}
if($state->_devinfoRequested == false &&
$this->_sourceLocURI != null &&
is_a($state->getPreferedContentTypeClient($this->_sourceLocURI), 'PEAR_Error')) {
$output->startElement($state->getURI(), 'Get', $attrs);
$currentCmdID = $status->output($currentCmdID, $output);
$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);
$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;
}
}
$output->startElement($state->getURI(), 'Alert', $attrs);
} elseif ($this->_alert == ALERT_NEXT_MESSAGE) {
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Alert');
$status->setCmdRef($this->_cmdID);
if ($this->_targetLocURI != null) {
$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);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID;
$output->characters($chars);
$output->endElement($state->getURI(), 'CmdID');
$state->setAlert222Received(true);
$output->startElement($state->getURI(), 'Data', $attrs);
$chars = $synctype;
$output->characters($chars);
$output->endElement($state->getURI(), 'Data');
} else {
$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));
}
$output->startElement($state->getURI(), 'Item', $attrs);
$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;
}
/**
* 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;
}
function getTargetLocURI()
{
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;
switch (count($this->_stack)) {
case 2:
if ($element == 'Data') {
$this->_alert = intval(trim($this->_chars));
}
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;
}
}
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;
parent::endElement($uri, $element);
}
}

View File

@ -1,24 +1,31 @@
<?php
include_once 'Horde/SyncML/Command.php';
/**
* eGroupWare - SyncML based on Horde 3
*
* 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
* 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
* @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';
class Horde_SyncML_Command_Final extends Horde_SyncML_Command {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Final';
function output($currentCmdID, &$output)
{
$state = $_SESSION['SyncML.state'];

View File

@ -1,32 +1,37 @@
<?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/Command.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 {
function output($currentCmdID, &$output)
{
$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->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() , '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->startElement($state->getURIDevInf() , 'Man', $attrs);
$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->characters('server');
$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,
array('text/plain' => '1.0'));
$this->_writeDataStore('./contacts', 'text/x-vcard', '2.1', $output);
$this->_writeDataStore('./tasks', 'text/x-vcalendar', '1.0', $output);
$this->_writeDataStore('./calendar', 'text/x-vcalendar', '1.0', $output);
$this->_writeDataStore('./caltasks', 'text/x-vcalendar', '1.0', $output);
$this->_writeDataStore('./contacts', 'text/vcard', '3.0', $output,
array('text/x-vcard' => '2.1'));
$this->_writeDataStore('./tasks', 'text/calendar', '2.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->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 &$output contenthandler that will received the output.
* @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,
$additionaltypes = false)
@ -124,6 +145,9 @@ class Horde_SyncML_Command_Get extends Horde_SyncML_Command {
$output->startElement($state->getURIDevInf() , 'SourceRef', $attrs);
$output->characters($sourceref);
$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() , '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() , 'SyncType', $attrs);
$output->characters('1');
$output->endElement($state->getURIDevInf() , 'SyncType', $attrs);
$output->startElement($state->getURIDevInf() , 'SyncType', $attrs);
$output->characters('2');
$output->endElement($state->getURIDevInf() , 'SyncType', $attrs);
// We support all sync Types from 1-6: two way, slow, refresh|update
// from client|server
for ($i = 1; $i <= 6; ++$i) {
$output->startElement($state->getURIDevInf(), 'SyncType', $attrs);
$output->characters($i);
$output->endElement($state->getURIDevInf(), 'SyncType', $attrs);
}
$output->endElement($state->getURIDevInf() , 'SyncCap', $attrs);
$output->endElement($state->getURIDevInf() , 'DataStore', $attrs);
}

View File

@ -1,43 +1,60 @@
<?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 Map command as defined in SyncML Representation Protocol,
* 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>
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* 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 Karsten Fourmont <fourmont@gmx.de>
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
include_once 'Horde/SyncML/State.php';
include_once 'Horde/SyncML/Command.php';
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 string $_targetURI
* Target database of the Map command.
*
* @var string
*/
var $_targetLocURI;
/**
* Use in xml tag.
* Recipient map item specifier.
*
* @var string
*/
var $_isInSource;
var $_mapTarget;
/**
* Originator map item specifier.
*
* @var string
*/
var $_mapSource;
function output($currentCmdID, &$output)
@ -60,76 +77,25 @@ class Horde_SyncML_Command_Map extends Horde_SyncML_Command {
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)
{
parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) {
case 2:
if ($element == 'Target') {
$this->_isInSource = false;
}
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;
if (count($this->_stack) == 2 &&
$element == 'MapItem') {
unset($this->_mapTarget);
unset($this->_mapSource);
}
}
function endElement($uri, $element)
{
switch ($this->_xmlStack) {
case 1:
$state = $_SESSION['SyncML.state'];
$sync = $state->getSync($this->_targetLocURI);
if (!$sync) {
}
$_SESSION['SyncML.state'] = $state;
break;
$state = &$_SESSION['SyncML.state'];
switch (count($this->_stack)) {
case 2:
if ($element == 'MapItem') {
$state = $_SESSION['SyncML.state'];
$sync = $state->getSync($this->_targetLocURI);
if (!$state->isAuthorized()) {
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:
if ($element == 'LocURI') {
if ($this->_isInSource) {
if ($this->_stack[1] == 'Source') {
$this->_sourceLocURI = trim($this->_chars);
} else {
} elseif ($this->_stack[1] == 'Target') {
$targetLocURIData = explode('?/',trim($this->_chars));
$this->_targetLocURI = $targetLocURIData[0];
if(isset($targetLocURIData[1]))
{
$this->_targetLocURIParameters = $targetLocURIData[1];
}
$this->_targetLocURI = $targetLocURIData[0];
}
}
break;
case 4:
if ($element == 'LocURI') {
if ($this->_isInSource) {
if ($this->_stack[2] == 'Source') {
$this->_mapSource = trim($this->_chars);
} else {
} elseif ($this->_stack[2] == 'Target') {
$this->_mapTarget = trim($this->_chars);
}
}

View File

@ -1,23 +1,31 @@
<?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/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 {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Put';
/**
* @var string $_manufacturer
*/
@ -36,6 +44,12 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
var $_oem;
/**
* @var array $_deviceInfo
*/
var $_deviceInfo;
/**
* @var string $_softwareVersion
*/
@ -43,62 +57,63 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
var $_softwareVersion;
function endElement($uri, $element) {
switch ($this->_xmlStack) {
switch (count($this->_stack)) {
case 5:
switch($element) {
switch ($element) {
case 'DataStore':
$this->_deviceInfo['dataStore'][$this->_sourceReference] = array(
$this->_deviceInfo['dataStore'][$this->_sourceReference] = array (
'maxGUIDSize' => $this->_maxGUIDSize,
'rxPreference' => $this->_rxPreference,
'txPreference' => $this->_txPreference,
'syncCapabilities' => $this->_syncCapabilities,
'properties' => $this->_properties,
);
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;
@ -109,18 +124,18 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
case 'MaxGUIDSize':
$this->_maxGUIDSize = trim($this->_chars);
break;
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,
@ -129,7 +144,7 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
break;
}
break;
case 7:
switch($element) {
case 'CTType':
@ -178,87 +193,165 @@ class Horde_SyncML_Command_Put extends Horde_SyncML_Command {
}
}
break;
case 'SyncType':
$this->_syncCapabilities[] = trim($this->_chars);
break;
case 'VerCT':
$this->_contentVersion = trim($this->_chars);
break;
case 'Property':
if (isset($this->_PropName)) {
$this->_properties[$this->_contentType][$this->_contentVersion][$this->_PropName] = array(
'Size' => $this->_PropSize,
'NoTruncate' => $this->_PropNoTruncate,
);
}
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);
}
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 (preg_match("/^\s*Funambol (.*) [^\d]*(\d+\.?\d*)[\.|\d]*\s*$/i", $ua, $matches)) {
// Funambol uses the hardware Manufacturer we don't care about
$this->_deviceInfo['manufacturer'] = 'Funambol';
$this->_deviceInfo['model'] = trim($matches[1]);
$this->_deviceInfo['softwareVersion'] = floatval($matches[2]);
if (!isset($this->_deviceInfo['deviceType']))
{
switch (strtolower(trim($matches[1])))
{
case 'outlook plug-in':
default:
$this->_deviceInfo['deviceType'] = 'workstation';
break;
if (!isset($this->_deviceInfo['deviceType'])) {
switch (strtolower(trim($matches[1]))) {
case 'pocket pc plug-in':
$this->_deviceInfo['deviceType'] = 'windowsmobile';
break;
case 'outlook plug-in':
default:
$this->_deviceInfo['deviceType'] = 'workstation';
break;
}
}
}
$devid = $this->_deviceInfo['deviceID'];
switch (strtolower($devid))
{
switch (strtolower($this->_deviceInfo['deviceID'])) {
case 'fmz-thunderbird-plugin':
if (empty($this->_devinceInfo['manufacturer']))
if (empty($this->_devinceInfo['manufacturer'])) {
$this->_deviceInfo['manufacturer'] = 'Funambol';
if (empty($this->_devinceInfo['model']))
}
if (empty($this->_devinceInfo['model'])) {
$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;
}
}
function output($currentCmdID, &$output ) {
$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);
$ref = ($state->getVersion() == 0) ? './devinf10' : './devinf11';
if ($state->getVersion() == 2) {
$ref = './devinf12';
} elseif ($state->getVersion() == 1) {
$ref = './devinf11';
} else {
$ref = './devinf10';
}
$status->setSourceRef($ref);
if($state->isAuthorized()) {
$this->finalizeDeviceInfo();
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();
}
}
return $status->output($currentCmdID, $output);
}
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);
}

View File

@ -1,22 +1,29 @@
<?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';
/**
* $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 {
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Replace';
function output($currentCmdID, &$output)
{
return $currentCmdID;

View File

@ -1,291 +1,36 @@
<?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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* The Results command is used to return the results of a Search or Get
* 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$
* @since Horde 3.0
* @package Horde_SyncML
*
* 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$
*/
class Horde_SyncML_Command_Results extends Horde_SyncML_Command {
include_once 'Horde/SyncML/Command/Put.php';
var $_cmdRef;
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;
class Horde_SyncML_Command_Results extends Horde_SyncML_Command_Put {
case 6:
switch($element) {
case 'MaxGUIDSize':
$this->_maxGUIDSize = trim($this->_chars);
break;
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);
}
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Results';
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
/**
* 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/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 {
/**
* 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;
/**
* The command ID (CmdID) of the command sent to the client, that this
* Status response refers to.
*
* @var integer
*/
var $_cmdRef;
/**
* Must be present.
* The command (Add, Replace, etc) sent to the client, that this Status
* response refers to.
*
* @var string
*/
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;
/**
* 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 $_chalMetaFormat;
@ -48,6 +87,15 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
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)
{
if ($response != null) {
@ -61,12 +109,11 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
function output($currentCmdID, &$output)
{
$state = &$_SESSION['SyncML.state'];
$attrs = array();
$state = $_SESSION['SyncML.state'];
if ($this->_cmd != null) {
$attrs = array();
$output->startElement($state->getURI(), 'Status', $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');
}
if (isset($this->_itemTargetLocURI) && isset($this->_itemSourceLocURI)) {
$output->startElement($state->getURI(), 'Item', $attrs);
if (isset($this->_syncItems)) {
// 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);
$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(), 'Item');
}
if (isset($this->_itemTargetLocURI)) {
$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');
}
if (isset($this->_itemSourceLocURI)) {
$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(), 'Item');
}
$output->endElement($state->getURI(), 'Status');
$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;
@ -323,4 +350,14 @@ class Horde_SyncML_Command_Status extends Horde_SyncML_Command {
{
$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
/**
* 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/Command.php';
include_once 'Horde/SyncML/Command/Sync/SyncElement.php';
include_once 'Horde/SyncML/Sync/TwoWaySync.php';
include_once 'Horde/SyncML/Sync/SlowSync.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/RefreshFromClientSync.php';
/**
* $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 {
class Horde_SyncML_Command_Sync extends Horde_SyncML_Command {
var $_isInSource;
var $_currentSyncElement;
var $_syncElements = array();
function output($currentCmdID, &$output) {
$state = &$_SESSION['SyncML.state'];
$attrs = array();
Horde::logMessage('SyncML: $this->_targetURI = ' . $this->_targetURI, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$status = new Horde_SyncML_Command_Status(RESPONSE_OK, 'Sync');
// $status->setState($state);
$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);
}
}
/**
* Name of the command.
*
* @var string
*/
var $_cmdName = 'Sync';
/**
* Source database of the <Sync> command.
*
* @var string
*/
var $_sourceURI;
/**
* Target database of the <Sync> command.
*
* @var string
*/
var $_targetURI;
/**
* Optional parameter for the Target.
*
* @var string
*/
var $_targetURIParameters;
/**
* SyncML_SyncElement object for the currently parsed sync command.
*
* @var SyncML_SyncElement
*/
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)
{
parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) {
switch (count($this->_stack)) {
case 2:
if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') {
$this->_currentSyncElement = &Horde_SyncML_Command_Sync_SyncElement::factory($element);
// $this->_currentSyncElement->setVersion($this->_version);
// $this->_currentSyncElement->setCmdRef($this->_cmdID);
// $this->_currentSyncElement->setMsgID($this->_msgID);
} elseif ($element == 'Target') {
$this->_isInSource = false;
} else {
$this->_isInSource = true;
if ($element == 'Replace' ||
$element == 'Add' ||
$element == 'Delete') {
Horde::logMessage("SyncML: sync element $element found", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->_curItem = &Horde_SyncML_Command_Sync_SyncElement::factory($element);
}
break;
}
if (isset($this->_currentSyncElement)) {
$this->_currentSyncElement->startElement($uri, $element, $attrs);
if (isset($this->_curItem)) {
$this->_curItem->startElement($uri, $element, $attrs);
}
}
// We create a seperate Sync Element for the Sync Data sent
// from the Server to the client as we want to process the
// client sync information before.
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'];
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();
$state = &$_SESSION['SyncML.state'];
if($state->getSyncStatus() >= CLIENT_SYNC_FINNISHED && $state->getSyncStatus() < SERVER_SYNC_FINNISHED)
{
$deviceInfo = $state->getClientDeviceInfo();
$targets = $state->getTargets();
foreach($targets as $target)
{
$sync = $state->getSync($target);
// make sure that the state reflects what is currently being done
$state->_currentSourceURI = $sync->_sourceLocURI;
$state->_currentTargetURI = $sync->_targetLocURI;
$output->startElement($state->getURI(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $sync->_sourceLocURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
#$chars = $sync->_targetLocURI;
$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)
{
$numberOfItems = $sync->loadData();
if($deviceInfo['supportNumberOfChanges'])
$sync = &$state->getSync($target);
Horde::logMessage('SyncML['. session_id() .']: sync alerttype '. $sync->_syncType .' found for target ' . $target, __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($sync->_syncType == ALERT_ONE_WAY_FROM_CLIENT ||
$sync->_syncType == ALERT_REFRESH_FROM_CLIENT) {
Horde::logMessage('SyncML['. session_id() .']: From client Sync, no sync of '. $target .' to client', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->clearSync($target);
} else if ($state->getSyncStatus() >= CLIENT_SYNC_ACKNOWLEDGED) {
Horde::logMessage("SyncML: starting sync to client $target", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$attrs = array();
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
$output->startElement($state->getURI(), 'Sync', $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$output->characters($currentCmdID);
$currentCmdID++;
$output->endElement($state->getURI(), 'CmdID');
$output->startElement($state->getURI(), 'Target', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$chars = $sync->_sourceLocURI;
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
$output->startElement($state->getURI(), 'Source', $attrs);
$output->startElement($state->getURI(), 'LocURI', $attrs);
$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);
$output->characters($numberOfItems);
$output->endElement($state->getURI(), 'NumberOfChanged');
$numberOfItems = $sync->loadData();
if($deviceInfo['supportNumberOfChanges'])
{
$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
if($state->getTargets() === FALSE)
if($state->getTargets() === FALSE &&
!isset($state->curSyncItem)) {
$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;
}
function endElement($uri, $element)
{
if (isset($this->_currentSyncElement)) {
$this->_currentSyncElement->endElement($uri, $element);
if (isset($this->_curItem)) {
$this->_curItem->endElement($uri, $element);
}
switch ($this->_xmlStack) {
switch (count($this->_stack)) {
case 2:
if ($element == 'Replace' || $element == 'Add' || $element == 'Delete') {
$this->_syncElements[] = $this->_currentSyncElement;
unset($this->_currentSyncElement);
if ($element == 'Replace' ||
$element == 'Add' ||
$element == 'Delete') {
$this->_syncElements[] = &$this->_curItem;
unset($this->_curItem);
}
break;
case 3:
$state = & $_SESSION['SyncML.state'];
if ($element == 'LocURI' && !isset($this->_currentSyncElement)) {
if ($this->_isInSource) {
if ($element == 'LocURI' && !isset($this->_curItem)) {
if ($this->_stack[1] == 'Source') {
$this->_sourceURI = trim($this->_chars);
$state->_currentSourceURI = $this->_sourceURI;
} else {
$this->_targetURI = trim($this->_chars);
} elseif ($this->_stack[1] == 'Target') {
$targetURIData = explode('?/',trim($this->_chars));
$this->_targetURI = $targetURIData[0];
$state->_currentTargetURI = $this->_targetURI;
if(isset($targetURIData[1]))
{
$this->_targetURIParameters = $targetURIData[1];
$state->_currentTargetURIParameters = $this->_targetURIParameters;
}
$this->_targetURI = $targetURIData[0];
if (isset($targetURIData[1])) {
$this->_targetURIParameters = $targetURIData[1];
}
}
}
break;
}
parent::endElement($uri, $element);
}
function characters($str)
{
if (isset($this->_currentSyncElement)) {
$this->_currentSyncElement->characters($str);
if (isset($this->_curItem)) {
$this->_curItem->characters($str);
} else {
if (isset($this->_chars)) {
$this->_chars = $this->_chars . $str;
$this->_chars .= $str;
} else {
$this->_chars = $str;
}

View File

@ -1,20 +1,20 @@
<?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';
/**
* $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 {
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->setCmdRef($this->_cmdID);
if (isset($this->_luid)) {
$status->setSourceRef($this->_luid);
if (!empty($this->_items)) {
$status->setSyncItems($this->_items);
}
return $status->output($currentCmdID, $output);
}

View File

@ -1,106 +1,44 @@
<?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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* Using the PEAR Log class (which need to be installed!)
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @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 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';
/**
* 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);
}
class Horde_SyncML_Command_Sync_ContentSyncElement extends Horde_SyncML_Command_Sync_SyncElementItem {
function outputCommand($currentCmdID, &$output, $command)
{
$state = $_SESSION['SyncML.state'];
$maxMsgSize = $state->getMaxMsgSizeClient();
$maxGUIDSize = $state->getMaxGUIDSizeClient();
if ($this->_moreData) {
$command = $this->_command;
} else {
$this->_command = $command;
}
$attrs = array();
$output->startElement($state->getURI(), $command, $attrs);
$output->startElement($state->getURI(), 'CmdID', $attrs);
$chars = $currentCmdID;
$output->characters($chars);
$output->characters($currentCmdID);
$output->endElement($state->getURI(), 'CmdID');
/*
if (isset($this->_contentType)) {
$output->startElement($state->getURI(), 'Meta', $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->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)
|| isset($this->_locURI) || isset($this->targetURI)) {
// <command><Meta>
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);
// 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(), 'LocURI', $attrs);
$chars = substr($this->_locURI,0,39);
$state->setUIDMapping($this->_locURI, $chars);
$chars = substr($this->_guid, 0, $maxGUIDSize);
$state->setUIDMapping($this->_guid, $chars);
$output->characters($chars);
$output->endElement($state->getURI(), 'LocURI');
$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(), 'LocURI', $attrs);
$chars = $this->_targetURI;
$output->characters($chars);
$output->characters($this->_luid);
$output->endElement($state->getURI(), 'LocURI');
$output->endElement($state->getURI(), 'Target');
}
// <command><Item><Data>
if (isset($this->_content)) {
$output->startElement($state->getURI(), 'Data', $attrs);
#$chars = '<![CDATA['.$this->_content.']]>';
$chars = $this->_content;
$output->characters($chars);
$currentSize = $output->getOutputSize();
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');
// <command><Item><MoreData/>
if ($this->_moreData) {
$output->startElement($state->getURI(), 'MoreData', $attrs);
$output->endElement($state->getURI(), 'MoreData');
}
}
$output->endElement($state->getURI(), 'Item');
}

View File

@ -1,20 +1,20 @@
<?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';
/**
* $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 {
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->setCmdRef($this->_cmdID);
if (isset($this->_luid)) {
$status->setSourceRef($this->_luid);
if (!empty($this->_items)) {
$status->setSyncItems($this->_items);
}
return $status->output($currentCmdID, $output);

View File

@ -1,32 +1,30 @@
<?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';
/**
* $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 {
function output($currentCmdID, &$output) {
$status = new Horde_SyncML_Command_Status($this->_status, 'Replace');
$status->setCmdRef($this->_cmdID);
if (isset($this->_luid)) {
$status->setSourceRef($this->_luid);
}
#$status->setItemSourceLocURI($this->_sourceLocURI);
#$status->setItemTargetLocURI(isset($this->_targetLocURIParameters) ? $this->_targetLocURI.'?/'.$this->_targetLocURIParameters : $this->_targetLocURI);
if (!empty($this->_items)) {
$status->setSyncItems($this->_items);
}
return $status->output($currentCmdID, $output);
}

View File

@ -1,37 +1,40 @@
<?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';
/**
* $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 {
var $_luid;
var $_guid;
var $_isSource;
var $_content;
var $_contentSize;
var $_contentType;
var $_contentFormat;
var $_status = RESPONSE_OK;
var $_items;
var $_curItem;
var $_items = array();
var $_moreData = false;
var $_command = false;
function &factory($command, $params = null) {
include_once 'Horde/SyncML/Command/Sync/SyncElementItem.php';
@include_once 'Horde/SyncML/Command/Sync/' . $command . '.php';
$class = 'Horde_SyncML_Command_Sync_' . $command;
if (class_exists($class)) {
#Horde::logMessage('SyncML: Class definition of ' . $class . ' found in SyncElement::factory.', __FILE__, __LINE__, PEAR_LOG_DEBUG);
return $element = new $class($params);
@ -44,78 +47,122 @@ class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command {
function startElement($uri, $element, $attrs) {
parent::startElement($uri, $element, $attrs);
switch ($this->_xmlStack) {
case 3:
if ($element == 'Source') {
$this->_isSource = true;
$state = &$_SESSION['SyncML.state'];
switch (count($this->_stack)) {
case 1:
$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;
}
}
function endElement($uri, $element) {
$state = &$_SESSION['SyncML.state'];
$search = array('/ *\n/','/ *$/m');
$replace = array('','');
switch ($this->_xmlStack) {
switch (count($this->_stack)) {
case 1:
$this->_command = false;
// Need to add sync elements to the Sync method?
#error_log('total # of items: '.count($this->_items));
#error_log(print_r($this->_items[10], true));
break;
case 2;
if($element == 'Item') {
$item = new Horde_SyncML_Command_Sync_SyncElementItem();
if($this->_luid) {
$item->setLocURI($this->_luid);
$item->setContent($this->_content);
$item->setContentType($this->_contentType);
$this->_curItem->setLocURI($this->_luid);
$this->_curItem->setContentType($this->_contentType);
$this->_curItem->setContentFormat($this->_contentFormat);
$this->_curItem->setCommand($this->_command);
if($this->_contentSize)
$item->setContentType($this->_contentSize);
if($this->_moreData)
$item->setMoreData($this->_moreData);
$this->_items[$this->_luid] = $item;
$this->_curItem->setContentSize($this->_contentSize);
if($this->_moreData) {
$state->curSyncItem = &$this->_curItem;
Horde::logMessage('SyncML: moreData item saved for LocURI ' . $this->_curItem->_luid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} 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->_luid);
}
break;
case 3:
if ($element == 'Source') {
$this->_isSource = false;
} elseif ($element == 'Data') {
$this->_content = $this->_chars;
} elseif ($element == 'MoreData') {
$this->_moreData = TRUE;
} elseif ($element == 'Type') {
if(empty($this->_contentType))
$this->_contentType = trim($this->_chars);
switch ($element) {
case 'Data':
$this->_curItem->_content .= $this->_chars;
break;
case 'MoreData':
$this->_moreData = true;
break;
case 'Type':
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;
case 4:
if ($element == 'LocURI' && $this->_isSource) {
$this->_luid = trim($this->_chars);
} elseif ($element == 'Type') {
$this->_contentType = trim($this->_chars);
} elseif ($element == 'Size') {
$this->_contentSize = trim($this->_chars);
switch ($element) {
case 'LocURI':
if ($this->_stack[2] == 'Source') {
$this->_luid = 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;
}
parent::endElement($uri, $element);
}
function getSyncElementItems() {
return (array)$this->_items;
}
function getSyncElementItems() {
return (array)$this->_items;
}
function getLocURI()
{
@ -149,14 +196,17 @@ class Horde_SyncML_Command_Sync_SyncElement extends Horde_SyncML_Command {
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)
{
$this->_status = $_status;

View File

@ -1,67 +1,95 @@
<?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';
/**
* $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 {
var $_luid;
var $_guid;
var $_content;
var $_content = '';
var $_contentSize;
var $_contentType;
var $_contentFormat;
var $_command;
var $_moreData = false;
function getLocURI() {
return $this->_luid;
}
function getGUID() {
return $this->_guid;
}
function getContentType() {
return $this->_contentType;
}
function getContentFormat() {
return $this->_contentFormat;
}
function getContent() {
return $this->_content;
}
function getContentSize() {
if (isset($this->_contentSize)) {
return $this->_contentSize;
}
return false;
}
function getCommand() {
return $this->_command;
}
function setLocURI($luid) {
$this->_luid = $luid;
}
function setGUID($guid) {
$this->_guid = $guid;
}
function setContent($content) {
$this->_content = $content;
function setContent($_content) {
$this->_content = $_content;
}
function setContentSize($_size) {
$this->_contentSize = $_size;
}
function setContentType($_contentType) {
$this->_contentType = $_contentType;
function setContentType($_type) {
$this->_contentType = $_type;
}
function setContentFormat($_format) {
$this->_contentFormat = $_format;
}
function setMoreData($_status) {
$this->_moreData = $_status;
}
function hasMoreData() {
return $this->_moreData;
}
function setCommand($_command) {
$this->_command = $_command;
}
}

View File

@ -1,4 +1,18 @@
<?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);
@ -21,6 +35,10 @@ define('ALERT_RESULT_ALERT', 221);
define('ALERT_NEXT_MESSAGE', 222);
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_WBXML', 'application/vnd.syncml+wbxml');
@ -107,7 +125,7 @@ define('RESPONSE_COMMAND_FAILED', 500);
// define('RESPONSE_COMMAND_FAILED', 505);
// define('RESPONSE_COMMAND_FAILED', 506);
// define('RESPONSE_COMMAND_FAILED', 507);
// define('RESPONSE_COMMAND_FAILED', 508);
define('RESPONSE_REFRESH_REQUIRED', 508);
// define('RESPONSE_COMMAND_FAILED', 509);
// define('RESPONSE_COMMAND_FAILED', 510);
// define('RESPONSE_COMMAND_FAILED', 511);
@ -117,12 +135,15 @@ define('RESPONSE_COMMAND_FAILED', 500);
// define('RESPONSE_COMMAND_FAILED', 515);
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_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_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_2', 'syncml:devinf1.2');
define('CLIENT_SYNC_STARTED', 1);
define('CLIENT_SYNC_FINNISHED', 2);
@ -131,8 +152,18 @@ define('SERVER_SYNC_DATA_PENDING', 4);
define('SERVER_SYNC_FINNISHED', 5);
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_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.
@ -148,6 +179,7 @@ define('MAX_ENTRIES', 10);
* @version $Revision$
* @since Horde 3.0
* @package Horde_SyncML
* @modified Joerg Lehrke <jlehrke@noc.de> 2009/01/20, support all syn types
*/
class Horde_SyncML_State {
@ -157,6 +189,10 @@ class Horde_SyncML_State {
var $_msgID;
var $_maxMsgSize;
var $_maxGUIDSize;
var $_targetURI;
var $_sourceURI;
@ -169,6 +205,8 @@ class Horde_SyncML_State {
var $_isAuthorized;
var $_AuthConfirmed;
var $_uri;
var $_uriMeta;
@ -181,7 +219,7 @@ class Horde_SyncML_State {
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
var $_changedItems;
@ -192,8 +230,11 @@ class Horde_SyncML_State {
// array list of added items, which need to be synced to the client
var $_addedItems;
// bool flag that we need to more data
var $_syncStatus;
// array list of items, which need to be refreshed at the client
var $_conflictItems;
// current session status
var $_syncStatus = 0;
var $_log = array();
@ -208,6 +249,22 @@ class Horde_SyncML_State {
*/
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.
*/
@ -220,7 +277,8 @@ class Horde_SyncML_State {
$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
*/
function getUIDMapping($_sentEgwUid) {
if(isset($this->_uidMappings[$_sentEgwUid])) {
if(strlen("$_sentEgwUid") && isset($this->_uidMappings[$_sentEgwUid])) {
return $this->_uidMappings[$_sentEgwUid];
}
@ -311,6 +369,16 @@ class Horde_SyncML_State {
return false;
}
function &getConflictItems($_type)
{
if(isset($this->_conflictItems[$_type]))
{
return $this->_conflictItems[$_type];
}
return false;
}
function getMoreDataPending()
{
return $this->_moreDataPending;
@ -321,6 +389,14 @@ class Horde_SyncML_State {
return $this->_msgID;
}
function getMaxMsgSizeClient()
{
if (isset($this->_maxMsgSize)) {
return $this->_maxMsgSize;
}
return false;
}
function setWBXML($wbxml)
{
$this->_wbxml = $wbxml;
@ -341,6 +417,11 @@ class Horde_SyncML_State {
$this->_addedItems[$_type] = $_addedItems;
}
function pushAddedItem($_type, $_addedItems)
{
$this->_addedItems[$_type] = $_addedItems;
}
function setChangedItems($_type, $_changedItems)
{
$this->_changedItems[$_type] = $_changedItems;
@ -356,9 +437,14 @@ class Horde_SyncML_State {
$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;
}
/**
* Setter for property maxMsgSize.
* @param size New value of property maxMsgSize.
*/
function setMaxMsgSize($size)
{
$this->_maxMsgSize = $size;
}
/**
* Setter for property locName.
* @param locName New value of property locName.
@ -408,15 +503,20 @@ class Horde_SyncML_State {
{
$this->_version = $version;
if ($version == 0) {
$this->_uri = NAME_SPACE_URI_SYNCML;
$this->_uriMeta = NAME_SPACE_URI_METINF;
$this->_uriDevInf = NAME_SPACE_URI_DEVINF;
} else {
if ($version == 2) {
$this->_uri = NAME_SPACE_URI_SYNCML_1_2;
$this->_uriMeta = NAME_SPACE_URI_METINF_1_2;
$this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_2;
} elseif ($version == 1) {
$this->_uri = NAME_SPACE_URI_SYNCML_1_1;
$this->_uriMeta = NAME_SPACE_URI_METINF_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)
@ -457,6 +557,16 @@ class Horde_SyncML_State {
return $this->_isAuthorized;
}
function isAuthConfirmed()
{
return $this->_AuthConfirmed;
}
function AuthConfirmed()
{
$this->_AuthConfirmed = true;
}
function clearSync($target)
{
unset($this->_syncs[$target]);
@ -486,6 +596,9 @@ class Horde_SyncML_State {
$targets[] = $target;
}
// Make sure we keep the order
sort($targets);
return $targets;
}
@ -502,6 +615,7 @@ class Horde_SyncML_State {
return '';
}
}
function getURIMeta()
{
return $this->_uriMeta;
@ -548,8 +662,9 @@ class Horde_SyncML_State {
* this->_locName . $this->_sourceURI . $type . $locid so you can
* have different syncs with different devices. If an entry
* 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();
@ -558,6 +673,7 @@ class Horde_SyncML_State {
$gid->set('type', $type);
$gid->set('locid', $locid);
$gid->set('ts', $ts);
$gid->set('expired', $expired);
$r = $dt->add($gid);
if (is_a($r, 'PEAR_Error')) {
@ -657,54 +773,41 @@ class Horde_SyncML_State {
*/
function adjustContentType($type, $target = null)
{
$ctype;
if (is_array($type))
{
if (is_array($type)) {
$ctype = $type['ContentType'];
$res = $type;
}
else
{
} else {
$ctype = $type;
$res = array();
$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']))
{
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))
{
// the funambol specific types need to be encoded in base64
switch (strtolower($ctype)) {
case 'text/x-s4j-sifc':
case 'text/x-s4j-sife':
case 'text/x-s4j-sift':
@ -712,17 +815,15 @@ class Horde_SyncML_State {
$res['ContentFormat'] = 'b64';
break;
}
return $res;
}
function getPreferedContentType($type)
{
$_type = str_replace('./','',$type);
switch(strtolower($_type))
{
switch (strtolower($_type)) {
case 'contacts':
return 'text/x-vcard';
return 'text/vcard';
break;
case 'notes':
@ -730,9 +831,10 @@ class Horde_SyncML_State {
break;
case 'calendar':
case 'events':
case 'tasks':
case 'caltasks':
return 'text/x-vcalendar';
return 'text/calendar';
break;
case 'sifcalendar':
@ -778,6 +880,7 @@ class Horde_SyncML_State {
return 'tasks';
break;
case 'events':
case 'calendar':
return 'calendar';
break;
@ -815,7 +918,6 @@ class Horde_SyncML_State {
}
}
/**
/**
* Returns the preferred contenttype of the client for the given
* sync data type (database).
@ -826,12 +928,33 @@ class Horde_SyncML_State {
function getPreferedContentTypeClient($_sourceLocURI, $_targetLocURI = null) {
$deviceInfo = $this->getClientDeviceInfo();
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']))
{
return $this->adjustContentType($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType'], $_targetLocURI);
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['maxGUIDSize']['contentType'])) {
$this->_maxGUIDSize = $deviceInfo['dataStore'][$this->_sourceURI]['maxGUIDSize']['contentType'];
}
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)
{
@ -841,6 +964,19 @@ class Horde_SyncML_State {
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)
{
$this->_clientAnchorNext[$type] = $a;
@ -900,6 +1036,11 @@ class Horde_SyncML_State {
*/
function getClientDeviceInfo()
{
if (isset($this->_clientDeviceInfo) && is_array($this->_clientDeviceInfo)) {
// use cached information
return $this->_clientDeviceInfo;
}
$dt = &$this->getDataTree();
$id = $dt->getId($this->_locName . $this->_sourceURI . 'deviceInfo');
@ -907,7 +1048,7 @@ class Horde_SyncML_State {
return false;
}
$info = $dt->getObjectById($id);
$info = $dt->getObjectById($id);
return $info->get('ClientDeviceInfo');
}
@ -1070,4 +1211,23 @@ class Horde_SyncML_State {
$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
/**
* eGroupWare SyncML
* eGroupWare - SyncML based on Horde 3
*
*
* Using the PEAR Log class (which need to be installed!)
*
* @link http://www.egroupware.org
* @author Lars Kneschke
* @package syncml
* @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$
*/
include_once dirname(__FILE__).'/State.php';
/**
@ -17,45 +21,42 @@ include_once dirname(__FILE__).'/State.php';
class EGW_SyncML_State extends Horde_SyncML_State
{
var $table_devinfo = 'egw_syncmldevinfo';
/*
* store the mappings of egw uids to client uids
*/
var $uidMappings = array();
/**
* get the local content id from a syncid
*
* @param sting $_syncid id used in syncml
* @return int local egw content id
*/
function get_egwID($_syncid)
{
/**
* get the local content id from a syncid
*
* @param sting $_syncid id used in syncml
* @return int local egw content id
*/
function get_egwID($_syncid) {
$syncIDParts = explode('-',$_syncid);
array_shift($syncIDParts);
$_id = implode ('', $syncIDParts);
return $_id;
}
/**
* when got a entry last added/modified/deleted
*
* @param $_syncid containing appName-contentid
* @param $_action string can be add, delete or modify
* @return string the last timestamp
*/
function getSyncTSforAction($_syncid, $_action)
{
}
/**
* when got a entry last added/modified/deleted
*
* @param $_syncid containing appName-contentid
* @param $_action string can be add, delete or modify
* @return string the last timestamp
*/
function getSyncTSforAction($_syncid, $_action) {
$syncIDParts = explode('-',$_syncid);
$_appName = array_shift($syncIDParts);
$_id = implode ('', $syncIDParts);
$ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action);
return $ts;
}
}
/**
* 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 $_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
*/
function getHistory($_appName, $_action, $_ts)
{
function getHistory($_appName, $_action, $_ts) {
$guidList = array ();
$syncIdList = array ();
$idList = $GLOBALS['egw']->contenthistory->getHistory($_appName, $_action, $_ts);
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
* 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()
{
if(($deviceID = $GLOBALS['egw']->db->select('egw_syncmldeviceowner', 'owner_devid',array (
'owner_locname' => $this->_locName,
'owner_deviceid' => $this->_sourceURI,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn()))
* 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;
}
/**
* 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(
'dev_dtdversion',
'dev_numberofchanges',
@ -129,156 +167,156 @@ class EGW_SyncML_State extends Horde_SyncML_State
'dev_utc',
);
#Horde::logMessage("SyncML: getClientDeviceInfo $deviceID", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$where = array(
'dev_id' => $deviceID,
'dev_id' => $deviceID,
);
if (($row = $GLOBALS['egw']->db->select('egw_syncmldevinfo', $cols, $where, __LINE__, __FILE__, false, '', 'syncml')->fetch()))
{
return array (
'DTDVersion' => $row['dev_dtdversion'],
'supportNumberOfChanges'=> $row['dev_numberofchanges'],
'supportLargeObjs' => $row['dev_largeobjs'],
'UTC' => $row['dev_utc'],
'softwareVersion' => $row['dev_swversion'],
'hardwareVersion' => $row['dev_hwversion'],
'firmwareVersion' => $row['dev_fwversion'],
'oem' => $row['dev_oem'],
'model' => $row['dev_model'],
'manufacturer' => $row['dev_manufacturer'],
'deviceType' => $row['dev_devicetype'],
'dataStore' => unserialize($row['dev_datastore']),
if (($row = $GLOBALS['egw']->db->select('egw_syncmldevinfo',
$cols, $where, __LINE__, __FILE__, false, '', 'syncml')->fetch())) {
$deviceMaxEntries = 'maxEntries-' . $this->_sourceURI;
$deviceUIDExtension = 'uidExtension-' . $this->_sourceURI;
$deviceNonBlockingAllday = 'nonBlockingAllday-' . $this->_sourceURI;
$syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
$this->_clientDeviceInfo = array (
'DTDVersion' => $row['dev_dtdversion'],
'supportNumberOfChanges' => $row['dev_numberofchanges'],
'supportLargeObjs' => $row['dev_largeobjs'],
'UTC' => $row['dev_utc'],
'softwareVersion' => $row['dev_swversion'],
'hardwareVersion' => $row['dev_hwversion'],
'firmwareVersion' => $row['dev_fwversion'],
'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;
}
/**
* returns GUIDs of all client items
*/
function _getClientItems($type)
{
$mapID = $this->_locName . $this->_sourceURI . $type;
* returns GUIDs of all client items
*/
function getClientItems() {
$mapID = $this->_locName . $this->_sourceURI . $this->_targetURI;
$guids = array();
foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array(
'map_id' => $mapID,
'map_expired' => 0,
), __LINE__, __FILE__, false, '', 'syncml') as $row)
{
$guids[] = $row['map_guid'];
}
return $guids ? $guids : false;
$guids = array();
foreach($GLOBALS['egw']->db->select('egw_contentmap', 'map_guid', array(
'map_id' => $mapID,
'map_expired' => false,
), __LINE__, __FILE__, false, '', 'syncml') as $row) {
$guids[] = $row['map_guid'];
}
return $guids ? $guids : false;
}
/**
* Retrieves the Horde server guid (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) for a given client
* locid. Returns false if no such id is stored yet.
*
* Opposite of getLocId which returns the locid for a given guid.
*/
function getGlobalUID($type, $locid)
{
$mapID = $this->_locName . $this->_sourceURI . $type;
/**
* Retrieves the Horde server guid (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) for a given client
* locid. Returns false if no such id is stored yet.
*
* Opposite of getLocId which returns the locid for a given guid.
*/
function getGlobalUID($type, $locid) {
$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(
'map_id' => $mapID,
'map_locuid' => $locid,
'map_expired' => 0,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn();
}
return $GLOBALS['egw']->db->select('egw_contentmap', 'map_guid',
array(
'map_id' => $mapID,
'map_locuid' => $locid,
'map_expired' => false,
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn();
}
/**
* Converts a EGW GUID (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) to a client ID as
* used by the sync client (like 12) returns false if no such id
* is stored yet.
*/
function getLocID($type, $guid)
{
$mapID = $this->_locName . $this->_sourceURI . $type;
/**
* Converts a EGW GUID (like
* kronolith:0d1b415fc124d3427722e95f0e926b75) to a client ID as
* used by the sync client (like 12) returns false if no such id
* is stored yet.
*/
function getLocID($type, $guid) {
$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(
'map_id' => $mapID,
'map_guid' => $guid
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn()))
{
Horde::logMessage('SyncML: found LocID: '.$locuid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $locuid;
}
if (($locuid = $GLOBALS['egw']->db->select('egw_contentmap', 'map_locuid', array(
'map_id' => $mapID,
'map_guid' => $guid
), __LINE__, __FILE__, false, '', 'syncml')->fetchColumn())) {
Horde::logMessage('SyncML: found LocID: '.$locuid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
return $locuid;
}
/**
* Retrieves information about the previous sync if any. Returns
* false if no info found or a DateTreeObject with at least the
* following attributes:
*
* ClientAnchor: the clients Next Anchor of the previous sync.
* ServerAnchor: the Server Next Anchor of the previous sync.
*/
function getSyncSummary($type)
{
/**
* Retrieves information about the previous sync if any. Returns
* false if no info found or a DateTreeObject with at least the
* following attributes:
*
* ClientAnchor: the clients Next Anchor of the previous sync.
* ServerAnchor: the Server Next Anchor of the previous sync.
*/
function getSyncSummary($type) {
$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(
'dev_id' => $deviceID,
'sync_path' => $type
), __LINE__, __FILE__, false, '', 'syncml')->fetch()))
{
#Horde::logMessage("SyncML: get SYNCSummary for $deviceID serverts: ".$row['sync_serverts']." clients: ".$row['sync_clientts'], __FILE__, __LINE__, PEAR_LOG_DEBUG);
return array(
'ClientAnchor' => $row['sync_clientts'],
'ServerAnchor' => $row['sync_serverts'],
);
}
return false;
}
if (($row = $GLOBALS['egw']->db->select('egw_syncmlsummary', array('sync_serverts','sync_clientts'), array(
'dev_id' => $deviceID,
'sync_path' => $type
), __LINE__, __FILE__, false, '', 'syncml')->fetch())) {
Horde::logMessage("SyncML: getSyncSummary for $deviceID serverts: ".$row['sync_serverts']." clients: ".$row['sync_clientts'], __FILE__, __LINE__, PEAR_LOG_DEBUG);
return array(
'ClientAnchor' => $row['sync_clientts'],
'ServerAnchor' => $row['sync_serverts'],
);
}
return false;
}
function isAuthorized()
{
if (!$this->_isAuthorized)
{
if(!isset($this->_locName) && !isset($this->_password))
{
function isAuthorized() {
if (!$this->_isAuthorized) {
if(!isset($this->_locName) && !isset($this->_password)) {
Horde::logMessage('SyncML: Authentication not yet possible currently. Username and password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE;
}
if(!isset($this->_password))
{
if(!isset($this->_password)) {
Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE;
}
if(strpos($this->_locName,'@') === False)
{
if(strpos($this->_locName,'@') === False) {
$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);
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))
{
$this->_isAuthorized = true;
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
else
{
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text')) {
if ($GLOBALS['egw_info']['user']['apps']['syncml']) {
$this->_isAuthorized = true;
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
$this->_isAuthorized = false;
Horde::logMessage('SyncML is not enabled for this user', __FILE__, __LINE__, PEAR_LOG_ERROR);
}
} else {
$this->_isAuthorized = false;
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
$sessionID = session_id();
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.
* Returns always true.
*/
function removeAllUID($type)
{
* Removes all locid<->guid mappings for the given type.
* Returns always true.
*/
function removeAllUID($type) {
$mapID = $this->_locName . $this->_sourceURI . $type;
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');
return true;
}
}
/**
* Used in SlowSync
* Removes all locid<->guid mappings for the given type,
* that are older than $ts.
*
* Returns always true.
*/
function removeOldUID($type, $ts)
{
* Used in SlowSync
* Removes all locid<->guid mappings for the given type,
* that are older than $ts.
*
* Returns always true.
*/
function removeOldUID($type, $ts) {
$mapID = $this->_locName . $this->_sourceURI . $type;
$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
* the guid that was removed or false if no mapping entry was
* found.
*/
function removeUID($type, $locid)
{
* Used at session end to cleanup expired entries
* Removes all locid<->guid mappings for the given type,
* that are marked as expired and older than $ts.
*
* Returns always true.
*/
function removeExpiredUID($type, $ts) {
$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 (
'map_id' => $mapID,
'map_locuid' => $locid
);
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);
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);
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');
return $guid;
}
/**
* Puts a given client $locid and Horde server $guid pair into the
* map table to allow mapping between the client's and server's
* IDs. Actually there are two maps: from the localid to the guid
* and vice versa. The localid is converted to a key as follows:
* this->_locName . $this->_sourceURI . $type . $locid so you can
* have different syncs with different devices. If an entry
* already exists, it is overwritten.
*/
function setUID($type, $locid, $_guid, $ts=0)
{
#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);
/**
* Puts a given client $locid and Horde server $guid pair into the
* map table to allow mapping between the client's and server's
* IDs. Actually there are two maps: from the localid to the guid
* and vice versa. The localid is converted to a key as follows:
* this->_locName . $this->_sourceURI . $type . $locid so you can
* have different syncs with different devices. If an entry
* already exists, it is overwritten.
* Expired entries can be deleted at the next session start.
*/
function setUID($type, $locid, $_guid, $ts=0, $expired=false) {
#Horde::logMessage("SyncML: setUID $type, $locid, $_guid, $ts ", __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
$where = array(
'map_id' => $mapID,
'map_locuid' => $locid,
);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts ",
__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');
$mapID = $this->_locName . $this->_sourceURI . $type;
$data = $where + array(
'map_locuid' => $locid,
'map_timestamp' => $ts,
'map_expired' => 0,
);
$GLOBALS['egw']->db->insert('egw_contentmap', $data, $where, __LINE__, __FILE__, 'syncml');
// expire all client id's
$where = array(
'map_id' => $mapID,
'map_locuid' => $locid,
);
$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
*/
function writeClientDeviceInfo()
{
if (!isset($this->_clientDeviceInfo) || !is_array($this->_clientDeviceInfo))
{
* writes clients deviceinfo into database
*/
function writeClientDeviceInfo() {
if (!isset($this->_clientDeviceInfo)
|| !is_array($this->_clientDeviceInfo)) {
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);
$this->size_dev_hwversion = $tableDefDevInfo['fd']['dev_hwversion']['precision'];
unset($tableDefDevInfo);
@ -437,77 +522,89 @@ class EGW_SyncML_State extends Horde_SyncML_State
'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 (
'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']),
);
$GLOBALS['egw']->db->update('egw_syncmldevinfo', $data, $where, __LINE__, __FILE__, 'syncml');
}
else
{
$GLOBALS['egw']->db->update('egw_syncmldevinfo', $data, $where,
__LINE__, __FILE__, 'syncml');
} else {
$data = array (
'dev_dtdversion' => $this->_clientDeviceInfo['DTDVersion'],
'dev_numberofchanges' => $this->_clientDeviceInfo['supportNumberOfChanges'] ? true : false,
'dev_largeobjs' => $this->_clientDeviceInfo['supportLargeObjs'] ? true : false,
'dev_utc' => $this->_clientDeviceInfo['UTC'] ? true : false,
'dev_swversion' => $softwareVersion,
'dev_hwversion' => $hardwareVersion,
'dev_fwversion' => $firmwareVersion,
'dev_oem' => $this->_clientDeviceInfo['oem'],
'dev_model' => $this->_clientDeviceInfo['model'],
'dev_manufacturer' => $this->_clientDeviceInfo['manufacturer'],
'dev_devicetype' => $this->_clientDeviceInfo['deviceType'],
'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']),
'dev_dtdversion' => $this->_clientDeviceInfo['DTDVersion'],
'dev_numberofchanges' => ($this->_clientDeviceInfo['supportNumberOfChanges'] ? true : false),
'dev_largeobjs' => ($this->_clientDeviceInfo['supportLargeObjs'] ? true : false),
'dev_utc' => ($this->_clientDeviceInfo['UTC'] ? true : false),
'dev_swversion' => $softwareVersion,
'dev_hwversion' => $hardwareVersion,
'dev_fwversion' => $firmwareVersion,
'dev_oem' => $this->_clientDeviceInfo['oem'],
'dev_model' => $this->_clientDeviceInfo['model'],
'dev_manufacturer' => $this->_clientDeviceInfo['manufacturer'],
'dev_devicetype' => $this->_clientDeviceInfo['deviceType'],
'dev_datastore' => serialize($this->_clientDeviceInfo['dataStore']),
);
$GLOBALS['egw']->db->insert('egw_syncmldevinfo', $data, $where, __LINE__, __FILE__, 'syncml');
$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 (
'owner_locname' => $this->_locName,
'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
* are written to the database so they can be used to negotiate
* upcoming syncs.
*/
function writeSyncSummary()
{
#parent::writeSyncSummary();
/**
* After a successful sync, the client and server's Next Anchors
* are written to the database so they can be used to negotiate
* upcoming syncs.
*/
function writeSyncSummary() {
#parent::writeSyncSummary();
if (!isset($this->_serverAnchorNext) || !is_array($this->_serverAnchorNext))
{
return;
}
if (!isset($this->_serverAnchorNext)
|| !is_array($this->_serverAnchorNext)) {
return;
}
$deviceID = $this->_locName . $this->_sourceURI;
$deviceID = $this->_locName . $this->_sourceURI;
foreach((array)$this->_serverAnchorNext as $type => $a)
{
Horde::logMessage("SyncML: write SYNCSummary for $deviceID $type serverts: $a clients: ".$this->_clientAnchorNext[$type], __FILE__, __LINE__, PEAR_LOG_DEBUG);
foreach((array)$this->_serverAnchorNext as $type => $a) {
Horde::logMessage("SyncML: write SYNCSummary for $deviceID "
. "$type serverts: $a clients: "
. $this->_clientAnchorNext[$type],
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$where = array(
'dev_id' => $deviceID,
'sync_path' => $type,
);
$where = array(
'dev_id' => $deviceID,
'sync_path' => $type,
);
$data = array(
'sync_serverts' => $a,
'sync_clientts' => $this->_clientAnchorNext[$type]
);
$data = array(
'sync_serverts' => $a,
'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
/**
* $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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
* Using the PEAR Log class (which need to be installed!)
*
* @author Anthony Mills <amills@pyramid6.com>
* @version $Revision$
* @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 Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) The Horde Project (http://www.horde.org/)
* @version $Id$
*/
class Horde_SyncML_Sync {
/**
* Target, either contacts, notes, events,
*/
var $_targetLocURI;
/**
* Target, either contacts, notes, events,
*/
var $_targetLocURI;
var $_sourceLocURI;
var $_sourceLocURI;
/**
* 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;
}
var $_locName;
/**
* 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.
*/
* The synchronization method, one of the ALERT_* constants.
*
* @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) {
#Horde::logMessage('SyncML: content type is ' . $command->getContentType() .' moreData '. $command->_moreData, __FILE__, __LINE__, PEAR_LOG_DEBUG);
global $registry;
$history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state'];
if(isset($state->_moreData['luid'])) {
if(($command->_luid == $state->_moreData['luid'])) {
Horde::logMessage('SyncML: got next moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$lastChunks = implode('',$state->_moreData['chunks']);
$command->_content = $lastChunks.$command->_content;
$stringlen1 = strlen($lastChunks);
$stringlen2 = strlen($command->_content);
if(!$command->_moreData && strlen($command->_content) != $state->_moreData['contentSize']) {
$command->_status = RESPONSE_SIZE_MISMATCH;
$state->_moreData = array();
return;
} 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);
}
} else {
// alert 223 needed too
#$command->_status = ALERT_NO_END_OF_DATA;
$state->_moreData = array();
return;
if ($command->hasMoreData()) {
Horde::logMessage('SyncML: moreData: TRUE', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$command->setStatus(RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED);
return true;
}
$type = $this->_targetLocURI;
$syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
if (isset($syncml_prefs[$type])) {
$sync_conflicts = $syncml_prefs[$type];
} else {
$sync_conflicts = CONFLICT_SERVER_WINNING;
}
$state->setLocName($this->_locName);
$locName = $state->getLocName();
$sourceURI = $state->getSourceURI();
$hordeType = $state->getHordeType($type);
$refts = $state->getServerAnchorLast($type);
$state->setTargetURI($type);
$changes = array();
// First we get all changes done after the previous sync start
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
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;
}
$hordeType = $type = $this->_targetLocURI;
$hordeType = $state->getHordeType($hordeType);
Horde::logMessage('SyncML: runSyncCommand found ' . count($changes) .
" possible conflicts for $type", __FILE__, __LINE__, PEAR_LOG_DEBUG);
if(!$contentType = $command->getContentType()) {
$contentType = $state->getPreferedContentType($type);
}
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($command->getContent(), 'BEGIN:VTODO') !== false)
{
&& strpos($command->getContent(), 'BEGIN:VTODO') !== false) {
$hordeType = 'tasks';
}
$syncElementItems = $command->getSyncElementItems();
foreach($syncElementItems as $syncItem) {
$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 ($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',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
if (!is_a($guid, 'PEAR_Error') && $guid != false) {
$ts = $state->getSyncTSforAction($guid, 'add');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-Add");
Horde::logMessage('SyncML: added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$state->log('Client-Add');
Horde::logMessage('SyncML: added client entry as '
. $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
$state->log("Client-AddFailure");
Horde::logMessage('SyncML: Error in adding client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
$state->log('Client-AddFailure');
Horde::logMessage('SyncML: Error in adding client entry: '
. $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
}
} 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->getGlobalUID($type, $syncItem->getLocURI());
Horde::logMessage('SyncML: about to delete entry ' . $type .' / '. $guid . ' due to client request '.$syncItem->getLocURI(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!$guid) {
// 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) {
$registry->call($hordeType . '/delete', array($guid));
#$ts = $state->getSyncTSforAction($guid, 'delete');
#$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-Delete");
Horde::logMessage('SyncML: deleted entry ' . $guid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$ts = $state->getSyncTSforAction($guid, 'delete');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts, 1);
$state->log('Client-Delete');
Horde::logMessage('SyncML: deleted entry '
. $guid . ' due to client request',
__FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
$state->log("Client-DeleteFailure");
Horde::logMessage('SyncML: Failure deleting client entry, maybe gone already on server. msg:'. $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
$state->log('Client-DeleteFailure');
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')) {
$guid = $state->getGlobalUID($type, $syncItem->getLocURI());
$replace = true;
$ok = false;
if ($guid) {
Horde::logMessage('SyncML: locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_ERR);
// Entry exists: replace current one.
$ok = $registry->call($hordeType . '/replace',
array($guid, $state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
if (!is_a($ok, 'PEAR_Error')) {
$ts = $state->getSyncTSforAction($guid, 'modify');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
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;
$merge = false;
if ($guid)
{
Horde::logMessage('SyncML: locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (($sync_conflicts > CONFLICT_RESOLVED_WITH_DUPLICATE) || in_array($guid, $changes))
{
Horde::logMessage('SyncML: CONFLICT for locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING);
switch ($sync_conflicts)
{
case CONFLICT_CLIENT_WINNING:
$command->setStatus(RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING);
break;
case CONFLICT_SERVER_WINNING:
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) {
// Entry does not exist in map or database: add a new one.
Horde::logMessage('SyncML: try to add contentype ' . $contentType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Entry does either not exist in map or database, or should be added due to a conflict
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',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $guid));
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->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");
Horde::logMessage('SyncML: Error in replacing/'
. 'add client entry:' . $guid->message,
__FILE__, __LINE__, PEAR_LOG_ERR);
$state->log('Client-AddFailure');
}
}
}
}
return $guid;
}
}

View File

@ -1,6 +1,6 @@
<?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 $
@ -15,20 +15,9 @@ include_once 'Horde/SyncML/Sync.php';
* @since Horde 3.0
* @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.
*/
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
/**
* 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';
/**
* $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 {
function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) {
global $registry;
$state = &$_SESSION['SyncML.state'];
$adds = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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);
$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)) {
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)) {
Horde::logMessage("SyncML: RefreshFromServerSync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
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);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$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')) {
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
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;
}
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);
$state->log("Server-ExportFailed");
continue;
}
$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);
return $currentCmdID;
}
function loadData() {
global $registry;
$state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$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);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array()));
$adds = &$state->getAddedItems($hordeType);
$state->setAddedItems($syncType, $registry->call($hordeType. '/listBy',
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;
return count($state->getAddedItems($hordeType));
return count($state->getAddedItems($syncType)) - $delta_add;
}
}

View File

@ -1,194 +1,282 @@
<?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
* 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
* 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
* @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/TwoWaySync.php';
class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) {
global $registry;
$history = $GLOBALS['egw']->contenthistory;
$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);
// now we remove all UID from contentmap that have not been verified in this slowsync
$state->removeOldUID($syncType, $serverAnchorNext);
$adds = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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)) {
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)) {
Horde::logMessage("SyncML: slowsync add to client: $guid ignored, already at client($locID)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
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);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$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')) {
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
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);
continue;
}
$size = strlen($c);
// 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;
}
$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'])
{
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: 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);
return $currentCmdID;
}
/**
* Here's where the actual processing of a client-sent Sync
* Command takes place. Entries are added or replaced
* from the server database by using Horde API (Registry) calls.
*/
function runSyncCommand(&$command) {
#Horde::logMessage('SyncML: content type is ' . $command->getContentType() .' moreData '. $command->_moreData, __FILE__, __LINE__, PEAR_LOG_DEBUG);
global $registry;
$history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state'];
if(isset($state->_moreData['luid'])) {
if(($command->_luid == $state->_moreData['luid'])) {
Horde::logMessage('SyncML: got next moreData chunk '.$command->getContent(), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$lastChunks = implode('',$state->_moreData['chunks']);
$command->_content = $lastChunks.$command->_content;
$stringlen1 = strlen($lastChunks);
$stringlen2 = strlen($command->_content);
if(!$command->_moreData && strlen($command->_content) != $state->_moreData['contentSize']) {
$command->_status = RESPONSE_SIZE_MISMATCH;
$state->_moreData = array();
return;
} 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);
}
} else {
// alert 223 needed too
#$command->_status = ALERT_NO_END_OF_DATA;
$state->_moreData = array();
if ($command->hasMoreData()) {
Horde::logMessage('SyncML: moreData: TRUE', __FILE__, __LINE__, PEAR_LOG_DEBUG);
$command->setStatus(RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED);
return true;
}
$type = $this->_targetLocURI;
$syncml_prefs = $GLOBALS['egw_info']['user']['preferences']['syncml'];
if (isset($syncml_prefs[$type])) {
$sync_conflicts = $syncml_prefs[$type];
} else {
$sync_conflicts = CONFLICT_SERVER_WINNING;
}
$hordeType = $state->getHordeType($type);
$syncElementItems = $command->getSyncElementItems();
foreach($syncElementItems as $syncItem) {
$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;
}
}
// 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()) {
$contentType = $state->getPreferedContentType($type);
}
if (($contentType == 'text/x-vcalendar' || $contentType == 'text/calendar')
&& strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false)
{
&& strpos($syncItem->getContent(), 'BEGIN:VTODO') !== false) {
$hordeType = 'tasks';
}
$guid = false;
$guid = $registry->call($hordeType . '/search',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()) ));
if ($guid) {
# entry exists in database already. Just update the mapping
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-Replace");
} else {
# Entry does not exist in database: add a new one.
$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, '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);
// Check if the found entry came from the client
$guid_ts = $state->getSyncTSforAction($guid, 'add');
$sync_ts = $state->getChangeTS($type, $guid);
if ($sync_ts && $sync_ts == $guid_ts) {
// Entry came from the client, so we get a duplicate here
Horde::logMessage('SyncML: CONFLICT for locuri ' . $syncItem->getLocURI()
. ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_WARNING);
if ($sync_conflicts != CONFLICT_RESOLVED_WITH_DUPLICATE) {
$state->log("Client-AddReplaceIgnored");
continue;
}
} else {
Horde::logMessage('SyncML: Error in replacing/add client entry:' . $guid->message, __FILE__, __LINE__, PEAR_LOG_ERR);
$state->log("Client-AddFailure");
# Entry exists in database already. Just update the mapping
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;
}
function loadData() {
global $registry;
$state = &$_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$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);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/list', array()));
$adds = &$state->getAddedItems($hordeType);
$delta_add = count($registry->call($hordeType. '/listBy',
array('action' => 'add',
'timestamp' => $future,
'type' => $syncType,
'filter' => $this->_filterExpression)));
$state->setAddedItems($syncType, $registry->call($hordeType. '/list', array('filter' => $this->_filterExpression)));
$this->_syncDataLoaded = TRUE;
return count($state->getAddedItems($hordeType));
return count($state->getAddedItems($syncType)) - $delta_add;
}
}

View File

@ -1,175 +1,329 @@
<?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/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 {
function endSync($currentCmdID, &$output) {
function endSync($currentCmdID, & $output) {
global $registry;
$state = &$_SESSION['SyncML.state'];
$state = & $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType);
$refts = $state->getServerAnchorLast($syncType);
$currentCmdID = $this->handleSync($currentCmdID,
$hordeType,
$syncType,
$output,
$refts);
$currentCmdID = $this->handleSync($currentCmdID, $hordeType, $syncType, $output, $refts);
return $currentCmdID;
}
function handleSync($currentCmdID, $hordeType, $syncType,&$output, $refts) {
function handleSync($currentCmdID, $hordeType, $syncType, & $output, $refts) {
global $registry;
// 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;
$state = &$_SESSION['SyncML.state'];
$counter = 0;
$state = & $_SESSION['SyncML.state'];
$maxMsgSize = $state->getMaxMsgSizeClient();
$deviceInfo = $state->getClientDeviceInfo();
$changes = &$state->getChangedItems($hordeType);
$deletes = &$state->getDeletedItems($hordeType);
$adds = &$state->getAddedItems($hordeType);
if (isset($deviceInfo['maxEntries'])) {
$maxEntries = $deviceInfo['maxEntries'];
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);
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);
$serverAnchorNext = $state->getServerAnchorNext($syncType);
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
if(is_array($changes)) {
while($guid = array_shift($changes)) {
if (is_array($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');
$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) {
// Change was done by us upon request of 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;
}
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);
if (!$locid) {
// somehow we missed to add, lets store the uid, so we add this entry later
$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;
}
// Create a replace request for client.
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
$c = $registry->call($hordeType. '/export',
array('guid' => $guid, 'contentType' => $contentType));
if (!is_a($c, 'PEAR_Error')) {
$c = $registry->call($hordeType . '/export', array (
'guid' => $guid,
'contentType' => $contentType
));
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen.
Horde::logMessage("SyncML: change: $guid export content: $c", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
# LK $cmd->setContent($state->convertServer2Client($c, $contentType));
$cmd->setContent($c);
$cmd->setSourceURI($guid);
$cmd->setTargetURI($locid);
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
Horde :: logMessage("SyncML: change: export of guid $guid failed:\n" . print_r($c, true), __FILE__, __LINE__, PEAR_LOG_WARNING);
continue;
}
$size = strlen($c);
// return if we have to much data
if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
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);
$state->log('Server-ExportFailed');
continue;
}
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Replace');
$state->log('Server-Replace');
// return if we have to much data
if (++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
if (($currentSize + $size +MIN_MSG_LEFT * 2) > $maxMsgSize) {
// put the item back in the queue
$changes[] = $guid;
$state->setSyncStatus(SERVER_SYNC_DATA_PENDING);
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
if(is_array($deletes)) {
while($guid = array_shift($deletes)) {
if (is_array($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');
$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) {
// Change was done by us upon request of 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;
}
$locid = $state->getLocID($syncType, $guid);
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;
}
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.
$cmd = new Horde_SyncML_Command_Sync_ContentSyncElement();
$cmd->setTargetURI($locid);
$cmd->setSourceURI($guid);
$cmd->setLocURI($locid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Delete');
$state->log('Server-Delete');
$state->removeUID($syncType, $locid);
$contentType = $state->getPreferedContentTypeClient($this->_sourceLocURI, $this->_targetLocURI);
// return if we have to much data
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
// 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();
}
}
// 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);
// handle missing adds.
if(count($missedAdds) > 0) {
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 = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: merged adds counter ".count($adds).' / '.$adds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (count($missedAdds) > 0) {
Horde :: logMessage("SyncML: add missed changes as adds " . count($adds) . ' / ' . $missedAdds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
$adds = array_merge($adds, $missedAdds);
Horde :: logMessage("SyncML: merged adds counter " . count($adds) . ' / ' . $adds[0], __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
if(is_array($adds)) {
while($guid = array_shift($adds)) {
if (is_array($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');
$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) {
// Change was done by us upon request of 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;
}
@ -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
// have a locid again. This is a heuristic to avoid
// 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;
}
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.
$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,
if (!is_a($c, 'PEAR_Error')) {
));
if (is_a($c, 'PEAR_Error')) {
// Item in history but not in database. Strange, but can happen.
$cmd->setContent($c);
$cmd->setContentType($contentType['ContentType']);
if (isset($contentType['ContentFormat']))
{
$cmd->setContentFormat($contentType['ContentFormat']);
}
$cmd->setSourceURI($guid);
$currentCmdID = $cmd->outputCommand($currentCmdID, $output, 'Add');
$state->log('Server-Add');
Horde :: logMessage("SyncML: add: export of guid $guid failed:\n" . print_r($c, true), __FILE__, __LINE__, PEAR_LOG_WARNING);
continue;
}
// return if we have to much data
if(++$counter >= MAX_ENTRIES
&& isset($contentType['mayFragment'])
&& $contentType['mayFragment'])
{
$size = strlen($c);
// return if we have to much data
if ($maxMsgSize && !$deviceInfo['supportLargeObjs']) {
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);
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);
return $currentCmdID;
@ -228,24 +400,53 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync {
function loadData() {
global $registry;
$state = &$_SESSION['SyncML.state'];
$state = & $_SESSION['SyncML.state'];
$syncType = $this->_targetLocURI;
$hordeType = $state->getHordeType($syncType);
$state->setTargetURI($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);
$state->setChangedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'modify', 'timestamp' => $refts)));
Horde :: logMessage("SyncML: reading changed items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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);
$state->setDeletedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'delete', 'timestamp' => $refts)));
Horde :: logMessage("SyncML: reading deleted items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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);
$state->setAddedItems($hordeType, $registry->call($hordeType. '/listBy', array('action' => 'add', 'timestamp' => $refts)));
Horde :: logMessage("SyncML: reading added items from database for $hordeType", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$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;
return count($state->getChangedItems($hordeType)) +
count($state->getDeletedItems($hordeType)) +
count($state->getAddedItems($hordeType));
return count($state->getChangedItems($syncType)) - $delta_mod +count($state->getDeletedItems($syncType)) + count($state->getAddedItems($syncType)) - $delta_add +count($state->getConflictItems($syncType));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,14 @@
/**
* 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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
@ -21,11 +20,6 @@ class Horde_iCalendar_valarm extends Horde_iCalendar {
return 'vAlarm';
}
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VALARM');
}
function exportvCalendar()
{
return parent::_exportvData('VALARM');

View File

@ -2,7 +2,6 @@
require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
// The following were shamelessly yoinked from Contact_Vcard_Build
// Part numbers for N components.
define('VCARD_N_FAMILY', 0);
@ -27,49 +26,47 @@ define('VCARD_GEO_LON', 1);
/**
* 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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Karsten Fourmont <karsten@horde.org>
* @version $Revision$
* @package Horde_iCalendar
*/
class Horde_iCalendar_vcard extends Horde_iCalendar {
function Horde_iCalendar_vcard($version = '2.1')
{
return parent::Horde_iCalendar($version);
}
function getType()
{
return 'vcard';
}
function parsevCalendar($data)
{
return parent::parsevCalendar($data, 'vcard');
}
/**
* Unlike vevent and vtodo, a vcard is normally not enclosed in an
* iCalendar container. (BEGIN..END)
*/
function exportvCalendar()
{
#$requiredAttributes['BODY'] = '';
$requiredAttributes['VERSION'] = '2.1';
$requiredAttributes['VERSION'] = $this->_version;
$requiredAttributes['N'] = ';;;;;;';
if ($this->_version == '3.0') {
$requiredAttributes['FN'] = '';
}
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);
}
}
//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
* "Professor Dagobert T Duck Sen"
*
* @return string Full name of vcard "N" tag
* or null if no N tag.
* @return string Full name of vcard "N" tag or null if no N tag.
*/
function printableName()
{
@ -90,6 +86,8 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
return null;
}
$name_arr = array();
if (!empty($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)
{
// Empty values are still empty.
if (!$address) {
return $address;
}
require_once 'Mail/RFC822.php';
require_once 'Horde/MIME.php';
@ -126,8 +129,9 @@ class Horde_iCalendar_vcard extends Horde_iCalendar {
$rfc822 = new Mail_RFC822();
}
$rfc822->validateMailbox($address);
if (!$rfc822->validateMailbox($address)) {
return $address;
}
return MIME::rfc822WriteAddress($address->mailbox, $address->host);
}

View File

@ -2,15 +2,14 @@
/**
* 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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
@ -21,32 +20,28 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
return 'vEvent';
}
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VEVENT');
}
function exportvCalendar()
{
// Default values.
$requiredAttributes = array();
$requiredAttributes['DTSTAMP'] = time();
#$requiredAttributes['ORGANIZER'] = 'Unknown Organizer';
$requiredAttributes['UID'] = $this->_exportDateTime(time()) . '@' . $_SERVER['SERVER_NAME'];
$requiredAttributes['UID'] = $this->_exportDateTime(time())
. 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) ?
$this->_container->getAttribute('METHOD') : 'PUBLISH';
switch ($method) {
case 'PUBLISH':
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = '';
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = '';
break;
case 'REQUEST':
$requiredAttributes['ATTENDEE'] = '';
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = '';
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['SUMMARY'] = '';
break;
case 'REPLY':
@ -54,9 +49,9 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
break;
case 'ADD':
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['DTSTART'] = time();
$requiredAttributes['SEQUENCE'] = 1;
$requiredAttributes['SUMMARY'] = '';
$requiredAttributes['SUMMARY'] = '';
break;
case 'CANCEL':
@ -88,7 +83,8 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
function updateAttendee($email, $status, $fullname = '')
{
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;
if (!empty($fullname)) {
$this->_attributes[$key]['params']['CN'] = $fullname;
@ -101,7 +97,7 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
if (!empty($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);
if (is_a($organizer, 'PEAR_Error')) {
return null;
return _("An unknown person");
}
if (isset($organizer[0]['CN'])) {
@ -128,7 +124,7 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
/**
* 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)
{
@ -137,7 +133,9 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
$currentValue = $this->getAttribute($newAttribute['name']);
if (is_a($currentValue, 'PEAR_error')) {
// Already exists so just add it.
$this->setAttribute($newAttribute['name'], $newAttribute['value'], $newAttribute['params']);
$this->setAttribute($newAttribute['name'],
$newAttribute['value'],
$newAttribute['params']);
} else {
// Already exists so locate and modify.
$found = false;
@ -178,19 +176,21 @@ class Horde_iCalendar_vevent extends Horde_iCalendar {
* Update just the attendess of 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 updateAttendeesFromvEvent($vevent)
{
$newAttributes = $vevent->getAllAttributes();
foreach ($newAttributes as $newAttribute) {
if (!$newAttribute['name'] == 'ATTENDEE') {
if ($newAttribute['name'] != 'ATTENDEE') {
continue;
}
$currentValue = $this->getAttribute($newAttribute['name']);
if (is_a($currentValue, 'PEAR_error')) {
// Already exists so just add it.
$this->setAttribute($newAttribute['name'], $newAttribute['value'], $newAttribute['params']);
$this->setAttribute($newAttribute['name'],
$newAttribute['value'],
$newAttribute['params']);
} else {
// Already exists so locate and modify.
$found = false;

View File

@ -1,52 +1,78 @@
<?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
* 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>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
var $_busyPeriods = array();
var $_extraParams = array();
/**
* Returns the type of this calendar component.
*
* @return string The type of this component.
*/
function getType()
{
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.
foreach ($this->_attributes as $key => $attribute) {
if ($attribute['name'] == 'FREEBUSY') {
foreach ($attribute['value'] as $value) {
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]);
if ($attribute['name'] != 'FREEBUSY') {
continue;
}
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()
{
foreach ($this->_busyPeriods as $start => $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');
@ -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()
{
@ -71,12 +99,12 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') {
$attr = 'ORGANIZER';
} else if ($method == 'REPLY') {
} elseif ($method == 'REPLY') {
$attr = 'ATTENDEE';
}
$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'];
}
@ -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()
{
@ -100,7 +130,7 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
if (is_a($method, 'PEAR_Error') || $method == 'PUBLISH') {
$attr = 'ORGANIZER';
} else if ($method == 'REPLY') {
} elseif ($method == 'REPLY') {
$attr = 'ATTENDEE';
}
@ -113,13 +143,34 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
}
}
/**
* Returns the busy periods.
*
* @return array All busy periods.
*/
function getBusyPeriods()
{
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)
{
@ -131,21 +182,21 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
return $periods;
}
// Locate the first time in the requested period we have data
// for.
// Locate the first time in the requested period we have data for.
$nextstart = max($startStamp, $this->getStart());
// Check each busy period and add free periods in between.
foreach ($this->_busyPeriods as $start => $end) {
if ($start <= $endStamp && $end >= $nextstart) {
$periods[$nextstart] = min($start, $endStamp);
if ($nextstart <= $start) {
$periods[$nextstart] = min($start, $endStamp);
}
$nextstart = min($end, $endStamp);
}
}
// If we didn't read the end of the requested period but still
// have data then mark as free to the end of the period or
// available data.
// If we didn't read the end of the requested period but still have
// data then mark as free to the end of the period or available data.
if ($nextstart < $endStamp && $nextstart < $this->getEnd()) {
$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.
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;
// Make sure the period length is always positive.
@ -171,26 +235,35 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
$start = min($start, $tempEnd);
if (isset($this->_busyPeriods[$start])) {
// Already a period starting at this time. Extend to the
// length of the longest of the two.
$this->_busyPeriods[$start] = max($end, $this->_busyPeriods[$start]);
// Already a period starting at this time. Change the current
// period only if the new one is longer. This might be a problem
// 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 {
// Add a new busy period.
$this->_busyPeriods[$start] = $end;
$this->_extraParams[$start] = $extra;
}
return true;
}
/**
* Get the timestamp of the start of the time period this free
* busy information covers.
* Returns the timestamp of the start of the time period this free busy
* information covers.
*
* @return integer A timestamp.
*/
function getStart()
{
if (!is_a($this->getAttribute('DTSTART'), 'PEAR_Error')) {
return $this->getAttribute('DTSTART');
} else if (count($this->_busyPeriods)) {
} elseif (count($this->_busyPeriods)) {
return min(array_keys($this->_busyPeriods));
} else {
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.
*
* @return integer A timestamp.
*/
function getEnd()
{
if (!is_a($this->getAttribute('DTEND'), 'PEAR_Error')) {
return $this->getAttribute('DTEND');
} else if (count($this->_busyPeriods)) {
} elseif (count($this->_busyPeriods)) {
return max(array_values($this->_busyPeriods));
} else {
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)
{
@ -221,70 +305,155 @@ class Horde_iCalendar_vfreebusy extends Horde_iCalendar {
return false;
}
$extra = $freebusy->getExtraParams();
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');
$thatattr = $freebusy->getAttribute('DTSTART');
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')) {
if ($thatattr > $thisattr) {
$this->setAttribute('DTSTART', $thatattr);
if ($thatattr < $thisattr) {
$this->setAttribute('DTSTART', $thatattr, array(), false);
}
}
$thisattr = $this->getAttribute('DTEND');
$thatattr = $freebusy->getAttribute('DTEND');
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')) {
if ($thatattr < $thisattr) {
$this->setAttribute('DTEND', $thatattr);
if ($thatattr > $thisattr) {
$this->setAttribute('DTEND', $thatattr, array(), false);
}
}
if ($simplify) {
$this->simplify();
}
return true;
}
/**
* Remove all overlaps and simplify the busy periods array as much
* as possible.
* Removes all overlaps and simplifies the busy periods array as much as
* possible.
*/
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();
$checkedExtra = array();
$checkedEmpty = true;
foreach ($this->_busyPeriods as $start => $end) {
foreach ($busyPeriods as $start => $end) {
if ($checkedEmpty) {
$checked[$start] = $end;
$checkedExtra[$start] = isset($extraParams[$start])
? $extraParams[$start] : array();
$checkedEmpty = false;
} else {
$added = false;
foreach ($checked as $testStart => $testEnd) {
if ($start == $testStart) {
$checked[$testStart] = max($testEnd, $end);
$added = true;
} else if ($end <= $testEnd && $end >= $testStart) {
// Replace old period if the new period lies around the
// old period.
if ($start <= $testStart && $end >= $testEnd) {
// Remove old period entry.
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;
}
if ($added) {
break;
}
}
if (!$added) {
$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.
*
* $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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
@ -21,11 +20,6 @@ class Horde_iCalendar_vjournal extends Horde_iCalendar {
return 'vJournal';
}
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VJOURNAL');
}
function exportvCalendar()
{
return parent::_exportvData('VJOURNAL');

View File

@ -5,29 +5,29 @@ require_once EGW_API_INC.'/horde/Horde/iCalendar.php';
/**
* 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
* 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>
* @version $Revision$
* @package Horde_iCalendar
*/
class Horde_iCalendar_vnote extends Horde_iCalendar {
function Horde_iCalendar_vnote($version = '1.1')
{
return parent::Horde_iCalendar($version);
}
function getType()
{
return 'vNote';
}
function parsevCalendar($data)
{
return parent::parsevCalendar($data, 'VNOTE');
}
/**
* Unlike vevent and vtodo, a vnote is normally not enclosed in an
* 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.
*
* $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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
@ -21,16 +20,145 @@ class Horde_iCalendar_vtimezone extends Horde_iCalendar {
return 'vTimeZone';
}
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VTIMEZONE');
}
function exportvCalendar()
{
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.
*
* $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
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author Mike Cochrane <mike@graftonhall.co.nz>
* @version $Revision$
* @since Horde 3.0
* @package Horde_iCalendar
*/
@ -21,11 +20,6 @@ class Horde_iCalendar_vtodo extends Horde_iCalendar {
return 'vTodo';
}
function parsevCalendar($data)
{
parent::parsevCalendar($data, 'VTODO');
}
function exportvCalendar()
{
return parent::_exportvData('VTODO');

View File

@ -1,8 +1,8 @@
<?php
/**
* Constants are from Binary XML Content Format Specification Version
* 1.3, 25 July 2001 found at http://www.wapforum.org
* Constants are from Binary XML Content Format Specification Version 1.3, 25
* 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_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_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_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.
@ -63,13 +71,14 @@ define('CHARSET_UTF_16LE', 'UTF-16LE');
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.
*
* $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
*/
class XML_WBXML {
@ -173,8 +182,17 @@ class XML_WBXML {
// Not all SyncML clients know this, so we
// should use the string table.
// 0xFD1 => DPI_DTD_SYNCML_1_1,
4051 => DPI_DTD_SYNCML_1_1,
4052 => DPI_DTD_DEVINF_1_1,
// These codes are taken from libwbxml wbxml_tables.h:
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;
}
@ -197,8 +215,18 @@ class XML_WBXML {
DPI_DTD_WTA_WML_1_2 => 12,
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.
// 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_DEVINF_1_1 => 0xFD2,
);

View File

@ -1,15 +1,16 @@
<?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
* 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
*/
class XML_WBXML_ContentHandler {
@ -35,9 +36,13 @@ class XML_WBXML_ContentHandler {
$this->_currentUri = new XML_WBXML_LifoQueue();
}
/**
*/
function raiseError($error)
{
include_once 'PEAR.php';
if (!class_exists('PEAR')) {
require 'PEAR.php';
}
return PEAR::raiseError($error);
}
@ -71,7 +76,7 @@ class XML_WBXML_ContentHandler {
return strlen($this->_output);
}
function startElement($uri, $element, $attrs)
function startElement($uri, $element, $attrs = array())
{
$this->_output .= '<' . $element;
@ -104,12 +109,7 @@ class XML_WBXML_ContentHandler {
function opaque($o)
{
// I can check the first chanracter and see if it is WBXML.
if (ord($o[0]) < 10) {
// Should decode this, I really need a call back function.
} else {
$this->_output .= $o;
}
$this->_output .= $o;
}
function setOpaqueHandler($opaqueHandler)
@ -122,6 +122,15 @@ class XML_WBXML_ContentHandler {
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 {

View File

@ -1,15 +1,16 @@
<?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
* 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.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
class XML_WBXML_DTD {
@ -88,6 +89,11 @@ class XML_WBXML_DTD {
function toCodePageURI($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;
return $ret;

View File

@ -3,16 +3,17 @@
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
* 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.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
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(0x31, "VerDTD"); // 0x00
$this->setTag(0x32, "VerProto"); // 0x00
$this->setTag(0x33, "NumberOfChanged"); // 0x00
$this->setTag(0x33, "NumberOfChanges"); // 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) {
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:syncml1.0');
$this->setCodePage(1, '-//SYNCML//DTD MetInf 1.0//EN', 'syncml:metinf');
$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');
if ($this->version == 1) {
$this->setCodePage(0, DPI_DTD_SYNCML_1_1, 'syncml:syncml1.1');
$this->setCodePage(1, DPI_DTD_METINF_1_1, 'syncml:metinf1.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';
/**
* $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
* 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.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
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'
*/
#Horde::logMessage("XML_WBXML_DTD_SyncMLDevInf version=" . $this->version, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$this->setTag(0x05, "CTCap"); // 0x00
$this->setTag(0x06, "CTType"); // 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(0x29, "SupportNumberOfChanges"); // 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) {
$this->setCodePage(0, '-//SYNCML//DTD DevInf 1.0//EN', 'syncml:devinf');
$this->setURI('syncml:devinf');
} else {
$this->setCodePage(0, '-//SYNCML//DTD DevInf 1.1//EN', 'syncml:devinf1.1');
if ($this->version == 1) {
$this->setCodePage(0, DPI_DTD_DEVINF_1_1, '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';
/**
* $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
* 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.
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
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(0x13, "Type"); // 0x01
$this->setTag(0x14, "Version"); // 0x01
$this->setTag(0x15, "MaxObjSize"); // 0x01
$this->setTag(0x16, "FieldLevel"); // 0x01
if ($this->version == 0) {
#$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:SYNCML1.0');
$this->setCodePage(0, '-//SYNCML//DTD SyncML 1.0//EN', 'syncml:syncml1.0');
$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');
if ($this->version == 1) {
$this->setCodePage(0, DPI_DTD_SYNCML_1_1, 'syncml:syncml1.1');
$this->setCodePage(1, DPI_DTD_METINF_1_1, 'syncml:metinf1.1');
$this->setURI('syncml:metinf1.1');
//$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';
/**
* $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.
*
* From Binary XML Content Format Specification Version 1.3, 25 July
* 2001 found at http://www.wapforum.org
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
class XML_WBXML_DTDManager {
/**
* @var array
*/
var $_strDTD = array();
/**
* @var array
*/
var $_strDTDURI = array();
/**
*/
function XML_WBXML_DTDManager()
{
$this->registerDTD('-//SYNCML//DTD SyncML 1.0//EN', '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_0, 'syncml:syncml1.0', new XML_WBXML_DTD_SyncML(0));
$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('-//SYNCML//DTD MetInf 1.1//EN', 'syncml:metinf1.1', new XML_WBXML_DTD_SyncMLMetInf(1));
$this->registerDTD(DPI_DTD_METINF_1_0, 'syncml:metinf1.0', new XML_WBXML_DTD_SyncMLMetInf(0));
$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('-//SYNCML//DTD DevInf 1.1//EN', 'syncml:devinf1.1', new XML_WBXML_DTD_SyncMLDevInf(1));
$this->registerDTD(DPI_DTD_DEVINF_1_0, 'syncml:devinf1.0', new XML_WBXML_DTD_SyncMLDevInf(0));
$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);
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)
{
$dtd->setDPI($publicIdentifier);
$publicIdentifier = strtolower($publicIdentifier);
$this->_strDTD[$publicIdentifier] = $dtd;
$this->_strDTDURI[strtolower($uri)] = $dtd;
}

View File

@ -5,16 +5,17 @@ include_once 'XML/WBXML/DTDManager.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.
*
* From Binary XML Content Format Specification Version 1.3, 25 July
* 2001 found at http://www.wapforum.org
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
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
*/
function setContentHandler(&$ch) {
function setContentHandler(&$ch)
{
$this->_ch = &$ch;
}
/**
@ -96,7 +98,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
{
$value = $input{$this->_strpos++};
$value = ord($value);
return $value;
}
@ -133,12 +135,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
*/
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->_strpos = 0;
if (empty($this->_ch)) {
@ -149,6 +147,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// version = u_int8
// currently 1, 2 or 3
$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
// publicid = mb_u_int32 | (zero index)
@ -156,9 +155,11 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// Containing the value zero (0)
// The actual DPI is determined after the String Table is read.
$dpiStruct = $this->getDocumentPublicIdentifier($wbxml);
// Get Charset from 5.6
// charset = mb_u_int32
$this->_charset = $this->getCharset($wbxml);
#Horde::logMessage("WBXML[" . $this->_strpos . "] charset " . $this->_charset, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Get String Table from 5.7
// strb1 = length *byte
@ -166,8 +167,8 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
// Get Document Public Idetifier from Section 5.5.
$this->_dpi = $this->getDocumentPublicIdentifierImpl($dpiStruct['dpiType'],
$dpiStruct['dpiNumber'],
$this->_stringTable);
$dpiStruct['dpiNumber']);
#$this->_stringTable);
// Now the real fun begins.
// 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);
if (!$this->_tagDTD) {
return $this->raiseError('No DTD found for '
. $this->_dpi . '/'
return $this->raiseError('No DTD found for '
. $this->_dpi . '/'
. $dpiStruct['dpiNumber']);
}
@ -204,6 +205,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
function getDocumentPublicIdentifier($input)
{
$i = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
if ($i == 0) {
return array('dpiType' => 2,
'dpiNumber' => $this->getByte($input));
@ -218,6 +220,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
if ($dpiType == 1) {
return XML_WBXML::getDPIString($dpiNumber);
} else {
#Horde::logMessage("WBXML string table $dpiNumber:\n" . print_r($this->_stringTable, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
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
* and then length bytes forming the table.
* 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)) {
$this->_error =
$this->_ch->raiseError('Invalid offset ' . $index
. ' value encountered around position '
. $this->_strpos
. '. Broken wbxml?');
$this->raiseError('Invalid offset ' . $index
. ' value encountered around position '
. $this->_strpos
. '. Broken wbxml?');
return '';
}
@ -270,17 +273,17 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
if (ord($ch) == 0) {
return ''; // don't return '#'
}
while (ord($ch) != 0) {
$str[$i++] = $ch;
if ($index >= strlen($this->_stringTable)) {
break;
break;
}
$ch = $this->_stringTable[$index++];
}
// print "string table entry: $str\n";
return $str;
}
function _decode($input)
@ -288,7 +291,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
$token = $this->getByte($input);
$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) {
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:
// Section 5.8.4.6
$size = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
// print "opaque of size $size\n"; // @todo remove debug
$b = $this->_substr($input, $this->_strpos, $size);
#$b = mb_substr($input, $this->_strpos, $size, 'ISO-8859-1');
$this->_strpos += $size;
if ($size > 0) {
#Horde::logMessage("WBXML opaque document size=$size, next=" . ord($input{$this->_strpos}), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$b = $this->_substr($input, $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
// 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.
if ($this->_isData && ord($b) <= 10) {
$decoder = new XML_WBXML_Decoder(true);
$decoder->setContentHandler($this->_ch);
$s = $decoder->decode($b);
// /* // @todo: FIXME currently we can't decode Nokia
// DevInf data. So ignore error for the time beeing.
if (is_a($s, 'PEAR_Error')) {
$this->_error = $s;
return;
if ($this->_isData && ord($b) < 10) {
#Horde::logMessage("WBXML opaque document size=$size, \$b[0]=" . ord($b), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$decoder = new XML_WBXML_Decoder(true);
$decoder->setContentHandler($this->_ch);
$s = $decoder->decode($b);
// /* // @todo: FIXME currently we can't decode Nokia
// DevInf data. So ignore error for the time beeing.
if (is_a($s, 'PEAR_Error')) {
$this->_error = $s;
return;
}
// */
// $this->_ch->characters($s);
} else {
/* 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:
// FIXME Opaque is used by SYNCML. Opaque data that depends on the context
// if (contentHandler instanceof OpaqueContentHandler) {
@ -650,7 +655,7 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
*/
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;
$ch = $input[$this->_strpos++];
if (ord($ch) == 0) {
@ -679,6 +684,5 @@ class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
}
return $ret;
}
}

View File

@ -6,16 +6,17 @@ include_once 'XML/WBXML/DTDManager.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.
*
* From Binary XML Content Format Specification Version 1.3, 25 July
* 2001 found at http://www.wapforum.org
*
* @author Anthony Mills <amills@pyramid6.com>
* @package XML_WBXML
*/
class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
@ -38,7 +39,7 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
var $_subParser = null;
var $_subParserStack = 0;
/**
* The XML parser.
*
@ -97,7 +98,10 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function writeHeader($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();
// Set Version Number from Section 5.4
@ -132,7 +136,11 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
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) {
$strings[0] = $dpiString;
@ -191,7 +199,7 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function _getBytes($string, $cs)
{
$string = String::convertCharset($string, $cs, 'utf-8');
$string = String::convertCharset($string, $cs, 'utf-8');
$nbytes = strlen($string);
$bytes = array();
@ -210,8 +218,10 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
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->_hasWrittenHeader) {
$this->writeHeader($uri);
@ -222,11 +232,11 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
if ($this->_subParser == null) {
$this->writeTag($name, $attributes, true, $this->_charset);
} else {
$this->_subParser->startElement($uri,$name, $attributes);
$this->_subParser->startElement($uri, $name, $attributes);
}
} else {
$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);
}
function opaque($bytes)
function opaque($o)
{
if ($this->_subParser == null) {
$this->_output .= chr(XML_WBXML_GLOBAL_TOKEN_OPAQUE);
XML_WBXML::intToMBUInt32($this->_output, count($bytes));
$this->_output .= $bytes;
XML_WBXML::intToMBUInt32($this->_output, strlen($o));
$this->_output .= $o;
}
}
@ -274,7 +284,6 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function writeTag($name, $attrs, $hasContent, $cs)
{
if ($attrs != null && !count($attrs)) {
$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);
}
}
@ -376,11 +385,16 @@ class XML_WBXML_Encoder extends XML_WBXML_ContentHandler {
function changecodepage($uri)
{
// @todo: this is a hack!
// what's the reason for this hack???? Lars
if ($uri != 'syncml:devinf' && $uri != 'syncml:metinf' && $uri != 'syncml:syncml1.0' && !preg_match('/1\.1$/', $uri)) {
if ($this->_dtd->getVersion() == 2 && !preg_match('/1\.2$/', $uri)) {
$uri .= '1.2';
}
if ($this->_dtd->getVersion() == 1 && !preg_match('/1\.1$/', $uri)) {
$uri .= '1.1';
}
if ($this->_dtd->getVersion() == 0 && !preg_match('/1\.0$/', $uri)) {
$uri .= '1.0';
}
$cp = $this->_dtd->toCodePageURI($uri);
if (strlen($cp)) {
$this->_dtd = &$this->_dtdManager->getInstanceURI($uri);

View File

@ -21,7 +21,7 @@ $conf['auth']['driver'] = 'auto';
$conf['log']['priority'] = PEAR_LOG_DEBUG;
$conf['log']['ident'] = 'EGWSYNC';
$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']['type'] = 'error_log';
$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
* the 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. */
include_once 'PEAR.php';
@ -30,14 +38,17 @@ include_once 'PEAR.php';
include_once 'Horde.php';
include_once 'Horde/Registry.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/Notification.php';
#include_once 'Horde/Auth.php';
#include_once 'Horde/Browser.php';
#include_once 'Horde/Perms.php';
include_once 'Horde/iCalendar.php';
//include_once 'Horde/Notification.php';
//include_once 'Horde/Auth.php';
//include_once 'Horde/Browser.php';
//include_once 'Horde/Perms.php';
#/* Browser detection object. */
#if (class_exists('Browser')) {
# $browser = &Browser::singleton();
#}
/* Browser detection object. *
if (class_exists('Browser')) {
$browser = &Browser::singleton();
}
*/