forked from extern/egroupware
SyncML fixes
- we dont handle tasks in calendar that way
This commit is contained in:
parent
1eaa8fb11e
commit
1ad5811345
937
phpgwapi/inc/horde/Horde/SyncML/State.php
Normal file
937
phpgwapi/inc/horde/Horde/SyncML/State.php
Normal file
@ -0,0 +1,937 @@
|
||||
<?php
|
||||
|
||||
define('ALERT_DISPLAY', 100);
|
||||
|
||||
// Not implemented.
|
||||
define('ALERT_TWO_WAY', 200);
|
||||
define('ALERT_SLOW_SYNC', 201);
|
||||
define('ALERT_ONE_WAY_FROM_CLIENT', 202);
|
||||
define('ALERT_REFRESH_FROM_CLIENT', 203);
|
||||
define('ALERT_ONE_WAY_FROM_SERVER', 204);
|
||||
define('ALERT_REFRESH_FROM_SERVER', 205);
|
||||
|
||||
// Not implemented.
|
||||
define('ALERT_TWO_WAY_BY_SERVER', 206);
|
||||
define('ALERT_ONE_WAY_FROM_CLIENT_BY_SERVER', 207);
|
||||
define('ALERT_REFRESH_FROM_CLIENT_BY_SERVER', 208);
|
||||
define('ALERT_ONE_WAY_FROM_SERVER_BY_SERVER', 209);
|
||||
define('ALERT_REFRESH_FROM_SERVER_BY_SERVER', 210);
|
||||
|
||||
define('ALERT_RESULT_ALERT', 221);
|
||||
define('ALERT_NEXT_MESSAGE', 222);
|
||||
define('ALERT_NO_END_OF_DATA', 223);
|
||||
|
||||
define('MIME_SYNCML_XML', 'application/vnd.syncml+xml');
|
||||
define('MIME_SYNCML_WBXML', 'application/vnd.syncml+wbxml');
|
||||
|
||||
define('MIME_SYNCML_DEVICE_INFO_XML', 'application/vnd.syncml-devinf+xml');
|
||||
define('MIME_SYNCML_DEVICE_INFO_WBXML', 'application/vnd.syncml-devinf+wbxml');
|
||||
|
||||
define('MIME_TEXT_PLAIN', 'text/plain');
|
||||
define('MIME_VCARD_V21', 'text/x-vcard');
|
||||
define('MIME_VCARD_V30', 'text/vcard');
|
||||
|
||||
define('MIME_VCALENDAR', 'text/x-vcalendar');
|
||||
define('MIME_ICALENDAR', 'text/calendar');
|
||||
define('MIME_XML_ICALENDAR', 'application/vnd.syncml-xcal');
|
||||
|
||||
define('MIME_MESSAGE', 'text/message');
|
||||
|
||||
define('MIME_SYNCML_XML_EMAIL', 'application/vnd.syncml-xmsg');
|
||||
define('MIME_SYNCML_XML_BOOKMARK', 'application/vnd.syncml-xbookmark');
|
||||
define('MIME_SYNCML_RELATIONAL_OBJECT', 'application/vnd.syncml-xrelational');
|
||||
|
||||
define('RESPONSE_IN_PROGRESS', 101);
|
||||
|
||||
define('RESPONSE_OK', 200);
|
||||
define('RESPONSE_ITEM_ADDED', 201);
|
||||
define('RESPONSE_ACCEPTED_FOR_PROCESSING', 202);
|
||||
define('RESPONSE_NONAUTHORIATATIVE_RESPONSE', 203);
|
||||
define('RESPONSE_NO_CONTENT', 204);
|
||||
define('RESPONSE_RESET_CONTENT', 205);
|
||||
define('RESPONSE_PARTIAL_CONTENT', 206);
|
||||
define('RESPONSE_CONFLICT_RESOLVED_WITH_MERGE', 207);
|
||||
define('RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING', 208);
|
||||
define('RESPONSE_CONFILCT_RESOLVED_WITH_DUPLICATE', 209);
|
||||
define('RESPONSE_DELETE_WITHOUT_ARCHIVE', 210);
|
||||
define('RESPONSE_ITEM_NO_DELETED', 211);
|
||||
define('RESPONSE_AUTHENTICATION_ACCEPTED', 212);
|
||||
define('RESPONSE_CHUNKED_ITEM_ACCEPTED_AND_BUFFERED', 213);
|
||||
define('RESPONSE_OPERATION_CANCELLED', 214);
|
||||
define('RESPONSE_NO_EXECUTED', 215);
|
||||
define('RESPONSE_ATOMIC_ROLL_BACK_OK', 216);
|
||||
|
||||
define('RESPONSE_MULTIPLE_CHOICES', 300);
|
||||
// Need to change names.
|
||||
// define('RESPONSE_MULTIPLE_CHOICES', 301);
|
||||
// define('RESPONSE_MULTIPLE_CHOICES', 302);
|
||||
// define('RESPONSE_MULTIPLE_CHOICES', 303);
|
||||
// define('RESPONSE_MULTIPLE_CHOICES', 304);
|
||||
define('RESPONSE_USE_PROXY', 305);
|
||||
|
||||
define('RESPONSE_BAD_REQUEST', 400);
|
||||
define('RESPONSE_INVALID_CREDENTIALS', 401);
|
||||
// Need to change names.
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 402);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 403);
|
||||
define('RESPONSE_NOT_FOUND', 404);
|
||||
// Need to change names.
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 405);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 406);
|
||||
define('RESPONSE_MISSING_CREDENTIALS', 407);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 408);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 409);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 410);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 411);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 412);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 413);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 414);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 415);
|
||||
define('RESPONSE_REQUEST_SIZE_TOO_BIG', 416);
|
||||
// Need to change names.
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 417);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 418);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 419);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 420);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 421);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 422);
|
||||
// define('RESPONSE_INVALID_CREDENTIALS', 423);
|
||||
define('RESPONSE_SIZE_MISMATCH', 424);
|
||||
|
||||
define('RESPONSE_COMMAND_FAILED', 500);
|
||||
// Need to change names.
|
||||
// define('RESPONSE_COMMAND_FAILED', 501);
|
||||
// define('RESPONSE_COMMAND_FAILED', 502);
|
||||
// define('RESPONSE_COMMAND_FAILED', 503);
|
||||
// define('RESPONSE_COMMAND_FAILED', 504);
|
||||
// define('RESPONSE_COMMAND_FAILED', 505);
|
||||
// define('RESPONSE_COMMAND_FAILED', 506);
|
||||
// define('RESPONSE_COMMAND_FAILED', 507);
|
||||
// define('RESPONSE_COMMAND_FAILED', 508);
|
||||
// define('RESPONSE_COMMAND_FAILED', 509);
|
||||
// define('RESPONSE_COMMAND_FAILED', 510);
|
||||
// define('RESPONSE_COMMAND_FAILED', 511);
|
||||
// define('RESPONSE_COMMAND_FAILED', 512);
|
||||
// define('RESPONSE_COMMAND_FAILED', 513);
|
||||
// define('RESPONSE_COMMAND_FAILED', 514);
|
||||
// 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_1', 'syncml:syncml1.1');
|
||||
define('NAME_SPACE_URI_METINF', 'syncml:metinf');
|
||||
define('NAME_SPACE_URI_METINF_1_1', 'syncml:metinf1.1');
|
||||
define('NAME_SPACE_URI_DEVINF', 'syncml:devinf');
|
||||
define('NAME_SPACE_URI_DEVINF_1_1', 'syncml:devinf1.1');
|
||||
|
||||
define('CLIENT_SYNC_STARTED', 1);
|
||||
define('CLIENT_SYNC_FINNISHED', 2);
|
||||
define('CLIENT_SYNC_ACKNOWLEDGED', 3);
|
||||
define('SERVER_SYNC_DATA_PENDING', 4);
|
||||
define('SERVER_SYNC_FINNISHED', 5);
|
||||
define('SERVER_SYNC_ACKNOWLEDGED', 6);
|
||||
|
||||
define('MAX_DATA', 19);
|
||||
define('MAX_ENTRIES', 10);
|
||||
|
||||
/**
|
||||
* The Horde_SyncML_State class provides a SyncML state object.
|
||||
*
|
||||
* $Horde: framework/SyncML/SyncML/State.php,v 1.15 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>
|
||||
* @version $Revision$
|
||||
* @since Horde 3.0
|
||||
* @package Horde_SyncML
|
||||
*/
|
||||
class Horde_SyncML_State {
|
||||
|
||||
var $_sessionID;
|
||||
|
||||
var $_verProto;
|
||||
|
||||
var $_msgID;
|
||||
|
||||
var $_targetURI;
|
||||
|
||||
var $_sourceURI;
|
||||
|
||||
var $_version;
|
||||
|
||||
var $_locName;
|
||||
|
||||
var $_password;
|
||||
|
||||
var $_isAuthorized;
|
||||
|
||||
var $_uri;
|
||||
|
||||
var $_uriMeta;
|
||||
|
||||
var $_syncs = array();
|
||||
|
||||
var $_clientAnchorNext = array(); // written to db after successful sync
|
||||
|
||||
var $_serverAnchorLast = array();
|
||||
|
||||
var $_serverAnchorNext = array(); // written to db after successful sync
|
||||
|
||||
var $_clientDeviceInfo = array();
|
||||
|
||||
// array list of changed items, which need to be synced to the client
|
||||
var $_changedItems;
|
||||
|
||||
// array list of deleted items, which need to be synced to the client
|
||||
var $_deletedItems;
|
||||
|
||||
// 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;
|
||||
|
||||
var $_log = array();
|
||||
|
||||
// stores if we received Alert 222 already
|
||||
var $_receivedAlert222 = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of Horde_SyncML_State.
|
||||
*/
|
||||
function Horde_SyncML_State($sourceURI, $locName, $sessionID, $password = false)
|
||||
{
|
||||
$this->setSourceURI($sourceURI);
|
||||
$this->setLocName($locName);
|
||||
$this->setSessionID($sessionID);
|
||||
if ($password) {
|
||||
$this->setPassword($password);
|
||||
}
|
||||
|
||||
$this->isAuthorized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DataTree used as persistence layer for SyncML. The
|
||||
* datatree var should not be a class member of State as State is
|
||||
* stored as a session var. Resource handles (=db connections)
|
||||
* cannot be stored in sessions.
|
||||
*
|
||||
* @return object DataTree The DataTree object.
|
||||
*/
|
||||
function &getDataTree()
|
||||
{
|
||||
$driver = $GLOBALS['conf']['datatree']['driver'];
|
||||
$params = Horde::getDriverConfig('datatree', $driver);
|
||||
$params = array_merge($params, array( 'group' => 'syncml' ));
|
||||
|
||||
return DataTree::singleton($driver, $params);
|
||||
}
|
||||
|
||||
function getLocName()
|
||||
{
|
||||
if(isset($this->_locName))
|
||||
return $this->_locName;
|
||||
else
|
||||
return False;
|
||||
}
|
||||
|
||||
function getSourceURI()
|
||||
{
|
||||
return $this->_sourceURI;
|
||||
}
|
||||
|
||||
function getTargetURI()
|
||||
{
|
||||
return $this->_targetURI;
|
||||
}
|
||||
|
||||
function getVersion()
|
||||
{
|
||||
return $this->_version;
|
||||
}
|
||||
|
||||
function &getAddedItems($_type)
|
||||
{
|
||||
if(isset($this->_addedItems[$_type]))
|
||||
{
|
||||
return $this->_addedItems[$_type];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function &getChangedItems($_type)
|
||||
{
|
||||
if(isset($this->_changedItems[$_type]))
|
||||
{
|
||||
return $this->_changedItems[$_type];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function &getDeletedItems($_type)
|
||||
{
|
||||
if(isset($this->_deletedItems[$_type]))
|
||||
{
|
||||
return $this->_deletedItems[$_type];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMoreDataPending()
|
||||
{
|
||||
return $this->_moreDataPending;
|
||||
}
|
||||
|
||||
function getMsgID()
|
||||
{
|
||||
return $this->_msgID;
|
||||
}
|
||||
|
||||
function setWBXML($wbxml)
|
||||
{
|
||||
$this->_wbxml = $wbxml;
|
||||
}
|
||||
|
||||
function isWBXML()
|
||||
{
|
||||
return !empty($this->_wbxml);
|
||||
}
|
||||
|
||||
function &getSyncStatus()
|
||||
{
|
||||
return $this->_syncStatus;
|
||||
}
|
||||
|
||||
function setAddedItems($_type, $_addedItems)
|
||||
{
|
||||
$this->_addedItems[$_type] = $_addedItems;
|
||||
}
|
||||
|
||||
function setChangedItems($_type, $_changedItems)
|
||||
{
|
||||
$this->_changedItems[$_type] = $_changedItems;
|
||||
}
|
||||
|
||||
function setClientDeviceInfo($clientDeviceInfo)
|
||||
{
|
||||
$this->_clientDeviceInfo = $clientDeviceInfo;
|
||||
}
|
||||
|
||||
function setDeletedItems($_type, $_deletedItems)
|
||||
{
|
||||
$this->_deletedItems[$_type] = $_deletedItems;
|
||||
}
|
||||
|
||||
function setMoreDataPending($_state)
|
||||
{
|
||||
$this->_moreDataPending = $_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for property msgID.
|
||||
* @param msgID New value of property msgID.
|
||||
*/
|
||||
function setMsgID($msgID)
|
||||
{
|
||||
$this->_msgID = $msgID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for property locName.
|
||||
* @param locName New value of property locName.
|
||||
*/
|
||||
function setLocName($locName)
|
||||
{
|
||||
$this->_locName = $locName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for property locName.
|
||||
* @param locName New value of property locName.
|
||||
*/
|
||||
function setPassword($password)
|
||||
{
|
||||
$this->_password = $password;
|
||||
}
|
||||
|
||||
function setSourceURI($sourceURI)
|
||||
{
|
||||
$this->_sourceURI = $sourceURI;
|
||||
}
|
||||
|
||||
function setSyncStatus($_syncStatus)
|
||||
{
|
||||
#Horde::logMessage('SyncML: syncState set to ==> ' . $_syncStatus, __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
$this->_syncStatus = $_syncStatus;
|
||||
}
|
||||
|
||||
function setTargetURI($targetURI)
|
||||
{
|
||||
$this->_targetURI = $targetURI;
|
||||
}
|
||||
|
||||
function setVersion($version)
|
||||
{
|
||||
$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 {
|
||||
$this->_uri = NAME_SPACE_URI_SYNCML_1_1;
|
||||
$this->_uriMeta = NAME_SPACE_URI_METINF_1_1;
|
||||
$this->_uriDevInf = NAME_SPACE_URI_DEVINF_1_1;
|
||||
}
|
||||
}
|
||||
|
||||
function setSessionID($sessionID)
|
||||
{
|
||||
$this->_sessionID = $sessionID;
|
||||
}
|
||||
|
||||
function isAuthorized()
|
||||
{
|
||||
if (!$this->_isAuthorized) {
|
||||
|
||||
if(strstr($this->_locName,'@') === False)
|
||||
{
|
||||
$this->_locName .= '@'.$GLOBALS['phpgw_info']['server']['default_domain'];
|
||||
}
|
||||
|
||||
#Horde::logMessage('SyncML: Authenticate ' . $this->_locName . ' - ' . $this->_password, __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
|
||||
if($GLOBALS['sessionid'] = $GLOBALS['phpgw']->session->create($this->_locName,$this->_password,'text','u'))
|
||||
{
|
||||
$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: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// store sessionID in a variable, because ->verify maybe resets that value
|
||||
$sessionID = session_id();
|
||||
if(!$GLOBALS['phpgw']->session->verify($sessionID, 'staticsyncmlkp3'))
|
||||
Horde::logMessage('SyncML_EGW: egw session('.$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
}
|
||||
|
||||
return $this->_isAuthorized;
|
||||
}
|
||||
|
||||
function clearSync($target)
|
||||
{
|
||||
unset($this->_syncs[$target]);
|
||||
}
|
||||
|
||||
function setSync($target, $sync)
|
||||
{
|
||||
$this->_syncs[$target] = $sync;
|
||||
}
|
||||
|
||||
function getSync($target)
|
||||
{
|
||||
if (isset($this->_syncs[$target])) {
|
||||
return $this->_syncs[$target];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getTargets()
|
||||
{
|
||||
if(count($this->_syncs) < 1)
|
||||
return FALSE;
|
||||
|
||||
foreach($this->_syncs as $target => $sync)
|
||||
{
|
||||
$targets[] = $target;
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
function getURI()
|
||||
{
|
||||
/*
|
||||
* The non WBXML devices (notably P900 and Sync4j seem to get confused
|
||||
* by a <SyncML xmlns="syncml:SYNCML1.1"> element. They require
|
||||
* just <SyncML>. So don't use an ns for non wbxml devices.
|
||||
*/
|
||||
if ($this->isWBXML()) {
|
||||
return $this->_uri;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
function getURIMeta()
|
||||
{
|
||||
return $this->_uriMeta;
|
||||
}
|
||||
|
||||
function getURIDevInf()
|
||||
{
|
||||
return $this->_uriDevInf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a Horde 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.
|
||||
*
|
||||
* Remember that the datatree is really a tree disguised as a
|
||||
* table. So to look up the guid above, getId first looks for an
|
||||
* entry 'kronolith' and then for an entry
|
||||
* 0d1b415fc124d3427722e95f0e926b75 with kronolith as parent.
|
||||
*/
|
||||
function getLocID($type, $guid)
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . $type . $guid);
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$gid = $dt->getObjectById($id);
|
||||
if (is_a($gid, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $gid->get('locid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
// Set $locid.
|
||||
$gid = &new DataTreeObject($this->_locName . $this->_sourceURI . $type . $guid);
|
||||
$gid->set('type', $type);
|
||||
$gid->set('locid', $locid);
|
||||
$gid->set('ts', $ts);
|
||||
|
||||
$r = $dt->add($gid);
|
||||
if (is_a($r, 'PEAR_Error')) {
|
||||
// Object already exists: update instead.
|
||||
$r = $dt->updateData($gid);
|
||||
}
|
||||
$this->dieOnError($r, __FILE__, __LINE__);
|
||||
|
||||
// Set $globaluid
|
||||
$lid = &new DataTreeObject($this->_locName . $this->_sourceURI . $type . $locid);
|
||||
$lid->set('globaluid', $guid);
|
||||
$r = $dt->add($lid);
|
||||
if (is_a($r, 'PEAR_Error')) {
|
||||
// object already exists: update instead.
|
||||
$r = $dt->updateData($lid);
|
||||
}
|
||||
$this->dieOnError($r, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->dieOnError($type, __FILE__, __LINE__);
|
||||
$this->dieOnError($locid, __FILE__, __LINE__);
|
||||
$this->dieOnError($locid, __FILE__, __LINE__);
|
||||
$this->dieOnError($this->_locName, __FILE__, __LINE__);
|
||||
$this->dieOnError($this->_sourceURI, __FILE__, __LINE__);
|
||||
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . $type . $locid);
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
$lid = $dt->getObjectById($id);
|
||||
if (is_a($lid, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $lid->get('globaluid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . $type . $guid);
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$gid = $dt->getObjectById($id);
|
||||
if (is_a($gid, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $gid->get('ts');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . $type . $locid);
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid) : nothing to remove", __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
return false;
|
||||
}
|
||||
$lid = $dt->getObjectById($id);
|
||||
$guid = $lid->get('globaluid');
|
||||
Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid) : removing guid:$guid and lid:$lid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
$dt->remove($guid);
|
||||
$dt->remove($lid);
|
||||
|
||||
return $guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should use DevINF information.
|
||||
*/
|
||||
function getPreferedContentType($type)
|
||||
{
|
||||
# if ($type == 'contacts') {
|
||||
# return 'text/x-vcard';
|
||||
# } elseif ($type == 'notes') {
|
||||
# return 'text/x-vnote';
|
||||
# } elseif ($type == 'tasks') {
|
||||
# return 'text/x-vcalendar';
|
||||
# } elseif ($type == 'calendar') {
|
||||
# return 'text/x-vcalendar';
|
||||
# }
|
||||
switch($type) {
|
||||
case 'contacts':
|
||||
return 'text/x-vcard';
|
||||
break;
|
||||
|
||||
case 'sifcalendar':
|
||||
case './sifcalendar':
|
||||
return 'text/x-s4j-sife';
|
||||
break;
|
||||
|
||||
case 'sifcontacts':
|
||||
case './sifcontacts':
|
||||
return 'text/x-s4j-sifc';
|
||||
break;
|
||||
|
||||
case 'siftasks':
|
||||
case './siftasks':
|
||||
return 'text/x-s4j-sift';
|
||||
break;
|
||||
|
||||
case 'notes':
|
||||
return 'text/x-vnote';
|
||||
break;
|
||||
|
||||
case 'tasks':
|
||||
return 'text/x-vcalendar';
|
||||
break;
|
||||
|
||||
case 'calendar':
|
||||
return 'text/x-vcalendar';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred contenttype of the client for the given
|
||||
* sync data type (database).
|
||||
*
|
||||
* This is passed as an option to the Horde API export functions.
|
||||
*/
|
||||
function getPreferedContentTypeClient($_sourceLocURI)
|
||||
{
|
||||
$deviceInfo = $this->getClientDeviceInfo();
|
||||
|
||||
if(isset($deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']))
|
||||
{
|
||||
return array('ContentType' => $deviceInfo['dataStore'][$_sourceLocURI]['rxPreference']['contentType']);
|
||||
}
|
||||
|
||||
Horde::logMessage('SyncML: sourceLocURI ' . $_sourceLocURI .' not found', __FILE__, __LINE__, PEAR_LOG_DEBUG);
|
||||
return PEAR::raiseError(_('sourceLocURI not found'));
|
||||
# elseif ($type == 'contacts') {
|
||||
# return 'text/x-vcard';
|
||||
# } elseif ($type == 'notes') {
|
||||
# return array('ContentType' => 'text/x-vnote',
|
||||
# 'ENCODING' => 'QUOTED-PRINTABLE',
|
||||
# 'CHARSET' => 'UTF-8');
|
||||
# } elseif ($type == 'tasks') {
|
||||
# return 'text/x-vcalendar';
|
||||
# } elseif ($type == 'calendar') {
|
||||
# return array('ContentType' => 'text/x-vcalendar',
|
||||
# 'ENCODING' => 'QUOTED-PRINTABLE',
|
||||
# 'CHARSET' => 'UTF-8');
|
||||
# }
|
||||
}
|
||||
|
||||
function setClientAnchorNext($type, $a)
|
||||
{
|
||||
$this->_clientAnchorNext[$type] = $a;
|
||||
}
|
||||
|
||||
function setServerAnchorLast($type, $a)
|
||||
{
|
||||
$this->_serverAnchorLast[$type] = $a;
|
||||
}
|
||||
|
||||
function setServerAnchorNext($type, $a)
|
||||
{
|
||||
$this->_serverAnchorNext[$type] = $a;
|
||||
}
|
||||
|
||||
function getClientAnchorNext($type)
|
||||
{
|
||||
return $this->_clientAnchorNext[$type];
|
||||
}
|
||||
|
||||
function getServerAnchorNext($type)
|
||||
{
|
||||
return $this->_serverAnchorNext[$type];
|
||||
}
|
||||
|
||||
function getServerAnchorLast($type)
|
||||
{
|
||||
return $this->_serverAnchorLast[$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)
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . $type . 'syncSummary');
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $dt->getObjectById($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$id = $dt->getId($this->_locName . $this->_sourceURI . 'deviceInfo');
|
||||
if (is_a($id, 'PEAR_Error')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$info = $dt->getObjectById($id);
|
||||
|
||||
return $info->get('ClientDeviceInfo');
|
||||
}
|
||||
|
||||
/**
|
||||
* write clients device info to database
|
||||
*/
|
||||
function writeClientDeviceInfo()
|
||||
{
|
||||
if (!isset($this->_clientDeviceInfo) || !is_array($this->_clientDeviceInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
$s = $this->_locName . $this->_sourceURI . 'deviceInfo';
|
||||
|
||||
// Set $locid.
|
||||
$info = &new DataTreeObject($s);
|
||||
$info->set('ClientDeviceInfo', $this->_clientDeviceInfo);
|
||||
$r = $dt->add($info);
|
||||
if (is_a($r, 'PEAR_Error')) {
|
||||
// Object already exists: update instead.
|
||||
$dt->updateData($info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (!isset($this->_serverAnchorNext) || !is_array($this->_serverAnchorNext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dt = &$this->getDataTree();
|
||||
|
||||
foreach (array_keys($this->_serverAnchorNext) as $type) {
|
||||
$s = $this->_locName . $this->_sourceURI . $type . 'syncSummary';
|
||||
|
||||
// Set $locid.
|
||||
$info = &new DataTreeObject($s);
|
||||
$info->set('ClientAnchor', $this->_clientAnchorNext);
|
||||
$info->set('ServerAnchor', $this->_serverAnchorNext);
|
||||
$r = $dt->add($info);
|
||||
if (is_a($r, 'PEAR_Error')) {
|
||||
// Object already exists: update instead.
|
||||
$dt->updateData($info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The log simply counts the entries for each topic.
|
||||
*/
|
||||
function log($topic)
|
||||
{
|
||||
if (isset($this->_log[$topic])) {
|
||||
$this->_log[$topic] += 1;
|
||||
} else {
|
||||
$this->_log[$topic] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Log is an array where the key is the event name and the
|
||||
* value says how often this event occured.
|
||||
*/
|
||||
function getLog()
|
||||
{
|
||||
return $this->_log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the content.
|
||||
*
|
||||
* Currently strips uid (primary key) information as client and
|
||||
* server might use different ones.
|
||||
*
|
||||
* Charset conversions might be added here too.
|
||||
*/
|
||||
function convertClient2Server($content, $contentType)
|
||||
{
|
||||
switch ($contentType) {
|
||||
case 'text/calendar':
|
||||
case 'text/x-icalendar':
|
||||
case 'text/x-vcalendar':
|
||||
case 'text/x-vevent':
|
||||
case 'text/x-vtodo':
|
||||
$content = preg_replace('/^UID:.*\n/m', '', $content, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the content.
|
||||
*
|
||||
* Currently strips uid (primary key) information as client and
|
||||
* server might use different ones.
|
||||
*
|
||||
* Charset conversions might be added here too.
|
||||
*/
|
||||
function convertServer2Client($content, $contentType)
|
||||
{
|
||||
switch ($contentType) {
|
||||
case 'text/calendar':
|
||||
case 'text/x-icalendar':
|
||||
case 'text/x-vcalendar':
|
||||
case 'text/x-vevent':
|
||||
case 'text/x-vtodo':
|
||||
$content = preg_replace('/^UID:.*\n/m', '', $content, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: utf8 really should be fine. But the P900 seems to
|
||||
// expect ISO 8559 even when <?xml version="1.0"
|
||||
// encoding="utf-8"> is specified.
|
||||
//
|
||||
// So at least make this dependant on the device information.
|
||||
return utf8_decode($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* When True, Task Item changes (NAG) are sent to the server
|
||||
* during "calendar" Syncs. That's the way the P800/900 handles
|
||||
* things. Should be retrieved from devinf?
|
||||
*/
|
||||
function handleTasksInCalendar()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a small helper function that can be included to check
|
||||
* whether a given $obj is a PEAR_Error or not. If so, it logs
|
||||
* to debug, var_dumps the $obj and exits.
|
||||
*/
|
||||
function dieOnError($obj, $file = __FILE__, $line = __LINE__)
|
||||
{
|
||||
if (!is_a($obj, 'PEAR_Error')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Horde::logMessage('SyncML: PEAR Error: ' . $obj->getMessage(), $file, $line, PEAR_LOG_ERR);
|
||||
print "PEAR ERROR\n\n";
|
||||
var_dump($obj);
|
||||
exit;
|
||||
}
|
||||
|
||||
function getAlert222Received() {
|
||||
return $this->_receivedAlert222;
|
||||
}
|
||||
|
||||
function setAlert222Received($_status) {
|
||||
$this->_receivedAlert222 = (bool)$_status;
|
||||
}
|
||||
|
||||
}
|
668
phpgwapi/inc/horde/XML/WBXML/Decoder.php
Normal file
668
phpgwapi/inc/horde/XML/WBXML/Decoder.php
Normal file
@ -0,0 +1,668 @@
|
||||
<?php
|
||||
|
||||
include_once 'XML/WBXML.php';
|
||||
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 $
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @package XML_WBXML
|
||||
*/
|
||||
class XML_WBXML_Decoder extends XML_WBXML_ContentHandler {
|
||||
|
||||
/**
|
||||
* Document Public Identifier type
|
||||
* 1 mb_u_int32 well known type
|
||||
* 2 string table
|
||||
* from spec but converted into a string.
|
||||
*
|
||||
* Document Public Identifier
|
||||
* Used with dpiType.
|
||||
*/
|
||||
var $_dpi;
|
||||
|
||||
/**
|
||||
* String table as defined in 5.7
|
||||
*/
|
||||
var $_stringTable = array();
|
||||
|
||||
/**
|
||||
* Content handler.
|
||||
* Currently just outputs raw XML.
|
||||
*/
|
||||
var $_ch;
|
||||
|
||||
var $_tagDTD;
|
||||
|
||||
var $_prevAttributeDTD;
|
||||
|
||||
var $_attributeDTD;
|
||||
|
||||
/**
|
||||
* State variables.
|
||||
*/
|
||||
var $_tagStack = array();
|
||||
var $_isAttribute;
|
||||
var $_isData = false;
|
||||
|
||||
var $_error = false;
|
||||
|
||||
/**
|
||||
* The DTD Manager.
|
||||
*
|
||||
* @var XML_WBXML_DTDManager
|
||||
*/
|
||||
var $_dtdManager;
|
||||
|
||||
/**
|
||||
* The string position.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $_strpos;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
function XML_WBXML_Decoder()
|
||||
{
|
||||
$this->_dtdManager = &new XML_WBXML_DTDManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the contentHandler that will receive the output of the
|
||||
* decoding.
|
||||
*
|
||||
* @param XML_WBXML_ContentHandler $ch The contentHandler
|
||||
*/
|
||||
function setContentHandler(&$ch) {
|
||||
$this->_ch = &$ch;
|
||||
}
|
||||
/**
|
||||
* Return one byte from the input stream.
|
||||
*
|
||||
* @param string $input The WBXML input string.
|
||||
*/
|
||||
function getByte($input)
|
||||
{
|
||||
$value = $input{$this->_strpos++};
|
||||
$value = ord($value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a WBXML input document and returns decoded XML.
|
||||
* However the preferred and more effecient method is to
|
||||
* use decode() rather than decodeToString() and have an
|
||||
* appropriate contentHandler deal with the decoded data.
|
||||
*
|
||||
* @param string $wbxml The WBXML document to decode.
|
||||
*
|
||||
* @return string The decoded XML document.
|
||||
*/
|
||||
function decodeToString($wbxml)
|
||||
{
|
||||
$this->_ch = &new XML_WBXML_ContentHandler();
|
||||
|
||||
$r = $this->decode($wbxml);
|
||||
if (is_a($r, 'PEAR_Error')) {
|
||||
return $r;
|
||||
}
|
||||
return $this->_ch->getOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a WBXML input document and decodes it.
|
||||
* Decoding result is directly passed to the contentHandler.
|
||||
* A contenthandler must be set using setContentHandler
|
||||
* prior to invocation of this method
|
||||
*
|
||||
* @param string $wbxml The WBXML document to decode.
|
||||
*
|
||||
* @return mixed True on success or PEAR_Error.
|
||||
*/
|
||||
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)) {
|
||||
return $this->raiseError('No Contenthandler defined.');
|
||||
}
|
||||
|
||||
// Get Version Number from Section 5.4
|
||||
// version = u_int8
|
||||
// currently 1, 2 or 3
|
||||
$this->_wbxmlVersion = $this->getVersionNumber($wbxml);
|
||||
|
||||
// Get Document Public Idetifier from Section 5.5
|
||||
// publicid = mb_u_int32 | (zero index)
|
||||
// zero = u_int8
|
||||
// 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);
|
||||
|
||||
// Get String Table from 5.7
|
||||
// strb1 = length *byte
|
||||
$this->retrieveStringTable($wbxml);
|
||||
|
||||
// Get Document Public Idetifier from Section 5.5.
|
||||
$this->_dpi = $this->getDocumentPublicIdentifierImpl($dpiStruct['dpiType'],
|
||||
$dpiStruct['dpiNumber'],
|
||||
$this->_stringTable);
|
||||
|
||||
// Now the real fun begins.
|
||||
// From Sections 5.2 and 5.8
|
||||
|
||||
|
||||
// Default content handler.
|
||||
$this->_dtdManager = &new XML_WBXML_DTDManager();
|
||||
|
||||
// Get the starting DTD.
|
||||
$this->_tagDTD = $this->_dtdManager->getInstance($this->_dpi);
|
||||
|
||||
if (!$this->_tagDTD) {
|
||||
return $this->raiseError('No DTD found for '
|
||||
. $this->_dpi . '/'
|
||||
. $dpiStruct['dpiNumber']);
|
||||
}
|
||||
|
||||
$this->_attributeDTD = $this->_tagDTD;
|
||||
|
||||
while (empty($this->_error) && $this->_strpos < strlen($wbxml)) {
|
||||
$this->_decode($wbxml);
|
||||
}
|
||||
if (!empty($this->_error)) {
|
||||
return $this->_error;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getVersionNumber($input)
|
||||
{
|
||||
return $this->getByte($input);
|
||||
}
|
||||
|
||||
function getDocumentPublicIdentifier($input)
|
||||
{
|
||||
$i = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
|
||||
if ($i == 0) {
|
||||
return array('dpiType' => 2,
|
||||
'dpiNumber' => $this->getByte($input));
|
||||
} else {
|
||||
return array('dpiType' => 1,
|
||||
'dpiNumber' => $i);
|
||||
}
|
||||
}
|
||||
|
||||
function getDocumentPublicIdentifierImpl($dpiType, $dpiNumber)
|
||||
{
|
||||
if ($dpiType == 1) {
|
||||
return XML_WBXML::getDPIString($dpiNumber);
|
||||
} else {
|
||||
return $this->getStringTableEntry($dpiNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character encoding. Only default character
|
||||
* encodings from J2SE are supported. From
|
||||
* http://www.iana.org/assignments/character-sets and
|
||||
* http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/Charset.html
|
||||
*/
|
||||
function getCharset($input)
|
||||
{
|
||||
$cs = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
|
||||
return XML_WBXML::getCharsetString($cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* starting position of the (null terminated)
|
||||
* string in this table.
|
||||
*/
|
||||
function retrieveStringTable($input)
|
||||
{
|
||||
$size = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
|
||||
$this->_stringTable = substr($input, $this->_strpos, $size);
|
||||
$this->_strpos += $size;
|
||||
// print "stringtable($size):" . $this->_stringTable ."\n";
|
||||
}
|
||||
|
||||
function getStringTableEntry($index)
|
||||
{
|
||||
if ($index >= strlen($this->_stringTable)) {
|
||||
$this->_error =
|
||||
$this->_ch->raiseError('Invalid offset ' . $index
|
||||
. ' value encountered around position '
|
||||
. $this->_strpos
|
||||
. '. Broken wbxml?');
|
||||
return '';
|
||||
}
|
||||
|
||||
// copy of method termstr but without modification of this->_strpos
|
||||
|
||||
$str = '#'; // must start with nonempty string to allow array access
|
||||
|
||||
$i = 0;
|
||||
$ch = $this->_stringTable[$index++];
|
||||
if (ord($ch) == 0) {
|
||||
return ''; // don't return '#'
|
||||
}
|
||||
|
||||
while (ord($ch) != 0) {
|
||||
$str[$i++] = $ch;
|
||||
if ($index >= strlen($this->_stringTable)) {
|
||||
break;
|
||||
}
|
||||
$ch = $this->_stringTable[$index++];
|
||||
}
|
||||
// print "string table entry: $str\n";
|
||||
return $str;
|
||||
|
||||
}
|
||||
|
||||
function _decode($input)
|
||||
{
|
||||
$token = $this->getByte($input);
|
||||
$str = '';
|
||||
|
||||
#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:
|
||||
// Section 5.8.4.1
|
||||
$str = $this->termstr($input);
|
||||
$this->_ch->characters($str);
|
||||
// print "str:$str\n"; // @TODO Remove debug code
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_STR_T:
|
||||
// Section 5.8.4.1
|
||||
$x = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
|
||||
$str = $this->getStringTableEntry($x);
|
||||
$this->_ch->characters($str);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_2:
|
||||
// Section 5.8.4.2
|
||||
$str = $this->termstr($input);
|
||||
$this->_ch->characters($str);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_2:
|
||||
// Section 5.8.4.2
|
||||
$str = $this->getStringTableEnty(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
$this->_ch->characters($str);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_2:
|
||||
// Section 5.8.4.2
|
||||
$extension = $this->getByte($input);
|
||||
$this->_ch->characters($extension);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_ENTITY:
|
||||
// Section 5.8.4.3
|
||||
// UCS-4 chracter encoding?
|
||||
$entity = $this->entity(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
|
||||
$this->_ch->characters('&#' . $entity . ';');
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_PI:
|
||||
// Section 5.8.4.4
|
||||
// throw new IOException
|
||||
// die("WBXML global token processing instruction(PI, " + token + ") is unsupported!\n");
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_LITERAL:
|
||||
// Section 5.8.4.5
|
||||
$str = $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
$this->parseTag($input, $str, false, false);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_LITERAL_A:
|
||||
// Section 5.8.4.5
|
||||
$str = $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
$this->parseTag($input, $str, true, false);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_LITERAL_AC:
|
||||
// Section 5.8.4.5
|
||||
$str = $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
$this->parseTag($input, $string, true, true);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_LITERAL_C:
|
||||
// Section 5.8.4.5
|
||||
$str = $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
$this->parseTag($input, $str, false, true);
|
||||
break;
|
||||
|
||||
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 = substr($input, $this->_strpos, $size);
|
||||
#$b = mb_substr($input, $this->_strpos, $size, 'ISO-8859-1');
|
||||
$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.
|
||||
|
||||
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;
|
||||
}
|
||||
// */
|
||||
// $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) {
|
||||
// ((OpaqueContentHandler)contentHandler).opaque(b);
|
||||
// } else {
|
||||
// String str = new String(b, 0, size, charset);
|
||||
// char[] chars = str.toCharArray();
|
||||
|
||||
// contentHandler.characters(chars, 0, chars.length);
|
||||
// }
|
||||
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_END:
|
||||
// Section 5.8.4.7.1
|
||||
$str = $this->endTag();
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_SWITCH_PAGE:
|
||||
// Section 5.8.4.7.2
|
||||
$codePage = $this->getByte($input);
|
||||
// print "switch to codepage $codePage\n"; // @todo: remove debug code
|
||||
$this->switchElementCodePage($codePage);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Section 5.8.2
|
||||
// Section 5.8.3
|
||||
$hasAttributes = (($token & 0x80) != 0);
|
||||
$hasContent = (($token & 0x40) != 0);
|
||||
$realToken = $token & 0x3F;
|
||||
$str = $this->getTag($realToken);
|
||||
|
||||
// print "element:$str\n"; // @TODO Remove debug code
|
||||
$this->parseTag($input, $str, $hasAttributes, $hasContent);
|
||||
|
||||
if ($realToken == 0x0f) {
|
||||
// store if we're inside a Data tag. This may contain
|
||||
// an additional enclosed wbxml document on which we have
|
||||
// to run a seperate encoder
|
||||
$this->_isData = true;
|
||||
} else {
|
||||
$this->_isData = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function parseTag($input, $tag, $hasAttributes, $hasContent)
|
||||
{
|
||||
$attrs = array();
|
||||
if ($hasAttributes) {
|
||||
$attrs = $this->getAttributes($input);
|
||||
}
|
||||
|
||||
$this->_ch->startElement($this->getCurrentURI(), $tag, $attrs);
|
||||
|
||||
if ($hasContent) {
|
||||
// FIXME I forgot what does this does. Not sure if this is
|
||||
// right?
|
||||
$this->_tagStack[] = $tag;
|
||||
} else {
|
||||
$this->_ch->endElement($this->getCurrentURI(), $tag);
|
||||
}
|
||||
}
|
||||
|
||||
function endTag()
|
||||
{
|
||||
if (count($this->_tagStack)) {
|
||||
$tag = array_pop($this->_tagStack);
|
||||
} else {
|
||||
$tag = 'Unknown';
|
||||
}
|
||||
|
||||
$this->_ch->endElement($this->getCurrentURI(), $tag);
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
function getAttributes($input)
|
||||
{
|
||||
$this->startGetAttributes();
|
||||
$hasMoreAttributes = true;
|
||||
|
||||
$attrs = array();
|
||||
$attr = null;
|
||||
$value = null;
|
||||
$token = null;
|
||||
|
||||
while ($hasMoreAttributes) {
|
||||
$token = $this->getByte($input);
|
||||
|
||||
switch ($token) {
|
||||
// Attribute specified.
|
||||
case XML_WBXML_GLOBAL_TOKEN_LITERAL:
|
||||
// Section 5.8.4.5
|
||||
if (isset($attr)) {
|
||||
$attrs[] = array('attribute' => $attr,
|
||||
'value' => $value);
|
||||
}
|
||||
|
||||
$attr = $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
break;
|
||||
|
||||
// Value specified.
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_I_2:
|
||||
// Section 5.8.4.2
|
||||
$value .= $this->termstr($input);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_T_2:
|
||||
// Section 5.8.4.2
|
||||
$value .= $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_0:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_1:
|
||||
case XML_WBXML_GLOBAL_TOKEN_EXT_2:
|
||||
// Section 5.8.4.2
|
||||
$value .= $input[$this->_strpos++];
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_ENTITY:
|
||||
// Section 5.8.4.3
|
||||
$value .= $this->entity(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_STR_I:
|
||||
// Section 5.8.4.1
|
||||
$value .= $this->termstr($input);
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_STR_T:
|
||||
// Section 5.8.4.1
|
||||
$value .= $this->getStringTableEntry(XML_WBXML::MBUInt32ToInt($input, $this->_strpos));
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_OPAQUE:
|
||||
// Section 5.8.4.6
|
||||
$size = XML_WBXML::MBUInt32ToInt($input, $this->_strpos);
|
||||
$b = substr($input, $this->_strpos, $this->_strpos + $size);
|
||||
$this->_strpos += $size;
|
||||
|
||||
$value .= $b;
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_END:
|
||||
// Section 5.8.4.7.1
|
||||
$hasMoreAttributes = false;
|
||||
if (isset($attr)) {
|
||||
$attrs[] = array('attribute' => $attr,
|
||||
'value' => $value);
|
||||
}
|
||||
break;
|
||||
|
||||
case XML_WBXML_GLOBAL_TOKEN_SWITCH_PAGE:
|
||||
// Section 5.8.4.7.2
|
||||
$codePage = $this->getByte($input);
|
||||
if (!$this->_prevAttributeDTD) {
|
||||
$this->_prevAttributeDTD = $this->_attributeDTD;
|
||||
}
|
||||
|
||||
$this->switchAttributeCodePage($codePage);
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($token > 128) {
|
||||
if (isset($attr)) {
|
||||
$attrs[] = array('attribute' => $attr,
|
||||
'value' => $value);
|
||||
}
|
||||
$attr = $this->_attributeDTD->toAttribute($token);
|
||||
} else {
|
||||
// Value.
|
||||
$value .= $this->_attributeDTD->toAttribute($token);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->_prevAttributeDTD) {
|
||||
$this->_attributeDTD = $this->_prevAttributeDTD;
|
||||
$this->_prevAttributeDTD = false;
|
||||
}
|
||||
|
||||
$this->stopGetAttributes();
|
||||
}
|
||||
|
||||
function startGetAttributes()
|
||||
{
|
||||
$this->_isAttribute = true;
|
||||
}
|
||||
|
||||
function stopGetAttributes()
|
||||
{
|
||||
$this->_isAttribute = false;
|
||||
}
|
||||
|
||||
function getCurrentURI()
|
||||
{
|
||||
if ($this->_isAttribute) {
|
||||
return $this->_tagDTD->getURI();
|
||||
} else {
|
||||
return $this->_attributeDTD->getURI();
|
||||
}
|
||||
}
|
||||
|
||||
function writeString($str)
|
||||
{
|
||||
$this->_ch->characters($str);
|
||||
}
|
||||
|
||||
function getTag($tag)
|
||||
{
|
||||
// Should know which state it is in.
|
||||
return $this->_tagDTD->toTagStr($tag);
|
||||
}
|
||||
|
||||
function getAttribute($attribute)
|
||||
{
|
||||
// Should know which state it is in.
|
||||
$this->_attributeDTD->toAttributeInt($attribute);
|
||||
}
|
||||
|
||||
function switchElementCodePage($codePage)
|
||||
{
|
||||
$this->_tagDTD = &$this->_dtdManager->getInstance($this->_tagDTD->toCodePageStr($codePage));
|
||||
$this->switchAttributeCodePage($codePage);
|
||||
}
|
||||
|
||||
function switchAttributeCodePage($codePage)
|
||||
{
|
||||
$this->_attributeDTD = &$this->_dtdManager->getInstance($this->_attributeDTD->toCodePageStr($codePage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hex version of the base 10 $entity.
|
||||
*/
|
||||
function entity($entity)
|
||||
{
|
||||
return dechex($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a null terminated string.
|
||||
*/
|
||||
function termstr($input)
|
||||
{
|
||||
$str = '#'; // must start with nonempty string to allow array access
|
||||
$i = 0;
|
||||
$ch = $input[$this->_strpos++];
|
||||
if (ord($ch) == 0) {
|
||||
return ''; // don't return '#'
|
||||
}
|
||||
while (ord($ch) != 0) {
|
||||
$str[$i++] = $ch;
|
||||
$ch = $input[$this->_strpos++];
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user