/*******************************************************************

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

********************************************************************/

#include "SctsDevManT&D.h"
#include "SctsAuth.h"
#include "SctsDevManDevice.h"

/*
 * Change to support TNDS. 
 */
char SctsDMFormatString[12][6]={"chr","node","null","bin","bool","b64","int","xml","date","time","float","None"};
char SctsDMNodePropertyString[12][11]={"ACL","Format","Name","Title","TStamp","Type","Size","VerNo","Struct","StructData","TNDS","None"};
char SctsGetTNDSPropertyString[7][9]={"Value","ACL","Format","VerNO","Size","Title","None"};

/*************************************************************
SctsDMNode Class 
**************************************************************/

bool SctsDMNode::VerifyACLSyntax(char *_acl)
{
	/* true - Command, false - ServerID */ 
	bool state=true;
	char *pos=_acl;
	if (!_acl) return true;
	if (sctsStrchr(_acl,' ')) return false;
	while(pos && *pos)
	{
		if (state)
		{
			if(sctsStrncmp(_acl,"Replace=",8) && sctsStrncmp(_acl,"Delete=",7) && sctsStrncmp(_acl,"Get=",4) && sctsStrncmp(_acl,"Exec=",5) && sctsStrncmp(_acl,"Add=",4))
				return false;
			state=false;
			pos=sctsStrchr(_acl,'=');
		}
		else
		{
			pos=pos+sctsStrcspn(_acl,"+&\0");
			if(!(*pos) && (sctsStrchr(_acl,'='))) return false; //Replace=*Delete=Raj
			if((_acl=sctsStrchr(_acl,'*')) && _acl<pos && ((pos-_acl!=1) || ((*(_acl-1)!='=') && (*(_acl-1)!='+')))) return false; //Replace=*rajkiran || Replace=Raj*kiran || Replace=Rajkiran*
			if (*(pos-1)=='=' || *(pos-1)=='&' || *(pos-1)=='+') return false;
			if (*pos=='&') state=true;
		}
		if (*pos) _acl=++pos;
	}
	return true;
}

bool SctsDMNode::VerifyACL(char *serverID, SctsCommandType command, bool checkParent,bool *grantable)
{
	char *startPos,*endPos;
	char *_acl;
	char endChar;
	char searchStr1[4]="ce=";
	char searchStr2[100];
	bool verified=false;
	bool *flag=&verified;
	SctsDMNode *node=this;
	SCTSASSERT(!grantable || checkParent);
	if (grantable) *grantable=false;
	
	/* VerifyACL exception */
	if (serverID==SIDX) return true;

	/* Generate search strings */
	switch(command)
	{
	case SCTS_DELETE:
		{
			searchStr1[0]='t';
			break;
		}
	case SCTS_GET:
		{
			searchStr1[0]='e';
			searchStr1[1]='t';
			break;
		}
	case SCTS_EXEC:
		{
			searchStr1[0]='e';
			searchStr1[1]='c';
			break;
		}
	case SCTS_ADD:
		{
			searchStr1[0]='d';
			searchStr1[1]='d';
			break;
		}
	case SCTS_REPLACE:
		break;
	default:
		return false;
	}
	sprintf(searchStr2,"+%s+",serverID);

	do
	{
		if (!node->acl)
		{
			node=node->parent;
			continue;
		}
		_acl=(char *)sctsMalloc(sctsStrlen(node->acl)+2);
		sctsStrcpy(_acl,node->acl);
		sctsStrcat(_acl,"&");
		endPos=_acl;
		while ((startPos=sctsStrstr(endPos,searchStr1)) && !(*flag))
		{
			endPos=startPos+sctsStrcspn(startPos,"&\0");
			*endPos='+';
			endChar=*(++endPos);
			*endPos=0;
			startPos[2]='+';
			if (sctsStrchr(startPos,'*') || sctsStrstr(startPos,searchStr2)) *flag=true;
			*endPos=endChar;
		}
		sctsFree(_acl);
		if (flag!=grantable) 
		{
			flag=grantable;
			if (flag)
			{
				searchStr1[0]='c';
				searchStr1[1]='e';
			}
		}
		node=node->parent;
	}
	while (node && checkParent && flag && !(*flag));
	
	return verified;
}

char *SctsDMNode::GetACL(bool checkParent)
{
	char *_acl=0;
	SctsDMNode *node=this;
	do
	{
		if (!node->acl)
		{
			node=node->parent;
			continue;
		}
        _acl=node->acl;
        break;
	}
	while (node && checkParent);
	
	return _acl;
}

bool SctsDMNode::IsParent(SctsDMNode *_parent)
{
	SctsDMNode *iterator;
	if (!parent) return false;
	for (iterator=parent;iterator && iterator!=_parent;iterator=iterator->parent);
	if (!iterator)
		return false;
	else 
		return true;
}

void SctsDMNode::GetPcdata(SctsPcdata &pcData,SctsDMNodeFormat &_format,SctsEncoding encoding)
{
	char *tempStr;
	pcData.rawData=(SmlPcdataPtr_t)sctsMalloc(sizeof(SmlPcdata_t));
	if (!data || !size)
	{
		_format=format;
		if (format!=DMFORMAT_NODE)
			format=_format=DMFORMAT_NULL;
		pcData.rawData->content=0;
		pcData.rawData->contentType=SML_PCDATA_STRING;
		pcData.rawData->length=0;
		return;
	}
	switch(format) 
	{
	// B64 Bug case DMFORMAT_B64:
	case DMFORMAT_BIN:
		if (encoding==XML) //B64 Bug || format==DMFORMAT_B64)
		{
			unsigned int _size=size;
			tempStr=sctsBase64Encode((unsigned char *)data,_size);
			pcData.rawData->content=tempStr;
			pcData.rawData->contentType=SML_PCDATA_STRING;
			pcData.rawData->length=_size;
			_format=DMFORMAT_B64;
			break;
		}
	case DMFORMAT_B64:
	case DMFORMAT_XML:
		pcData.rawData->content=sctsMalloc(sizeof(char)*size);
		sctsMemcpy(pcData.rawData->content,data,size);
		pcData.rawData->contentType=encoding==XML?SML_PCDATA_CDATA:SML_PCDATA_OPAQUE;
		pcData.rawData->length=size;
		_format=format;
		break;
	case DMFORMAT_CHR:
	case DMFORMAT_INT:
    /*
     * Change to support TNDS. 
     */
    case DMFORMAT_FLOAT:
		pcData.rawData->content=sctsStrdup((char *)data);
		pcData.rawData->length=size;
		pcData.rawData->contentType=SML_PCDATA_STRING;
		_format=format;
		break;
	case DMFORMAT_BOOL:
		/* V1.1.1 */
		SCTSASSERT(data);
		pcData.rawData->content=sctsStrdup((char *)data);
		pcData.rawData->length=size;
		pcData.rawData->contentType=SML_PCDATA_STRING;
		_format=DMFORMAT_BOOL;
		break;
	case DMFORMAT_NULL:
		pcData.rawData->content=0;
		pcData.rawData->contentType=SML_PCDATA_STRING;
		pcData.rawData->length=0;
		_format=format;
		break;
    /*
     * Change to support TNDS. 
     */
    case DMFORMAT_DATE:
    case DMFORMAT_TIME:
        pcData.rawData->content=sctsStrdup((char *)data);
        pcData.rawData->length=size;
        pcData.rawData->contentType=SML_PCDATA_STRING;
		_format=format;
		break;
	default:
		SCTSASSERT(false);
	};
}
SctsDMNode::SctsDMNode(SctsDMNode *node)
{
	sctsMemset(this,0,sizeof(SctsDMNode));
	if (node->acl) acl=sctsStrdup(node->acl);
	if (node->name) name=sctsStrdup(node->name);
	if (node->title) title=sctsStrdup(node->title);
	if (node->mimeType) mimeType=sctsStrdup(node->mimeType);
	format=node->format;
	size=node->size;
	version=node->version;
	permanent=node->permanent;
	markedForDelete=node->markedForDelete;
	if(size && node->data)
	{
		data=sctsMalloc(size+1);
		sctsMemcpy(data,node->data,size);
		((char *)data)[size]=0;
	}
	uiData=0;
}	

SctsDMNode::SctsDMNode(char *_name,SctsDMNodeFormat _format,bool _permanent,char *_acl,void *_data,char *_mimeType,unsigned int _size)
{
	sctsMemset(this,0,sizeof(SctsDMNode));
	permanent=_permanent;
	format=_format;
	name=sctsStrdup(_name);
	markedForDelete=false;
	if(_acl)
	{
		if(VerifyACLSyntax(_acl)) 
			acl=sctsStrdup(_acl);
	}
	if(_mimeType) mimeType=sctsStrdup(_mimeType);
	if (_size && _data && format!=DMFORMAT_NULL) 
	{
		/* B64 Bug 
		if (_format==DMFORMAT_B64)
			data=sctsBase64Decode((char *)_data,_size);
		else
		{
			data=sctsMalloc(_size+1);
			sctsMemcpy(data,_data,_size);
			((char *)data)[_size]=0;	
		}
		*/
		data=sctsMalloc(_size+1);
		sctsMemcpy(data,_data,_size);
		((char *)data)[_size]=0;	
		size=_size;
	}	
}

SctsDMNode::SctsDMNode(FILE *fp)
{
	sctsMemset(this,0,sizeof(SctsDMNode));
	name=sctsFReadString(fp);
	acl=sctsFReadString(fp);
	title=sctsFReadString(fp);
	mimeType=sctsFReadString(fp);
	timeStamp=sctsFReadString(fp);
	fread(&permanent,sizeof(bool),1,fp);
	fread(&format,sizeof(unsigned int),1,fp);
	fread(&version,sizeof(unsigned int),1,fp);
	fread(&size,sizeof(unsigned int),1,fp);
	if (size)
	{
		data=sctsMalloc(size+1);
		fread(data,size+1,1,fp);
	}	
}

void SctsDMNode::Save(FILE *fp)
{
	sctsFWriteString(name,fp);
	sctsFWriteString(acl,fp);
	sctsFWriteString(title,fp);
	sctsFWriteString(mimeType,fp);
	sctsFWriteString(timeStamp,fp);
	fwrite(&permanent,sizeof(bool),1,fp);
	fwrite(&format,sizeof(SctsDMNodeFormat),1,fp);
	fwrite(&version,sizeof(unsigned int),1,fp);
	fwrite(&size,sizeof(unsigned int),1,fp);
	if (size)
    {
        if (format == DMFORMAT_CHR)
            fwrite(data,size+1,1,fp);
        else
        {
            unsigned char *tmpBuf = (unsigned char *)sctsMalloc(sizeof(unsigned char)*size+1);
            memset(tmpBuf, 0, sizeof(unsigned char)*size+1);
            sctsMemcpy(tmpBuf, data, sizeof(unsigned char)*size);
            fwrite(tmpBuf,size+1,1,fp);
            sctsFree(tmpBuf);
        }
    }
}

SctsDMNode::~SctsDMNode()
{
	while(child)
	{
		SctsDMNode *temp=child;
		child=child->next;
		delete(temp);
	}
	if(!prev)
	{
		if(parent) parent->child=next; 
		if(next) next->prev=0;
	}
	else
	{
		prev->next=next;
		if(next) next->prev=prev;
	}
	if(acl) sctsFree(acl);
	if(mimeType) sctsFree(mimeType);
	if(timeStamp) sctsFree(timeStamp);
	if(name) sctsFree(name);
	if(title) sctsFree(title);
	if(data) sctsFree(data);
}

/*************************************************************
SctsDMCommand Class 
**************************************************************/

void SctsDMCommand::RollBack()
{
	if (!nodeRef) return;
	switch(command)
	{
	case SCTS_ADD:
		{
			delete nodeRef;
			nodeRef=0;
			break;
		}
	case SCTS_COPY:
		{
			delete nodeRef;
			nodeRef=0;
			break;
		}
	case SCTS_DELETE:
		{
			nodeRef->markedForDelete=false;
			break;
		}
	case SCTS_REPLACE:
		{
			switch (prop)
			{
			case DMPROP_NAME:
				{
					if (nodeRef->name) sctsFree(nodeRef->name);
					nodeRef->name=(char *)data;
					break;
				}
			case DMPROP_TITLE:
				{
					if (nodeRef->title) sctsFree(nodeRef->title);
					nodeRef->title=(char *)data;
					break;
				}
			case DMPROP_ACL:
				{
					if (nodeRef->acl) sctsFree(nodeRef->acl);
					nodeRef->acl=(char *)data;
					break;
				}
			case DMPROP_NONE:
				{
					nodeRef->markedForDelete=((SctsDMNode *)data)->markedForDelete;
					nodeRef->format=((SctsDMNode *)data)->format;
					nodeRef->size=((SctsDMNode *)data)->size;
					nodeRef->version=((SctsDMNode *)data)->version;
					if (nodeRef->data) sctsFree(nodeRef->data);
					if (nodeRef->acl) sctsFree(nodeRef->acl);
					if (nodeRef->title) sctsFree(nodeRef->title);
					if (nodeRef->name) sctsFree(nodeRef->name);
					if (nodeRef->mimeType) sctsFree(nodeRef->mimeType);
					nodeRef->data=(char *)((SctsDMNode *)data)->data;
					nodeRef->acl=((SctsDMNode *)data)->acl;
					nodeRef->title=((SctsDMNode *)data)->title;
					nodeRef->name=((SctsDMNode *)data)->name;
					nodeRef->mimeType= ((SctsDMNode *)data)->mimeType;
					sctsMemset(data,0,sizeof(SctsDMNode));
					delete data;
					break;
				}
			} //End Switch
			data=0;
			break;
		}//End case Replace
	case SCTS_GET:
    /*
     * Change to support Exec. 
     */
	case SCTS_EXEC:
		break;
	default:
		SCTSASSERT(false);
	}// End outer Switch
}

void SctsDMCommand::RollBack(std::list<SctsDMCommand *>& cmdList,std::list<SctsDMCommand *>::iterator cmdListIt)
{
	SctsDMCommand *_command;
	SCTSASSERT(command==SCTS_ADD);
	if (!nodeRef) return;
	for (++cmdListIt;cmdListIt!=cmdList.end();)
	{
		_command=(*cmdListIt);
		/* Delete only commands that have not been sent */
		if (_command->nodeRef && (_command->nodeRef==nodeRef || _command->nodeRef->IsParent(nodeRef)))
		{
			_command->nodeRef=0;
			if (!_command->cmdRef) 
			{
				delete *cmdListIt;
				cmdListIt=cmdList.erase(cmdListIt);
				continue;
			}
		}
		++cmdListIt;
	}
	delete nodeRef;
	nodeRef=0;	
}

SctsDMCommand::SctsDMCommand(SctsCommandType _command,SctsDMNode *_nodeRef,SctsDMNodeProp _prop,bool _reusable,SctsDMCommandSpecific *_specific) 
{
	data=0;
	size=0;
	if (_nodeRef && _command==SCTS_DELETE) _nodeRef->markedForDelete=true;
	if (_nodeRef && _command==SCTS_REPLACE)
	{
		switch (_prop)
		{
		case DMPROP_TITLE:
			if (!_nodeRef->title) break;
			data=sctsStrdup(_nodeRef->title);
			size=sctsStrlen(_nodeRef->title);
			break;
		case DMPROP_ACL:
			if (!_nodeRef->acl) break;
			data=sctsStrdup(_nodeRef->acl);
			_reusable=false;
			size=sctsStrlen(_nodeRef->acl);
			break;
		case DMPROP_NAME:
			if (!_nodeRef->name) break;
			data=sctsStrdup(_nodeRef->name);
			size=sctsStrlen(_nodeRef->name);
			break;
		case DMPROP_NONE:
			data=new SctsDMNode(_nodeRef);
			break;
		default:
			SCTSASSERT(false);
		}
	}
    if (_specific)
    {
        cmdSpecific = *_specific;
    }
	cmdRef=0;
	msgRef=0;
	status=0;
	nodeRef=_nodeRef;
	command=_command;
	prop=_prop;
	reusable=_reusable;
}

SctsDMCommand::SctsDMCommand(FILE *fp)
{
	cmdRef=0;
	msgRef=0;
	status=0;
	fread(&command,sizeof(SctsCommandType),1,fp);
	fread(&prop,sizeof(SctsDMNodeProp),1,fp);
	fread(&reusable,sizeof(bool),1,fp);
	fread(&size,sizeof(unsigned int),1,fp);
	if (fgetc(fp))
	{
		/* Persistence of user interaction alert */
		if (command==SCTS_ALERT)
		{
			char **_data;
			int i;
			data=sctsMalloc(sizeof(char *)*size);
			_data=(char **)data;
			fread(_data,sizeof(char *),1,fp);
			for (i=1;i<size;++i) _data[i]=sctsFReadString(fp);
		}
		else
		{
			if (prop==DMPROP_NONE)
				data=new SctsDMNode(fp);
			else
				data=sctsFReadString(fp);
		}
	}
	else
		data=0;
    char *tmpStr=0;
    tmpStr = sctsFReadString(fp);
    cmdSpecific.correlator = tmpStr?tmpStr:"";
    tmpStr = sctsFReadString(fp);
    cmdSpecific.tndsFields = tmpStr?tmpStr:"";
    tmpStr = sctsFReadString(fp);
    cmdSpecific.copyFrom = tmpStr?tmpStr:"";
}

void SctsDMCommand::Save(FILE *fp)
{
	fwrite(&command,sizeof(SctsCommandType),1,fp);
	fwrite(&prop,sizeof(SctsDMNodeProp),1,fp);
	fwrite(&reusable,sizeof(bool),1,fp);
	fwrite(&size,sizeof(unsigned int),1,fp);
	if (data)
	{
		fputc(1,fp);
		/* Persistence of user interaction alert */
		if (command==SCTS_ALERT)
		{
			char **_data=(char **)data;
			int i;
			fwrite(_data,sizeof(char *),1,fp);
			for (i=1;i<size;++i) sctsFWriteString(_data[i],fp);
		}
		else
		{
			if (prop!=DMPROP_NONE)
				sctsFWriteString((char *)data,fp);
			else
				((SctsDMNode *)data)->Save(fp);
		}
	}	
	else 
    {
		fputc(0,fp);
    }
    sctsFWriteString(cmdSpecific.correlator.length()?(char *)cmdSpecific.correlator.c_str():0,fp);
    sctsFWriteString(cmdSpecific.tndsFields.length()?(char *)cmdSpecific.tndsFields.c_str():0,fp);
    sctsFWriteString(cmdSpecific.copyFrom.length()?(char *)cmdSpecific.copyFrom.c_str():0,fp);
}

SctsDMCommand::~SctsDMCommand()
{
	sctsFree(cmdRef);
	if (data)
	{
		if (command==SCTS_ALERT)
		{
			char **_data=(char **)data;
			int i;
			for (i=1;i<size;++i) sctsFree(_data[i]);
			sctsFree(data);
		}
		else
		{
			if (prop==DMPROP_NONE)
				delete (SctsDMNode *)data;
			else
				sctsFree(data);
		}
	}
}		

/*************************************************************
SctsDMTree Class 
**************************************************************/

SctsDMNode *SctsDMTree::FindDMNode(char *nodeName,SctsDMNode *startNode)
{
	SctsDMNode *current=startNode?startNode:root;
	char *pos;
	char *tempBuffer;
	if (!nodeName) return 0;
	tempBuffer=sctsStrdup(nodeName);
	nodeName=tempBuffer;
	if (nodeName[0]=='.' && nodeName[1] =='/') 
		nodeName+=2;
	else if ((nodeName[0]=='.' || nodeName[0]=='/') && !nodeName[1])
	{
		sctsFree(tempBuffer);
		return current;
	} 
	if (!nodeName[0]) 
	{
		sctsFree(tempBuffer);
		return current;
	}
	do
	{
		pos=sctsStrchr(nodeName,'/');
		if (pos) *pos=0;
		for(current=current->child;current && sctsStrcmp(nodeName,current->name);current=current->next);
		if (!current || current->markedForDelete)
		{
			sctsFree(tempBuffer);
			return 0;
		}
		if (pos) 
		{
			*pos='/';
			nodeName=++pos;
		}
	} while(pos);
	sctsFree(tempBuffer);
	return current;
}
			
char *SctsDMTree::GetPath(SctsDMNode *node,char *path,SctsDMNode *rootNode)
{
	if (node->parent && rootNode!=node)
		path=GetPath(node->parent,path,rootNode);
	else
		return strdup(".");
	path=(char *)sctsRealloc(path,strlen(path)+strlen(node->name)+2);
	strcat(path,"/");
	strcat(path,node->name);
	return path;
}

int SctsDMTree::DeleteDMNode(char *nodeName,char *serverID,SctsDMCommand **command)
{
	SctsDMNode *node=FindDMNode(nodeName);
	if (!node)
	{
		char tempBuffer[500];
		sprintf(tempBuffer,"Delete Failed : Node with path %s not found.",nodeName);
		SctsErrorLog::AppendError(tempBuffer);
		return 404;
	}
	return DeleteDMNode(node,serverID,command);
}

int SctsDMTree::DeleteDMNode(SctsDMNode *node,char *serverID,SctsDMCommand **command)
{
	if (command) *command=0;
	if (!node) return 404;
	
	if (node->permanent) 
	{
		char tempBuffer[500];
		char *name=GetPath(node);	
		sprintf(tempBuffer,"Delete Failed : %s is a permanent node.",name);
		SctsErrorLog::AppendError(tempBuffer);
		return 405;
	}
	if (!node->VerifyACL(serverID,SCTS_DELETE))	return 425;
	if (uiCallback && !node->markedForDelete) (*uiCallback)(this,node,SCTS_DELETE);
	if (command)
		*command=new SctsDMCommand(SCTS_DELETE,node,DMPROP_NONE,true);
	else
		delete(node);
	return 200;
}

int SctsDMTree::AddDMNode(char *_nodeName,char *_serverID,char *_format,bool _permanent,void *_data,char *_mimeType,unsigned int _size,SctsDMNode **node,SctsDMCommand **command)
{
	SctsDMNode *parent,*current;
	SctsDMNodeFormat format=DMFORMAT_CHR;
	char *name;
	char errorMsg[300]="";
	if (command) *command=0;
	if (!_nodeName || !(*_nodeName)) return 500;

	/* Check Format */
	if (_format && *_format)
	{
		while(format != DMFORMAT_NONE)
		{
			if (!sctsStrcmp(_format,SctsDMFormatString[format]))
				break;
			format=(SctsDMNodeFormat)(format+1);
		}
		if (format==DMFORMAT_NONE)
		{
			sprintf(errorMsg,"'%s' is not valid format.",_format);
			SctsErrorLog::AppendError(errorMsg);
			return 500;
		}
	}
	
	if (format==DMFORMAT_NODE)
	{
		if (_size && _data)
		{
			SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (6.2) : Interior node MUST NOT have any data");
			return 500;
		}
	}
	
	/* Get short name and find parent node */
	name=_nodeName+sctsStrlen(_nodeName);
	while (name!=_nodeName && *name!='/') --name;
	if (name==_nodeName)	
		parent=root;
	else
	{
		parent=SctsDMTree::FindDMNode(sctsStrncpy(errorMsg,_nodeName,name-_nodeName));
		++name;
	}
	if (!parent)
	{
		sprintf(errorMsg,"%s is not a valid path or Add access is denied.",_nodeName);
		SctsErrorLog::AppendError(errorMsg);
		return 500;
	}
	if (parent->format==DMFORMAT_NULL)
		parent->format=DMFORMAT_NODE;
	if ((_permanent && !parent->permanent))
	{
		SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (6.2.3) : Permanent nodes MUST NOT be children of dynamic nodes");
		return 405; 
	}
	if (parent->format!=DMFORMAT_NODE)
	{
		SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (6.2) : Leaf nodes cannot have children.");
		return 405; 
	}

	for(current=parent->child;current && (sctsStrcmp(name,current->name) || current->markedForDelete);current=current->next);
	if (current)
	{
		sprintf(errorMsg,"Node with the path %s already exists",_nodeName);
		SctsErrorLog::AppendError(errorMsg);
		return 418;
	}
	if (!parent->VerifyACL(_serverID,SCTS_ADD)) return 425;

	/* Exception : If the node being added is a ServerID in DMAcc/x, check if the
	 serverID already exists. */
	if (!sctsStrcmp(name,"ServerID") && _data)
	{
        if (IsDevice())
        {
            SctsDMDevice *unkDev = 0;
            if (!IsServerTree())
            {
                SctsDMClientDevice *cliDev = (SctsDMClientDevice *)this;
                unkDev = (SctsDMDevice *)cliDev;
            }
            else
            {
                SctsDMServerDevice *srvDev = (SctsDMServerDevice *)this;
                unkDev = (SctsDMDevice *)srvDev;
            }

            SctsDMDevice::MDAccType::iterator it = unkDev->accounts.begin();
            while (it != unkDev->accounts.end())
            {
                std::string serverIDPath = (it->second).accPath;
                SctsDMNode *dmAcc = FindDMNode((char *)serverIDPath.c_str());
		        if (dmAcc && parent->IsParent(dmAcc))
		        {
				    SctsDMNode *serverID=FindDMNode("ServerID",dmAcc);
				    if (serverID && !sctsStrcmp((char *)serverID->data,(char *)_data)) 
				    {
					    sprintf(errorMsg,"Node with the ServerID : %s, exists in the DMAcc %s",_data,dmAcc->name);
					    SctsErrorLog::AppendError(errorMsg);
					    return 500;
				    }
		        }
                ++it;
            }
        }
	}

	if (!name || !sctsStrcmp(name,"") || !sctsStrcmp(name," ") || !sctsStrcmp(name,".") || !sctsStrcmp(name,"/")) 
	{
		SctsErrorLog::AppendError("The node's name is invalide.");
		return 500;
	}

	current= new SctsDMNode(name,format,_permanent,0,_data,_mimeType,_size);
	current->parent=parent;
	if (!parent->child)
		parent->child=current;
	else
	{
		current->next=parent->child;
		parent->child->prev=current;
		parent->child=current;
	}
	if (format==DMFORMAT_NODE && !parent->VerifyACL(_serverID,SCTS_REPLACE))
	{
        /*
         * Bug fix: array boundry read/write. 
         */
		char c='*';
        int aclLen = sctsStrlen("Add=&Replace=&Delete=&Get=") + sctsStrlen(_serverID)*4 + 1;
		current->acl=(char *)sctsMalloc(aclLen);
        memset(current->acl, 0, aclLen);
		if (_serverID==SIDX) _serverID=&c;
		sprintf(current->acl,"Add=%s&Replace=%s&Delete=%s&Get=%s",_serverID,_serverID,_serverID,_serverID);	
	}
	if (node) *node=current;
	if (command) *command=new SctsDMCommand(SCTS_ADD,current,DMPROP_NONE,true);
	if (uiCallback) (*uiCallback)(this,current,SCTS_ADD);
	return 200;
}

int SctsDMTree::ReplaceDMNodeData(char *_nodeName,char *_serverID,void *_data,unsigned int _size,char *_format,char *_mimeType,SctsDMCommand **command)
{
	return ReplaceDMNodeData(FindDMNode(_nodeName),_serverID,_data,_size,_format,_mimeType,command);
}

int SctsDMTree::ReplaceDMNodeData(SctsDMNode *node,char *_serverID,void *_data,unsigned int _size,char *_format,char *_mimeType,SctsDMCommand **command)
{
	SctsDMNodeFormat format=DMFORMAT_CHR;
	char errorMsg[500]="";
	if (command) *command=0;
	if (!node) return 404;
	if (node->name[0]=='/' && !node->name[1]) return 500; 
	/* temp=FindDMNode("./DevInfo");
	if (node->IsParent(temp)) return 405;
	temp=FindDMNode("./DevDetail");
	if (node->IsParent(temp)) return 405; */
	if (node->format==DMFORMAT_NODE && _data) 
	{
		SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (6.2) : Interior node MUST NOT have any data");
		return 500;
	}

	/* Check Format */
	if (_format && *_format)
	{
		for (format=DMFORMAT_CHR;format!=DMFORMAT_NONE && sctsStrcmp(_format,SctsDMFormatString[format]);format=(SctsDMNodeFormat)(format+1));
		if (format==DMFORMAT_NONE)
		{
			sprintf(errorMsg,"'%s' is not valid format.",_format);
			SctsErrorLog::AppendError(errorMsg);
			return 500;
		}
	}
	node->format=format;

	/* Verify the ACL */
	if (!node->VerifyACL(_serverID,SCTS_REPLACE)) return 425;

	if (_mimeType)
	{
		if (node->mimeType) sctsFree(node->mimeType);
		node->mimeType=sctsStrdup(_mimeType);
	}
	if (command) *command=new SctsDMCommand(SCTS_REPLACE,node,DMPROP_NONE,true);
	if (node->data)	sctsFree(node->data);
	node->data=0;
	node->size=0;
	if (node->format!=DMFORMAT_NULL && _size && _data)
	{
/* B64 Bug		if (node->format==DMFORMAT_B64) _data=sctsBase64Decode((char *)_data,_size); */
		node->data=sctsMalloc(_size+1);
		sctsMemcpy(node->data,_data,_size);
		((char *)node->data)[_size]=0;	
		node->size=_size;
/* B64 Bug		if (node->format==DMFORMAT_B64)	sctsFree(_data); */
	}
	else
		node->format=DMFORMAT_NULL;
	if (!IsServerTree())
	{
		++node->version;
		if (node->timeStamp) sctsFree(node->timeStamp);
		node->timeStamp=sctsGetTimeStamp(true);
	}
	if (uiCallback) (*uiCallback)(this,node,SCTS_REPLACE);
	return 200;
}

int SctsDMTree::ReplaceDMNodeProp(char *nodeName,char *serverID,SctsDMNodeProp prop,char *propVal,SctsDMCommand **command)
{
	return ReplaceDMNodeProp(FindDMNode(nodeName),serverID,prop,propVal,command);
}

int SctsDMTree::ReplaceDMNodeProp(SctsDMNode *node,char *serverID,SctsDMNodeProp prop,char *propVal,SctsDMCommand **command)
{
	SctsDMNode *iter;
	char errorMsg[500]="";
	if (command) *command=0;
	if (!node) return 404;
	if (prop!=DMPROP_NAME && prop!=DMPROP_ACL && prop!=DMPROP_TITLE)
	{
		SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (7.5) : Replace is allowed only on Name, ACL and Title properties of the node.");
		return 405;
	}
	if (node->permanent && prop==DMPROP_NAME)
	{
		SctsErrorLog::AppendError("SyncML DM T&D V1.1.2 (7.5.1) : 'Name' MUST NOT be changed for permanent objects");
		return 405;
	}
	if (prop==DMPROP_NAME)
	{
		iter=node->parent->child;
		if (!propVal || sctsStrlen(propVal)<1)
		{
			sprintf(errorMsg,"A node name cannot be blank.",propVal);
			SctsErrorLog::AppendError(errorMsg);
			return 500;
		}
		for(;iter && sctsStrcmp(iter->name,propVal);iter=iter->next);
		if (iter) 
		{
			sprintf(errorMsg,"A node with the name %s already exists",propVal);
			SctsErrorLog::AppendError(errorMsg);
			return 418; 
		}
		if (!node->VerifyACL(serverID,SCTS_REPLACE)) return 425;
		if (command) *command=new SctsDMCommand(SCTS_REPLACE,node,prop,true);
		sctsFree(node->name);
		node->name=sctsStrdup(propVal);
	}
	if (prop==DMPROP_ACL) 
	{
		/* DMEvent V1.1.1 Handle ACL with encoding &amp */
		char *_propVal=0;
		if (propVal && *propVal)
		{
			char *t,*t1;
			_propVal=sctsStrdup(propVal);
			t1=_propVal;	
			while (t=sctsStrstr(t1,"&amp;"))
			{
				*(t+1)=0;
				t+=5;
				sctsStrcpy(_propVal,t1);				
				sctsStrcat(_propVal,t);
				t1=_propVal;
			}
		}
		if (!node->VerifyACLSyntax(_propVal))
		{
			sprintf(errorMsg,"%s is invalid ACL. The ACL should follow the syntax as specified in SyncML DM T&D V1.1.1 (6.7.1.5).",propVal);
			SctsErrorLog::AppendError(errorMsg);
			return 500;
		}
		if ((node->format!=DMFORMAT_NODE || !node->VerifyACL(serverID,SCTS_REPLACE)) && node->parent && !node->parent->VerifyACL(serverID,SCTS_REPLACE)) return 425;
		if (command) *command=new SctsDMCommand(SCTS_REPLACE,node,prop,true);
		if (node->acl) sctsFree(node->acl);
		node->acl=0;
		if (_propVal && sctsStrlen(_propVal)>1) node->acl=sctsStrdup(_propVal);
		if (_propVal) sctsFree(_propVal);
		if (node->timeStamp) sctsFree(node->timeStamp);
		node->timeStamp=sctsGetTimeStamp(true);
		if (++node->version==0) ++node->version;
	}
	if (prop==DMPROP_TITLE)
	{
		if (!node->VerifyACL(serverID,SCTS_REPLACE)) return 425;
		if (command) *command=new SctsDMCommand(SCTS_REPLACE,node,prop,true);
		if (node->title) sctsFree(node->title);
		node->title=0;
		if (propVal && sctsStrlen(propVal)>1) node->title=sctsStrdup(propVal);
	}
	if (uiCallback) (*uiCallback)(this,node,SCTS_REPLACE);
	return 200;
}

int SctsDMTree::GetDMNode(char *nodeName,char *serverID,SctsDMNodeProp prop,SctsDMCommand **command)
{
	return GetDMNode(FindDMNode(nodeName),serverID,prop,command);
}

int SctsDMTree::GetDMNode(SctsDMNode *node,char *serverID,SctsDMNodeProp prop,SctsDMCommand **command)
{
	int rc=200;
	if (command) *command=0;
	if (!node) return 404;
	if (!node->VerifyACL(serverID,SCTS_GET)) return 425;
	switch(prop)
	{
	case DMPROP_ACL:
	case DMPROP_FORMAT:
	case DMPROP_MIMETYPE:
	case DMPROP_NAME:
	case DMPROP_TITLE:
	case DMPROP_TIMESTAMP:
	case DMPROP_VERSION:
	case DMPROP_NONE:
	case DMPROP_STRUCT:
	case DMPROP_STRUCTDATA:
    /*
     * Change to support TNDS. 
     */
    case DMPROP_TNDS:
		break;
	case DMPROP_SIZE:
		if (node->format==DMFORMAT_NODE) return 406;
		break;
	default:
		return 500;
	}
	/* Get on Password and Nonces should return 405 */
    /*
     * Change to support DM 1.2 DMAcc node. 
     */
    if (!(sctsStrcmp(node->name,"AAuthSecret") && sctsStrcmp(node->name,"AAuthData")))
	{
        if (IsDevice())
        {
            SctsDMDevice *unkDev = 0;
            if (!IsServerTree())
            {
                SctsDMClientDevice *cliDev = (SctsDMClientDevice *)this;
                unkDev = (SctsDMDevice *)cliDev;
            }
            else
            {
                SctsDMServerDevice *srvDev = (SctsDMServerDevice *)this;
                unkDev = (SctsDMDevice *)srvDev;
            }
            SctsDMDevice::MDAccType::iterator it = unkDev->accounts.begin();
            while (it != unkDev->accounts.end())
            {
                std::string authPath = (it->second).accPath;
                authPath += "/AppAuth";
                SctsDMNode *authNode = FindDMNode((char *)authPath.c_str());
                if (authNode && node->IsParent(authNode)) return 405;
                ++it;
            }
        }
	}
	if (command) *command=new SctsDMCommand(SCTS_GET,node,prop);
	return rc;
}

int SctsDMTree::GetDMNodeTNDS(char *nodeName,char *serverID,char *fields,SctsDMCommand **command)
{
	int rc = GetDMNode(nodeName,serverID,DMPROP_TNDS,command);
    if (rc == 200 && *command && fields)
    {
        (*command)->cmdSpecific.tndsFields = fields;
    }
    return rc;
}

int SctsDMTree::GetDMNodeTNDS(SctsDMNode *node,char *serverID,char *fields,SctsDMCommand **command)
{
	int rc = GetDMNode(node,serverID,DMPROP_TNDS,command);
    if (rc == 200 && *command && fields)
    {
        (*command)->cmdSpecific.tndsFields = fields;
    }
    return rc;
}

void SctsDMTree::RemoveServerID(char *sid)
{
	SctsDMNode *current=root;
	char *acl;
	char *pos1,*pos2,*pos3;
	SCTSASSERT(sid);
	int size=sctsStrlen(sid);
	char c1,c2;
	while (true)
	{
		if (current->acl)
		{
			pos3=pos2=acl=current->acl;
			while (pos1=sctsStrstr(pos2,sid))
			{
				c2=pos1[size];                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
				pos2=pos1+size;
				c1=*(--pos1);
				if ((c1!='=' && c1!='+') || (c2!='+' && c2!='&' && c2!=0)) continue;
				if (c1=='=')
				{
					if (c2=='&' || c2==0)
					{
						switch (*(pos1-1))
						{
							case 't':
							case 'd':
								pos1-=3;
								break;
							case 'e':
								pos1-=6;
								if (*pos1=='e') --pos1;
								break;
							case 'c':
								pos1-=4;
								break;
						}
						if (pos1!=acl) --pos1;
					}
					else
					{
						++pos1;
						++pos2;						
					}

				}
				sctsMemcpy(pos3,acl,pos1-acl);
				pos3+=pos1-acl;
				sctsStrcpy(pos3,pos2);
				acl=pos2=pos3;
			}
			if (pos3==current->acl)
			{
				sctsFree(current->acl);
				current->acl=0;
				if (current==root)
					current->acl=sctsStrdup("Add=*&Delete=*&Replace=*&Get=*&Exec=*");
			}
			else
			{
				sctsStrcpy(pos3,acl);
				current->acl=(char *)sctsRealloc(current->acl,sctsStrlen(current->acl)+1);
			}
		}
		if (current->child)
		{
			current=current->child;
			continue;
		}
		if (current->next)
		{
			current=current->next;
			continue;
		}
		while (current!=root && !current->next)
			current=current->parent;
		if (current==root) break;
		current=current->next;
	}
	/* Check if the ACL of the root node has some serverId for every command,
	   if not restore the serverId to '*' */
	
	if (!sctsStrstr(root->acl,"Add=")) 
	{
		root->acl=(char *)sctsRealloc(root->acl,sctsStrlen(root->acl)+6);
		sctsStrcat(root->acl,"Add=*");
	}
	if (!sctsStrstr(root->acl,"Get=")) 
	{
		root->acl=(char *)sctsRealloc(root->acl,sctsStrlen(root->acl)+6);
		sctsStrcat(root->acl,"Get=*");
	}
	if (!sctsStrstr(root->acl,"Replace=")) 
	{
		root->acl=(char *)sctsRealloc(root->acl,sctsStrlen(root->acl)+10);
		sctsStrcat(root->acl,"Replace=*");
	}
	if (!sctsStrstr(root->acl,"Delete=")) 
	{
		root->acl=(char *)sctsRealloc(root->acl,sctsStrlen(root->acl)+9);
		sctsStrcat(root->acl,"Delete=*");
	}
	if (!sctsStrstr(root->acl,"Exec=")) 
	{
		root->acl=(char *)sctsRealloc(root->acl,sctsStrlen(root->acl)+7);
		sctsStrcat(root->acl,"Exec=*");
	}
}

void SctsDMTree::Load(FILE *fp)
{
	SctsDMNode *current,*parent;
	char ch='p';
	if (uiCallback) (*uiCallback)(this,root,SCTS_DELETE);
	delete root;
	root=new SctsDMNode(fp);
	current=parent=root;
	while(true)
	{
		fread(&ch,sizeof(char),1,fp);
		switch(ch)
		{
		case 'c':
			{
				parent=current;
				current=new SctsDMNode(fp);
				current->parent=parent;
				if (!parent->child)
					parent->child=current;
				else
				{
					parent->child->prev=current;
					current->next=parent->child;
					parent->child=current;
				}
				continue;
			}
		case 'n':
			{
				current->next=new SctsDMNode(fp);
				current->next->prev=current;
				current=current->next;		
				current->parent=parent;
				continue;
			}
		case 'p':
			{
				current=current->parent;
				if (current==root) break;
				parent=parent->parent;				
				continue;
			}
		default:
			SCTSASSERT(false);
		};
		break;
	}
	if (uiCallback) (*uiCallback)(this,root,SCTS_ADD);
}

void SctsDMTree::Save(FILE *fp)
{
	SctsDMNode *current=root;
	char ch;
	while (true)
	{
		current->Save(fp);
		if (current->child)
		{
			ch='c';
			fwrite(&ch,sizeof(char),1,fp);
			current=current->child;
			continue;
		}
		if (current->next)
		{
			ch='n';
			fwrite(&ch,sizeof(char),1,fp);
			current=current->next;
			continue;
		}
		while (!current->next && current!=root)
		{
			ch='p';
			fwrite(&ch,sizeof(char),1,fp);
			current=current->parent;
		}
		if (current==root) break;
		current=current->next;
		if (current)
		{
			ch='n';
			fwrite(&ch,sizeof(char),1,fp);
		}
	}
}

/*************************************************************
SctsDMServerTree Class 
**************************************************************/

void SctsDMServerTree::InsertCommand(SctsDMCommand *item)
{
	std::list<SctsDMCommand *>::iterator cmdListIt;
	bool insert=true;
	SctsDMCommand *command;
	if (newDevice)
	{
		delete item;
		return;
	}
	if (item->reusable)
	{
		for (cmdListIt=cmdList.begin();cmdListIt!=cmdList.end();++cmdListIt)
		{
			command=*cmdListIt;
			if (command->nodeRef!=item->nodeRef || !command->reusable) continue;
			if (item->prop==command->prop && command->command==item->command) return;
		}
	}
	if (insert) cmdList.push_back(item);
}

bool SctsDMServerTree::GrantACLRight(SctsDMNode *node,char *serverID,SctsCommandType commandType,bool lastNode,bool insert)
{
	int size;
	SctsDMCommand *command;
	if (lastNode)
	{
		if ((node->format==DMFORMAT_NODE && GrantACLRight(node,serverID,commandType,false,insert)) ||
			(node->format!=DMFORMAT_NODE && GrantACLRight(node->parent,serverID,commandType,false,insert)))
		{
			/* Change the ACL */
			if (insert)
			{
				command=new SctsDMCommand(SCTS_REPLACE,node,DMPROP_ACL);
				InsertCommand(command);
			}
			if (node->acl)
			{
				size=sctsStrlen(node->acl)+sctsStrlen(serverID)+sctsStrlen(sctsCommandStrings[commandType])+3;
				node->acl=(char *)sctsRealloc(node->acl,size);
				sprintf(node->acl+sctsStrlen(node->acl),"&%s=%s",sctsCommandStrings[commandType],serverID);
			}
			else
			{
				size=sctsStrlen(serverID)+sctsStrlen(sctsCommandStrings[commandType])+9;
				node->acl=(char *)sctsMalloc(size);
				sprintf(node->acl,"Get=*&%s=%s",sctsCommandStrings[commandType],serverID);
			}
			return true;
		}
		return false;
	}
	/* Recursive portion */
	while (node && !node->acl) node=node->parent;
	if (!node) return false;
	if (node->VerifyACL(serverID,SCTS_REPLACE,false)) return true;
	if (GrantACLRight(node->parent,serverID,SCTS_REPLACE,false,insert))
	{
		/* Change the ACL to grant only Replace rights */
		if (insert)
		{
			command=new SctsDMCommand(SCTS_REPLACE,node,DMPROP_ACL);
			InsertCommand(command);
		}
		size=sctsStrlen(node->acl)+sctsStrlen(serverID)+10;
		node->acl=(char *)sctsRealloc(node->acl,size);
		sctsStrcat(node->acl,"&Replace=");
		sctsStrcat(node->acl,serverID);
		return true;
	}
	return false;
}

int SctsDMServerTree::ReplaceDMNodeProp(char *nodeName,char *serverID,SctsDMNodeProp prop,char *propVal,SctsDMCommand **command)
{
	return ReplaceDMNodeProp(FindDMNode(nodeName),serverID,prop,propVal,command);
}

int SctsDMServerTree::ReplaceDMNodeProp(SctsDMNode *node,char *serverID,SctsDMNodeProp prop,char *propVal,SctsDMCommand **command)
{
	int rc;
	if (!node) return 404;
	rc=SctsDMTree::ReplaceDMNodeProp(node,serverID,prop,propVal,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
		   DMEVENT V1.1.1
		if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		*/
		if (GrantACLRight(prop==DMPROP_ACL?node->parent:node,serverID,SCTS_REPLACE,true,command?true:false)) 
		{
			rc=SctsDMTree::ReplaceDMNodeProp(node,serverID,prop,propVal,command);
			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);
			}
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

int SctsDMServerTree::ReplaceDMNodeData(char *nodeName,char *serverID,void *data,unsigned int size,char *format,char *mimeType,SctsDMCommand **command)
{
	return ReplaceDMNodeData(FindDMNode(nodeName),serverID,data,size,format,mimeType,command);		
}

int SctsDMServerTree::ReplaceDMNodeData(SctsDMNode *node,char *serverID,void *data,unsigned int size,char *format,char *mimeType,SctsDMCommand **command)
{
	int rc;
	if (!node) return 404;
	rc=SctsDMTree::ReplaceDMNodeData(node,serverID,data,size,format,mimeType,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
		DMEVENT V1.1.1
		if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		*/
		if (GrantACLRight(node,serverID,SCTS_REPLACE,true,command?true:false)) 
		{
			rc=SctsDMTree::ReplaceDMNodeData(node,serverID,data,size,format,mimeType,command);

			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);
			}
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

int SctsDMServerTree::AddDMNode(char *nodeName,char *serverID,char *_format,bool _permanent,void *_data,char *_mimeType,unsigned int _size,SctsDMNode **_node,SctsDMCommand **command)
{
	int rc;
	SctsDMNode *node;
	char *name;
	rc=SctsDMTree::AddDMNode(nodeName,serverID,_format,_permanent,_data,_mimeType,_size,_node,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
			DMEVENT V1.1.1
		 if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		 */

		/* Find the parent node */
		/* Get short name and find parent node */
		name=nodeName+sctsStrlen(nodeName);
		while (name!=nodeName && *name!='/') --name;
		if (name==nodeName)	
			node=root;
		else
		{
			*name=0;
			node=SctsDMTree::FindDMNode(nodeName);
			*name='/';
			++name;
		}
		if (GrantACLRight(node,serverID,SCTS_ADD,true,command?true:false)) 
		{
			rc=SctsDMTree::AddDMNode(nodeName,serverID,_format,_permanent,_data,_mimeType,_size,&node,command);
			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);

			}
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

int SctsDMServerTree::DeleteDMNode(char *nodeName,char *serverID,SctsDMCommand **command)
{
	return DeleteDMNode(FindDMNode(nodeName),serverID,command);		
}

int SctsDMServerTree::DeleteDMNode(SctsDMNode *node,char *serverID,SctsDMCommand **command)
{
	int rc;
	if (!node) return 404;
	
	rc=SctsDMTree::DeleteDMNode(node,serverID,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
		   DMEVENT V1.1.1 
		if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		*/
		if (GrantACLRight(node,serverID,SCTS_DELETE,true,*command?true:false)) 
		{
			rc=SctsDMTree::DeleteDMNode(node,serverID,command);
			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);
			}
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

int SctsDMServerTree::GetDMNode(char * nodeName,char *serverID,SctsDMNodeProp prop,SctsDMCommand **command)
{
	return GetDMNode(FindDMNode(nodeName),serverID,prop,command);
}

int SctsDMServerTree::GetDMNode(SctsDMNode *node,char *serverID,SctsDMNodeProp prop,SctsDMCommand **command)
{
	int rc;
	if (!node) return 404;
	rc=SctsDMTree::GetDMNode(node,serverID,prop,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
		   DMEVENT V1.1.1 
		if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		*/
		if (GrantACLRight(node,serverID,SCTS_GET,true,command?true:false)) 
		{
			rc=SctsDMTree::GetDMNode(node,serverID,prop,command);
			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);
			}			
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

void SctsDMServerTree::Load(FILE *fp)
{
	SctsDMCommand *command;
	char *nodeName=0;
	SctsDMTree::Load(fp);
	while (fgetc(fp))
	{
		command=new SctsDMCommand(fp);
		if (fgetc(fp))
		{
			char *path=sctsFReadString(fp);
			command->nodeRef=FindDMNode(path);
			if (command->command==SCTS_DELETE) 
			{
				if (uiCallback) (*uiCallback)(this,command->nodeRef,SCTS_DELETE);
				command->nodeRef->markedForDelete=true;
			}
			sctsFree(path);
		}
		else
			command->nodeRef=0;
		cmdList.push_back(command);
	}		
	fread(&newDevice,sizeof(bool),1,fp);
}

void SctsDMServerTree::Save(FILE *fp)
{
	SctsDMTree::Save(fp);
	std::list<SctsDMCommand *>::iterator cmdIt;
	char *path;
	for (cmdIt=cmdList.begin();cmdIt!=cmdList.end();cmdIt++)
	{
		fputc(1,fp);
		(*cmdIt)->Save(fp);
		if ((*cmdIt)->nodeRef)
		{
			path=GetPath((*cmdIt)->nodeRef);
			fputc(1,fp);
			sctsFWriteString(path,fp);
			sctsFree(path);
		}
		else
			fputc(0,fp);
	}
	fputc(0,fp);
	fwrite(&newDevice,sizeof(bool),1,fp);
}

	
SctsDMServerTree::~SctsDMServerTree()
{
	SctsDMCommand *command;
	std::list<SctsDMCommand *>::iterator commandIt;
	for(commandIt=cmdList.begin();commandIt!=cmdList.end();)
	{
		command=*commandIt;
		delete command;
		commandIt=cmdList.erase(commandIt);
	}
}

int SctsDMTree::ExecDMNode(char *nodeName,char *serverID,SctsDMCommand **command)
{
	SctsDMNode *node=FindDMNode(nodeName);
	if (!node)
	{
		char tempBuffer[500];
		sprintf(tempBuffer,"Exec Failed : Node with path %s not found.",nodeName);
		SctsErrorLog::AppendError(tempBuffer);
		return 404;
	}
	return ExecDMNode(node,serverID,command);
}

int SctsDMTree::ExecDMNode(SctsDMNode *node,char *serverID,SctsDMCommand **command)
{
	if (command) *command=0;
	if (!node) return 404;

    char *acl=node->GetACL();
	if (acl && !sctsStrstr(acl, "Exec"))	return 405;
	if (!node->VerifyACL(serverID,SCTS_EXEC)) return 425;
	if (uiCallback) (*uiCallback)(this,node,SCTS_EXEC);
	if (command)
		*command=new SctsDMCommand(SCTS_EXEC,node,DMPROP_NONE,true);
	return 200;
}

int SctsDMServerTree::ExecDMNode(char *nodeName,char *serverID,SctsDMCommand **command)
{
	return ExecDMNode(FindDMNode(nodeName),serverID,command);
}

int SctsDMServerTree::ExecDMNode(SctsDMNode *node,char *serverID,SctsDMCommand **command)
{
	int rc;
	if (!node) return 404;
	
	rc=SctsDMTree::ExecDMNode(node,serverID,command);
	if (rc==200 && command) InsertCommand(*command);
	if (rc==425)
	{
		/* Make the operation of granting ACL Atomic->Sequence 
		   DMEVENT V1.1.1 
		if (command) InsertCommand(*command=new SctsDMCommand(SCTS_ATOMIC_START));
		*/
		if (GrantACLRight(node,serverID,SCTS_EXEC,true,*command?true:false)) 
		{
			rc=SctsDMTree::ExecDMNode(node,serverID,command);
			SCTSASSERT(rc==200);
			if (command)
			{
				// DMEVENT V1.1.1 InsertCommand(new SctsDMCommand(SCTS_ATOMIC_END));
				InsertCommand(*command);
			}
		}
		else
		{
			if (command && *command)
			{
				cmdList.pop_back();
				delete *command;
				*command=0;
			}
		}
	}
	return rc;
}

int SctsDMTree::CopyDMNode(char *nodeNameFrom,char *nodeNameTo,char *serverID,SctsDMCommand **command)
{
	SctsDMNode *nodeFrom=FindDMNode(nodeNameFrom);
	if (!nodeFrom)
	{
		char tempBuffer[500];
		sprintf(tempBuffer,"Copy Failed : Node with path %s not found.",nodeNameFrom);
		SctsErrorLog::AppendError(tempBuffer);
		return 404;
	}
	return CopyDMNode(nodeFrom,nodeNameTo,serverID,command);
}

int SctsDMTree::CopyDMNode(SctsDMNode *nodeFrom,char *nodeNameTo,char *serverID,SctsDMCommand **command)
{
	if (command) *command=0;
	if (!nodeFrom) return 404;
    if (nodeFrom->format == DMFORMAT_NODE) return 405;
    int rc;

    if (rc = SctsDMTree::GetDMNode(nodeFrom, serverID, DMPROP_NONE, 0), rc == 200)
    {
        SctsDMNode *nodeTo = FindDMNode(nodeNameTo);
        if (nodeTo)
        {
            rc = SctsDMTree::ReplaceDMNodeData(nodeTo, serverID, nodeFrom->data, nodeFrom->size, SctsDMFormatString[nodeFrom->format], nodeFrom->mimeType, 0);
        }
        else
        {
            rc = SctsDMTree::AddDMNode(nodeNameTo, serverID, SctsDMFormatString[nodeFrom->format], false, nodeFrom->data, nodeFrom->mimeType, nodeFrom->size, &nodeTo);
        }
	    if (rc == 200 && command)
        {
            SctsDMCommand::SctsDMCommandSpecific specific;
            char *nodeNameFrom = GetPath(nodeFrom);
            specific.copyFrom = nodeNameFrom;
            sctsFree(nodeNameFrom);
		    *command = new SctsDMCommand(SCTS_COPY,nodeTo,DMPROP_NONE,true,&specific);
        }
    }
	return rc;
}

int SctsDMServerTree::CopyDMNode(char *nodeNameFrom,char *nodeNameTo,char *serverID,SctsDMCommand **command)
{
	return CopyDMNode(FindDMNode(nodeNameFrom),nodeNameTo,serverID,command);
}

int SctsDMServerTree::CopyDMNode(SctsDMNode *nodeFrom,char *nodeNameTo,char *serverID,SctsDMCommand **command)
{
	int rc;
	if (!nodeFrom) return 404;
    SctsDMNode *nodeTo = FindDMNode(nodeNameTo);

    rc = SctsDMTree::GetDMNode(nodeFrom, serverID, DMPROP_NONE);
	if (rc == 425)
	{
        if (rc = SctsDMServerTree::GetDMNode(nodeFrom, serverID, DMPROP_NONE), rc != 200)
            return rc;
	}
	rc = SctsDMTree::CopyDMNode(nodeFrom, nodeNameTo, serverID, command);
	if (rc == 200 && command) InsertCommand(*command);
	if (rc == 425)
	{
        if (nodeTo)
        {
            if (rc = SctsDMServerTree::ReplaceDMNodeData(nodeFrom, serverID, nodeFrom->data, nodeFrom->size, SctsDMFormatString[nodeFrom->format], nodeFrom->mimeType, 0), rc != 200)
                return rc;
        }
        else
        {
            if (rc = SctsDMServerTree::AddDMNode(nodeNameTo, serverID, SctsDMFormatString[nodeFrom->format], false, nodeFrom->data, nodeFrom->mimeType, nodeFrom->size, &nodeTo), rc != 200)
                return rc;
        }
        rc = SctsDMTree::CopyDMNode(nodeFrom, nodeNameTo, serverID, command);
        if (rc == 200 && command) InsertCommand(*command);
	}
	return rc;
}

int SctsDMTree::GetDMNodeTNDS(char *nodeName,char *serverID,char *props,SctsPcdata& pcData,SctsEncoding encoding,SctsDMCommand **command)
{
	return GetDMNodeTNDS(FindDMNode(nodeName),serverID,props,pcData,encoding,command);
}

int SctsDMTree::GetDMNodeTNDS(SctsDMNode *node,char *serverID,char *props,SctsPcdata& pcData,SctsEncoding encoding,SctsDMCommand **command)
{
    const static int TNDS_MAX_BUFFER_LEN = 64 * 1024;
    SctsDMNode *subTreeRoot = 0;
    int getRight = 0;
    SmlSubTreePtr_t getSubTree = 0;
    SmlSubTreeNodePtr_t getSubTreeNode = 0;

    pcData.rawData=(SmlPcdataPtr_t)sctsMalloc(sizeof(SmlPcdata_t));
    pcData.rawData->contentType=encoding==XML?SML_PCDATA_CDATA:SML_PCDATA_OPAQUE;
    pcData.rawData->length=0;
    /* Allocate buffer to strore subtree */
    if (pcData.rawData->content=sctsMalloc(TNDS_MAX_BUFFER_LEN))
    {
        getSubTree = (SmlSubTreePtr_t)smlLibMalloc(sizeof(SmlSubTree_t));
        memset(getSubTree, 0, sizeof(SmlSubTree_t));
        getSubTree->version = smlString2Pcdata((char *)"1.2");
        getSubTreeNode = (SmlSubTreeNodePtr_t)smlLibMalloc(sizeof(SmlSubTreeNode_t));
        memset(getSubTreeNode, 0, sizeof(SmlSubTreeNode_t));
        getSubTree->node = getSubTreeNode;

        char * pBufPos = (char *)(pcData);
        /* Build TNDS Message */
        int rc = smlMarshalMgmtTree(encoding==XML?0:1, pBufPos + TNDS_MAX_BUFFER_LEN, &pBufPos, getSubTree);
        if (rc == SML_ERR_OK)
        {
            /* Log TNDS WBXML. */
            pcData.rawData->length = pBufPos - (char *)(pcData);
            FILE *fp = fopen("tnds.wbxml", "wb");
            if (fp)
            {
                fwrite(pcData.rawData->content, pcData.rawData->length, 1, fp);
                fclose(fp);
            }
        }
    }
    else
    {
        sctsFree(pcData.rawData);
    }
    return 0;
}

int SctsDMTree::AddFullDMNode(char *_nodeName,char *_serverID,char *_format,bool _permanent,void *_data,char *_mimeType,unsigned int _size,SctsDMNode **node,SctsDMCommand **command)
{
	int status = -1;
	if (command) *command=0;
	if (!_nodeName || !(*_nodeName)) return 500;
    std::string nodeFullName = _nodeName;
    std::string::size_type it = nodeFullName.find_first_of("/");
    std::string::size_type itend = nodeFullName.find_last_of("/");
    while (it != std::string::npos && it < nodeFullName.size())
    {
        std::string findNodeName = nodeFullName.substr(0, it);
        SctsDMNode *current = FindDMNode((char *)findNodeName.c_str());
        if (current)
        {
            it = nodeFullName.find("/", it + 1);
            continue;
        }
        status = 200;
        break;
    }
    while (it != std::string::npos && it < nodeFullName.size())
    {
        std::string findNodeName = nodeFullName.substr(0, it);
        status = AddDMNode((char *)findNodeName.c_str(),_serverID,"node",_permanent,0,0,0,0,0);
        if (status == 200)
        {
            it = nodeFullName.find("/", it + 1);
        }
        else
            return status;
    }
    if (status == -1 || status == 200)
    {
        status = AddDMNode(_nodeName,_serverID,_format,_permanent,_data,_mimeType,_size,node,command);
    }
	return status;
}

const SctsDMTree::SctsDMNodePtrVec SctsDMTree::FindAnyDMNode(char *nodeName,SctsDMNode *startNode)
{
    SctsDMNodePtrVec nodeVec;
	SctsDMNode *current=startNode?startNode:root;
	char *pos;
	char *tempBuffer;
	if (!nodeName) return nodeVec;
	tempBuffer=sctsStrdup(nodeName);
	nodeName=tempBuffer;
    nodeVec.push_back(current);
	if (nodeName[0]=='.' && nodeName[1] =='/')
    {
		nodeName+=2;
    }
	else if ((nodeName[0]=='.' || nodeName[0]=='/') && !nodeName[1])
	{
		sctsFree(tempBuffer);
		return nodeVec;
	} 
	if (!nodeName[0]) 
	{
		sctsFree(tempBuffer);
		return nodeVec;
	}

    SctsDMNodePtrVec tmpNodeVec;
    do
	{
		pos=sctsStrchr(nodeName,'/');
		if (pos) *pos=0;
        for (SctsDMNodePtrVec::iterator it = nodeVec.begin(); it != nodeVec.end(); ++it)
        {
            if (*nodeName == '*')
            {
                for (current = (*it)->child; current; current = current->next)
                {
                    if (!current->markedForDelete)
                        tmpNodeVec.push_back(current);
                }
            }
            else
            {
                current = FindDMNode(nodeName, *it);
                if (current) tmpNodeVec.push_back(current);
            }
        }
        nodeVec = tmpNodeVec;
        tmpNodeVec.clear();
		//for(current=current->child;current && sctsStrcmp(nodeName,current->name);current=current->next);
		//if (!current || current->markedForDelete)
		//{
		//	sctsFree(tempBuffer);
		//	return nodeVec;
		//}
		if (pos) 
		{
			*pos='/';
			nodeName=++pos;
        }
	} while(pos);
	sctsFree(tempBuffer);
	return nodeVec;
}

SctsDMTNDSTree::SctsDMTNDSTree(const char *_format, const char *_mime, char *_tndsData, int _dataSize)
{
    format = _format? _format:"";
    mime = _mime? _mime:"";
    tndsData = _tndsData;
    dataSize = _dataSize;
    errCode = VAL_OK;
    pMgmtTree = 0;
    if (format == "xml" && mime != "application/vnd.syncml.dmtnds+xml")
    {
        errCode = NVAL_TYPE_FORMAT;
    }
    else if ((format == "b64" || format == "bin") && mime != "application/vnd.syncml.dmtnds+wbxml")
    {
        errCode = NVAL_TYPE_FORMAT;
    }
    if (errCode == VAL_OK)
    {
        if (!tndsData || dataSize<= 0)
        {
            errCode = NVAL_CONTENT;
        }
    }
    if (errCode == VAL_OK)
    {
        int encType = (format == "bin"?1:0);
        char * bufferEnd = tndsData + dataSize - (encType?0:1);
        if (smlProcessMgmtTree(encType, bufferEnd, tndsData, &pMgmtTree) || !pMgmtTree)
        {
            errCode = NVAL_STRUCTURE;
        }
    }
    if (errCode == VAL_OK)
    {
        if (!pMgmtTree->node)
        {
            errCode = NVAL_STRUCTURE;
        }
    }
}

int SctsDMTNDSTree::IsValid(char *tndsInfoFields)
{
    if (errCode != VAL_OK)
    {
        return errCode;
    }
    unsigned int tndsPropSet = 0;
    SetGetTNDSProperty(GET_TNDS_VALUE, 1, tndsPropSet);
    SetGetTNDSProperty(GET_TNDS_ACL, 1, tndsPropSet);
    SetGetTNDSProperty(GET_TNDS_FORMAT, 1, tndsPropSet);
    //SetGetTNDSProperty(GET_TNDS_VALUE, 1, tndsPropSet);
    //SetGetTNDSProperty(GET_TNDS_VALUE, 1, tndsPropSet);
    //SetGetTNDSProperty(GET_TNDS_VALUE, 1, tndsPropSet);

    SmlSubTreeNodePtr_t root = pMgmtTree->node, node = 0;
    std::deque<SmlSubTreeNodePtr_t> nodes;
    nodes.push_back(root);
    while (!nodes.empty())
    {
        node = nodes.front();
        nodes.pop_front();
        if (CheckTndsInfo(node, tndsPropSet))
        {
            errCode = NVAL_ENOUGH_INFO;
        }
        if(node->child)
        {
            nodes.push_back(node->child);
        }
        while (node->nextSibling)
        {
            node = node->nextSibling;
            if (CheckTndsInfo(node, tndsPropSet))
            {
                errCode = NVAL_ENOUGH_INFO;
            }
            if(node->child)
            {
                nodes.push_back(node->child);
            }
        }
        if (errCode != VAL_OK)
        {
            break;
        }
    }
    return errCode;
}

int SctsDMTNDSTree::CheckTndsInfo(SmlSubTreeNodePtr_t node, unsigned int tndsPropSet)
{
    int rc = 0;
    while (node)
    {
        if (IsGetTNDSProperty(GET_TNDS_ACL, tndsPropSet) && !node->ACL)
        {
            if (!node->parent)
            {
                rc = -1;
                break;
            }
        }
        if (IsGetTNDSProperty(GET_TNDS_FORMAT, tndsPropSet) && !node->format)
        {
            rc = -1;
            break;
        }
        if (IsGetTNDSProperty(GET_TNDS_VERNO, tndsPropSet) && !node->verNo)
        {
            rc = -1;
            break;
        }
        if (IsGetTNDSProperty(GET_TNDS_TITLE, tndsPropSet) && !node->title)
        {
            rc = -1;
            break;
        }
        char *format = (char *)SctsPcdata(node->format);
        if (sctsStrcmp(format, "node") && sctsStrcmp(format, "null"))
        {
            if (IsGetTNDSProperty(GET_TNDS_VALUE, tndsPropSet) && !node->data)
            {
                if (!node->child && node->size)
                {
                    rc = -1;
                    break;
                }
            }
        }
        break;
    }
    return rc;
}
