/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

/* $Id$ */

#include "Logger/Logger.h"
#include "treemanager/MOTreeManager.h"
#include "commontypes.h"
#include "Node.h"
#include <syncml/core/Item.h>
#include <syncml/formatter/Formatter.h>
#include <syncml/core/ObjectDel.h>
#include "DeviceAdapter/DeviceUpdater.h"
#include "common/Buffer.h"
#include "daemon/INotificationCenter.h"

#include "MOTreeManagerTransaction.h"
#include "treemanager/MOTreeManagerListenerCollection.h"

#include <memory>
#include <algorithm>

#include "DeviceAdapter/Diagnostics/CollectDiagnosticsCommand.h"

namespace ds = NS_DM_Client::NS_DataStorage;

#define fitString(s)					\
			strlen(s) > 256 ?			\
				&(s[strlen(s)-256]) :	\
				s


static const char c_RootAccessID[] = "<root>";

#if defined(WIN32)
#	if !defined(snprintf)
#		define snprintf _snprintf
#		define vsnprintf _vsnprintf
#	endif
#endif

namespace NS_DM_Client
{
	const String S_beginScope = "<";
	const String S_endScope = ">";
	const String S_slash = "/";
	const String S_mgmtTree = "MgmtTree";
	const String S_versionNS = " xmlns='syncml:dmddf1.2'";
	const String S_verDTD = "1.2";
	const String S_node = "Node";
	const String S_nodeName = "NodeName";
	const String S_path = "Path";
	const String S_value = "Value";
	const String S_RTProperties = "RTProperties";

    const char* const c_accessTypeParam = "AccessTypeContentPath";
    const size_t c_maxItemPathLength = 1024;

    const char* const c_DRDMTestEnvironment = "DRDMTestEnvironment";

	void CompleteItem(const Funambol::Item& item);

	//-------------------------------------------------------------------------------------------
	MOTreeManager::MOTreeManager(): m_pch(0), m_dataStorage(0), m_transaction(0), m_listeners(0),
		  m_logger(0), m_isInited(false), m_needFinishThansaction(false)
	{
	}
	//-------------------------------------------------------------------------------------------

	bool MOTreeManager::Init(const StringMap& settings, const String& loggerInstance, ProfileComponentsHolder& pch)
	{
		if (m_isInited)
		{
			return true;
		}

		m_logger = &NS_Logging::GetLogger(loggerInstance.c_str());
		LOG_INFO_(*m_logger, "Logger was inited");

		m_pch = &pch;

        if (!getAccessTypeTree(settings, pch))
        {
			LOG_WARNING_(*m_logger, "Failed to get access type map");
        }

		if (!(m_dataStorage = pch.GetDataStorage()))
		{
			LOG_ERROR_(*m_logger, "Failed to get IDataStorage instance from ProfileComponentHolder");
			return false;
		}

		m_listeners = GetMOTreeManagerListenerCollection(pch, settings);
		if (m_listeners)
		{
			if (!m_listeners->Init(pch, settings))
			{
				m_listeners->Release();
				LOG_ERROR_(*m_logger, "Failed to init MOTreeManagerListenerCollection instance");
				return false;
			}
		}
		else
		{
			LOG_ERROR_(*m_logger, "Failed to get MOTreeManagerListenerCollection instance");
			return false;
		}

		m_transaction = new MOTreeManagerTransaction;
		if (m_transaction)
		{
			if (!m_transaction->Init(m_listeners, pch.GetDeviceAdapter(), pch.GetDataStorage(), this)) // also synchronize with device
		    {
			    LOG_ERROR_(*m_logger, "Failed to init MOTreeManagerTransaction instance");
			    m_transaction->Release();
			    m_listeners->Release();
			    return false;
		    }
		}
		else
		{
			LOG_ERROR_(*m_logger, "Failed to get MOTreeManagerTransaction instance");
			m_listeners->Release();
			return false;
		}

		m_isInited = true;

		LOG_(*m_logger, "MOTreeManager was inited successfuly");
		return true;
	}
	//-------------------------------------------------------------------------------------------

    void MOTreeManager::release()
    {
		if (m_transaction)
		{
            m_transaction->Release();
            m_transaction = 0;
		}
		if (m_listeners)
		{
            m_listeners->Release();
		    m_listeners = 0;
		}
    }

	//-------------------------------------------------------------------------------------------

	void MOTreeManager::Release()
	{
        release();

		delete this;
	}
	//-------------------------------------------------------------------------------------------

	MOTreeManager::~MOTreeManager()
	{
        release();
	}

	//-------------------------------------------------------------------------------------------

	bool MOTreeManager::Exist(const URI& uri, const char* serverId/* = 0*/)
	{
		LOG_(*m_logger, "MOTreeManager::Exist, uri = '%s'", uri.c_str());
		Node node(uri, m_dataStorage);
		return node.Exist();
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::Get(const URI& uri, Funambol::Item& item, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_WARNING_(*m_logger, "Node wasn't found");
    		res = e_NotFound;
    		LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		res = checkOperationPermission("Get", node, serverId);
		if (res != e_Ok)
		{
    		LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		    return res;
        }

		String content;
		if (node.IsLeaf())
		{
			LOG_(*m_logger, "Node is a leaf node");
			if (!node.GetContent(content))
			{
				LOG_ERROR_(*m_logger, "Failed to get content from leaf node with uri = '%s'", uri.c_str());
    		    res = e_CommandFailed;
    		    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
				return res;
			}
			LOG_(*m_logger, "Leaf node with uri = '%s' has content = '%s'", uri.c_str(), fitString(content.c_str()));
		}
		else
		{
			LOG_(*m_logger, "Node is an interior node");
			//add to data children's list
			Node::ChildrenPaths children;
			if (!node.GetChildren(children))
			{
				content = ""; // no children - return empty string
			}
			Node::ChildrenPaths::const_iterator end(children.end());
			for (Node::ChildrenPaths::const_iterator iter(children.begin()); iter != end; ++iter)
			{
				content += *iter;
				content += S_seperator;
			}
			content = content.substr(0, content.length() - 1); // delete last '/'
		}

		Funambol::Meta metaData;
		// get format property
		String value;
		if (node.GetProperty(e_format, value))
		{
			metaData.setFormat(value.c_str());
		}
		else
		{
			LOG_ERROR_(*m_logger, "Failed to get \"Format\" property");
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}
		// get type property
		if (node.GetProperty(e_type, value))
		{
			metaData.setType(value.c_str());
		}
		else
		{
			LOG_ERROR_(*m_logger, "Failed to get \"Type\" property");
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}
		// get size property (not mandatory)
		size_t size;
		if (node.GetContentSize(size))
		{
			metaData.setSize((int)size);
		}

		Funambol::ComplexData complexData(content.c_str());
		item.setData(&complexData);
		Funambol::Target target(uri.c_str());
		item.setTarget(&target);
		item.setMeta(&metaData);

        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return value: %s, Return status: %d", fitString(content.c_str()), res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::GetAttributeStructData(const URI& uri, Funambol::ArrayList& items, const char* serverId/* = 0*/)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);

		res = checkOperationPermission("Get", node, serverId);
		if (res != e_Ok)
		{
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		    return res;
        }

		Funambol::Item item;
		res = Get(uri, item);
		if (res != e_Ok)
		{
			LOG_ERROR_(*m_logger, "Failed to get item for uri = '%s'", uri.c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}
		items.add(item);
		if (!(node.IsLeaf())) // if it is node
		{
			ChildrenPaths children;
			res = GetChildren(uri, children);
			if (e_Ok == res)
			{
				ChildrenPaths::const_iterator end(children.end());
				for (ChildrenPaths::const_iterator iter = children.begin(); iter != end; ++iter)
				{
					GetAttributeStructData(uri + S_seperator + *iter, items, serverId);
				}
			}
		}
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::GetAttributeTNDS(const URI& uri, const RequiredProperties& requiredProp,
		Funambol::StringBuffer*& xml, const char* serverId/* = 0*/)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);

		res = checkOperationPermission("Get", node, serverId);
		if (res != e_Ok)
		{
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		    return res;
        }

		xml = new Funambol::StringBuffer((S_beginScope + S_mgmtTree + S_versionNS + S_endScope).c_str());
		Funambol::VerDTD ver(S_verDTD.c_str());
		Funambol::StringBuffer* verDTD = Funambol::Formatter::getVerDTD(&ver);
		xml->append(verDTD);
		Funambol::deleteStringBuffer(&verDTD);

		// in case parent's nodes should be included in tree - uncomment these lines
// 		Funambol::StringBuffer* nodeXml = getAboveNodeXMLRepresentation(uri, requiredProp);
// 		if (!nodeXml)
// 		{
// 			LOG_ERROR_(*m_logger, "Failed to get xml representation of node, uri = '%s'", uri.c_str());
// 			return e_CommandFailed;
// 		}
// 		xml->append(nodeXml);
// 		Funambol::deleteStringBuffer(&nodeXml);

		Funambol::StringBuffer* childrenNodes = getBelowNodeXMLRepresentation(uri, requiredProp);
		if (!childrenNodes)
		{
			LOG_ERROR_(*m_logger, "Failed to get xml representation of node, uri = '%s'", uri.c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", e_CommandFailed);
			return e_CommandFailed;
		}
		xml->append(childrenNodes);
		Funambol::deleteStringBuffer(&childrenNodes);

		xml->append((S_beginScope + S_slash + S_mgmtTree + S_endScope).c_str());
		LOG_INFO_(*m_logger, "Management tree\n = %s", xml->c_str());
        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	Funambol::StringBuffer* MOTreeManager::getAboveNodeXMLRepresentation(const URI& uri, const RequiredProperties& requiredProp)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s'", uri.c_str());
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_ERROR_(*m_logger, "Node doesn't exist, uri = '%s'", uri.c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Result: NULL");
			return 0;
		}
		Funambol::StringBuffer* nodeXml = new Funambol::StringBuffer();
		// Parent
		Node* parent = node.GetParent();
		if (parent)
		{
			Funambol::StringBuffer* parentXml = getAboveNodeXMLRepresentation(parent->GetPath(), requiredProp);
			nodeXml->append(parentXml);
			Funambol::deleteStringBuffer(&parentXml);
		}
		nodeXml->append((S_beginScope + S_node + S_endScope).c_str());

		if (parent)
		{
			appendValue(nodeXml, S_path.c_str(), parent->GetPath().c_str());
			delete parent;
		}

		appendRequiredProperties(nodeXml, node, requiredProp);
		nodeXml->append((S_beginScope + S_slash + S_node + S_endScope).c_str());
        LOG_DEBUG_(*m_logger, "LEAVE << Result: %s", nodeXml->c_str());
		return nodeXml;
	}
	//-------------------------------------------------------------------------------------------

	Funambol::StringBuffer* MOTreeManager::getBelowNodeXMLRepresentation(const URI& uri, const RequiredProperties& requiredProp)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s'", uri.c_str());
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_ERROR_(*m_logger, "Node doesn't exist, uri = '%s'", uri.c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Result: NULL");
			return 0;
		}
		// Children
		Funambol::ArrayList items;
		StatusCode res = GetAttributeStructData(uri, items);
		if (res != e_Ok)
		{
			LOG_ERROR_(*m_logger, "Failed to GetAttributeStructData for uri = '%s'", uri.c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Result: NULL");
			return 0;
		}

		Funambol::StringBuffer* nodeXml = new Funambol::StringBuffer();
		for (int i = 0; i != items.size(); ++i)
		{
			Funambol::Item* curItem = (Funambol::Item*)items.get(i);
			nodeXml->append((S_beginScope + S_node + S_endScope).c_str());
			Node curNode(curItem->getTarget()->getLocURI(), m_dataStorage);
			Node* parent = curNode.GetParent();
			if (parent)
			{
				appendValue(nodeXml, S_path.c_str(), parent->GetPath().c_str());
				delete parent;
			}
			appendRequiredProperties(nodeXml, curNode, requiredProp);
			nodeXml->append((S_beginScope + S_slash + S_node + S_endScope).c_str());
		}
        LOG_DEBUG_(*m_logger, "LEAVE << Result: %s", nodeXml->c_str());
		return nodeXml;
	}
	//-------------------------------------------------------------------------------------------

	void MOTreeManager::appendRequiredProperties(Funambol::StringBuffer*& nodeXml, const Node& node,
		const RequiredProperties& requiredProp)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::appendRequiredProperties");
		Funambol::StringBuffer* RTPPropertiesXml = new Funambol::StringBuffer((S_beginScope + S_RTProperties + S_endScope).c_str());
		bool res = false;
		String value;
		Funambol::StringBuffer formattedVal;
		RequiredProperties::const_iterator end(requiredProp.end());
		for (RequiredProperties::const_iterator iter = requiredProp.begin(); iter != end; ++iter)
		{
			switch (*iter)
			{
			case e_TNDS_ACL:
				{
					res = node.GetProperty(e_ACL, value); // RTPProperties::ACL
					LOG_INFO_(*m_logger, "\"ACL\" property is required, value = %s", value.c_str());
					if (res && !(value.empty()))
					{
						formattedVal = "";
						Funambol::Formatter::formatValue(formattedVal, value.c_str());
						appendValue(RTPPropertiesXml, S_propertyName[e_ACL], formattedVal.c_str());
					}
					break;
				}
			case e_TNDS_format:
				{
					res = node.GetProperty(e_format, value); // RTPProperties::Format
					LOG_INFO_(*m_logger, "\"Format\" property is required, value = %s", value.c_str());
					appendValue(RTPPropertiesXml, S_propertyName[e_format], value.c_str());
					break;
				}
			case e_TNDS_name:
				{
					value = node.GetName(); // NodeName
					LOG_INFO_(*m_logger, "\"NodeName\" property is required, value = %s", value.c_str());
					appendValue(nodeXml, S_nodeName, value.c_str());
					break;
				}
			case e_TNDS_size:
				{
					size_t size; // RTPProperties::Size
					res = node.GetContentSize(size);
					LOG_INFO_(*m_logger, "\"Size\" property is required, value = %d", size);
					if (res)
					{
						appendValue(RTPPropertiesXml, S_propertyName[e_size], IntToString((int)size).c_str());
					}
					break;
				}
			case e_TNDS_title:
				res = node.GetProperty(e_title, value); // RTPProperties::Title
				LOG_INFO_(*m_logger, "\"Title\" property is required, value = %s", value.c_str());
				if (res && !(value.empty()))
				{
					appendValue(RTPPropertiesXml, S_propertyName[e_title], value.c_str());
				}
				break;
			case e_TNDS_tstamp:
				{
					res = node.GetProperty(e_tstamp, value); // RTPProperties::TStamp
					LOG_INFO_(*m_logger, "\"TStamp\" property is required, value = %s", value.c_str());
					if (res && !(value.empty()))
					{
						appendValue(RTPPropertiesXml, S_propertyName[e_tstamp], value.c_str());
					}
					break;
				}
			case e_TNDS_type:
				{
					res = node.GetProperty(e_type, value); // RTPProperties::Type
					LOG_INFO_(*m_logger, "\"Type\" property is required, value = %s", value.c_str());
					appendValue(RTPPropertiesXml, S_propertyName[e_type], value.c_str());
					break;
				}
			case e_TNDS_verNo:
				{
					res = node.GetProperty(e_verNo, value); // RTPProperties::VerNo
					LOG_INFO_(*m_logger, "\"VerNo\" property is required, value = %s", value.c_str());
					if (res && !(value.empty()))
					{
						appendValue(RTPPropertiesXml, S_propertyName[e_verNo], value.c_str());
					}
					break;
				}
			case e_TNDS_value:
				{
					if (node.IsLeaf()) // if node is leaf, add value
					{
						res = node.GetContent(value);
						LOG_INFO_(*m_logger, "\"Value\" property is required, value = %s", value.c_str());

						formattedVal = "";
						Funambol::Formatter::formatValue(formattedVal, value.c_str());
						appendValue(nodeXml, S_value, formattedVal.c_str());
					}
					break;
				}
			default:
				break;
			}
		}
		RTPPropertiesXml->append((S_beginScope + S_slash + S_RTProperties + S_endScope).c_str());
		nodeXml->append(RTPPropertiesXml);
		Funambol::deleteStringBuffer(&RTPPropertiesXml);
		LOG_DEBUG_(*m_logger, "LEAVE >> MOTreeManager::appendRequiredProperties");
	}
	//-------------------------------------------------------------------------------------------

	void MOTreeManager::appendValue(Funambol::StringBuffer*& nodeXml, const String& target, const char* value)
	{
		Funambol::StringBuffer* valueBuffer = Funambol::Formatter::getValue(target.c_str(), value);
		nodeXml->append(valueBuffer);
		Funambol::deleteStringBuffer(&valueBuffer);
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::GetValue(const URI& uri, String& value, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_WARNING_(*m_logger, "Node wasn't found");
            res = e_NotFound;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		if (!node.IsLeaf())
		{
			LOG_WARNING_(*m_logger, "Failed to get value from interior node");
            res = e_ForbiddenCommand;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		res = checkOperationPermission("Get", node, serverId);
		if (res != e_Ok)
		{
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		    return res;
        }

		bool res_content = node.GetContent(value);
		if (!res_content)
		{
			LOG_ERROR_(*m_logger, "Failed to get content from leaf node with uri = '%s'", uri.c_str());
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		LOG_DEBUG_(*m_logger, "Leaf node with uri = '%s' has content = '%s'", uri.c_str(), fitString(value.c_str()));
        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::GetPropertyValue(const URI& uri, const String& property_name, String& value, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', property name = '%s', server id = '%s'", uri.c_str(), property_name.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_WARNING_(*m_logger, "Node: '%s' wasn't found", uri.c_str());
            res = e_NotFound;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		Property prop;
		if (!GetPropertyByName(property_name, prop))
		{
			LOG_WARNING_(*m_logger, "Property name: '%s' not valid", property_name.c_str());
            res = e_OptionalFeatureNotSupported;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		res = checkOperationPermission("Get", node, serverId);
		if (res != e_Ok)
		{
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
            return res;
        }

		if (e_size == prop && !(node.IsLeaf()))
		{
			LOG_WARNING_(*m_logger, "Failed to get content size on interior node");
            res = e_ForbiddenCommand;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}
		bool resGetProperty = node.GetProperty(prop, value);
		if (!resGetProperty)
		{
			LOG_ERROR_(*m_logger, "Failed to get property value. uri = '%s', property name = '%s'", uri.c_str(), property_name.c_str());
            res = e_OptionalFeatureNotSupported;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		LOG_(*m_logger, "Node property: '%s' with uri = '%s' has content = '%s'", property_name.c_str(), uri.c_str(), fitString(value.c_str()));
        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return value: %s, Return status: %d", fitString(value.c_str()), res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::addIntermediateInteriorNodes(const URI& source, const char* serverId, bool isUpdateRequired)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> source = '%s', server id = '%s', update required = %d", source.c_str(), serverId ? serverId : c_RootAccessID, isUpdateRequired);
		StatusCode res = e_Failed;
		size_t pos = 0;
		if (source.find(S_rootNode) == 0)
		{   // skip "./"
			pos = 2;
		}
		String subPath;
		while ((pos = source.find(S_seperator, pos)) != URI::npos)
		{
			subPath = source.substr(0, pos++);
			Node node(subPath, m_dataStorage);
			if (node.Exist())
			{
                LOG_DEBUG_(*m_logger, "Intermediate node: %s already exist. No need add", node.GetPath().c_str());
				continue;
			}

            LOG_DEBUG_(*m_logger, "Add next intermediate node: %s", node.GetPath().c_str());

			if (!m_transaction->Data().BackupAdd(node.GetPath(), isUpdateRequired))
			{
                res = e_CommandFailed;
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
				return res;
			}

			node.SetProperty(e_format, S_interiorNode);
			node.SetProperty(e_type, S_interiorNode);
			addACLPropertyForNewNode(node, false, serverId);
		}
        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::Add(const URI& uri, const Funambol::Item& item, const char* serverId, bool isUpdateRequired/* = true*/)
	{
		LOG_(*m_logger, "ENTER >> uri = '%s', server id = '%s', update required = %d", uri.c_str(), serverId ? serverId : c_RootAccessID, isUpdateRequired);

		StatusCode res = e_CommandFailed;
		startTransaction(uri);
		CompleteItem(item);

		while (true)
		{
			Node node(uri, m_dataStorage);
			String content;
			if (node.Exist())
			{
				LOG_(*m_logger, "Node with uri = '%s' already exists", uri.c_str());
				res = e_AlreadyExists;
				break;
			}

			res = checkOperationPermission("Add", node, serverId);
			if (res != e_Ok)
			{
				break;
			}

			res = addIntermediateInteriorNodes(uri, serverId, isUpdateRequired);
			if ((res != e_Ok) && (res != e_AlreadyExists))
			{
				break;
			}
			else
			{
				res = e_Ok;
			}

			if (!m_transaction->Data().BackupAdd(uri, isUpdateRequired))
			{
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: backup add failed", e_CommandFailed);
				return e_CommandFailed;
			}

			Funambol::Item& tmpItem = const_cast<Funambol::Item&>(item);
			if (tmpItem.getMeta() == NULL)
			{
				LOG_ERROR_(*m_logger, "Empty meta is received");
				res = e_CommandFailed;
				break;
			}
			// set format of node
			const char* chr_value;
			chr_value = tmpItem.getMeta()->getFormat();
			if (chr_value)
			{
				node.SetProperty(e_format, chr_value);
			}
			else
			{
				node.SetProperty(e_format, "chr");
			}

			const bool isLeaf = node.IsLeaf();

			// set type of node
			chr_value = tmpItem.getMeta()->getType();
			if (chr_value)
			{
				node.SetProperty(e_type, chr_value);
			}
			else
			{
				if (isLeaf)
				{
					node.SetProperty(e_type, "text/plain");
				}
			}

			// set ACL of leaf node. Must be empty value
			node.SetProperty(e_ACL, "");

			if (isLeaf)
			{
				// set content of leaf node
				node.SetContent(tmpItem.getData()->getData());
				LOG_(*m_logger, "Leaf node is added. URI = '%s'", uri.c_str());
			}
			else // node
			{
				LOG_(*m_logger, "Interior node is added. URI = '%s'", uri.c_str());
			}

			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	StatusCode MOTreeManager::Delete(const URI& uri, const char* serverId)
	{
		LOG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);

		StatusCode res = e_CommandFailed;
		startTransaction(uri);

		while (true)
		{
			Node node(uri, m_dataStorage);
			if (!node.Exist())
			{
				LOG_WARNING_(*m_logger, "Node with uri = '%s' doesn't exist", uri.c_str());
				res = e_NotFound;
				break;
			}

			if (node.IsPermanent())
			{
				LOG_WARNING_(*m_logger, "Try to delete permanent node with uri = '%s'", uri.c_str());
				res = e_ForbiddenCommand;
				break;
			}

			res = checkOperationPermission("Delete", node, serverId);
			if (res != e_Ok)
			{
				break;
			}

			if (!m_transaction->Data().BackupDelete(uri))
			{
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: backup delete failed", e_CommandFailed);
				return e_CommandFailed;
			}

			res = (node.Delete()) ? e_Ok : e_CommandFailed;
			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::Replace(const URI& uri, const Funambol::Item& item, const char* serverId, bool isUpdateRequired /*= true*/)
	{
		LOG_(*m_logger, "ENTER >> uri = '%s', server id = '%s', update required = %d", uri.c_str(), serverId ? serverId : c_RootAccessID, isUpdateRequired);

		StatusCode res = e_CommandFailed;
		startTransaction(uri);
		CompleteItem(item);

		while (true)
		{
			Node node(uri, m_dataStorage);
			if (!node.Exist())
			{
				LOG_WARNING_(*m_logger, "Node with uri = '%s' doesn't exist", uri.c_str());
				res = e_NotFound;
				break;
			}

			res = checkOperationPermission("Replace", node, serverId);
			if (res != e_Ok)
			{
				break;
			}

			Funambol::Item& tmpItem = const_cast<Funambol::Item&>(item);
			bool check_res = checkFormat(node, tmpItem);
			if (!check_res)
			{
				LOG_ERROR_(*m_logger, "Node with uri = '%s' already exists with another format", uri.c_str());
				res = e_AlreadyExists;
				break;
			}
			if (node.IsLeaf())
			{
				String new_value = tmpItem.getData()->getData();
				if (!m_transaction->Data().BackupReplace(uri, new_value, isUpdateRequired))
				{
                    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: backup replace failed", e_CommandFailed);
					return e_CommandFailed;
				}

				node.SetContent(tmpItem.getData()->getData());
				LOG_(*m_logger, "Data of leaf node was replaced");
				res = e_Ok;
				break;
			}
			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::replaceNode(const URI& uri, const String& sibling_new_name)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', new sibling name = %s", uri.c_str(), sibling_new_name.c_str());

		// replace by copy and delete in one transaction
		String path_name, node_name;
		ParseNodePath(uri, path_name, node_name);
		Node check_node(path_name + sibling_new_name, m_dataStorage);
		if (check_node.Exist())
		{
			LOG_WARNING_(*m_logger, "Node: '%s' already exist", (path_name + sibling_new_name).c_str());
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", e_AlreadyExists);
			return e_AlreadyExists;
		}

		StatusCode res = Copy(uri, path_name + sibling_new_name);
		if (res == e_Ok)
		{
			res = Delete(uri);
		}

        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	struct add_info_replace_property
	{
		const String* value;
		const String* name;
	};
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::ReplacePropertyValue(const URI& uri, const String& property_name, const String& new_value, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', property name = %s, new value = '%s', server id = '%s'", uri.c_str(), property_name.c_str(), new_value.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_CommandFailed;
		startTransaction(uri);

		while (true)
		{
			Node node(uri, m_dataStorage);
			if (!node.Exist())
			{
				LOG_WARNING_(*m_logger, "Node: '%s' wasn't found", uri.c_str());
				res = e_NotFound;
				break;
			}
			Property prop;
			if (!GetPropertyByName(property_name, prop))
			{
				LOG_WARNING_(*m_logger, "Property name: '%s' not valid", property_name.c_str());
				res = e_NotFound;
				break;
			}
			if (!IsPropertyReplacementSupported(prop))
			{
				LOG_WARNING_(*m_logger, "Property: '%s' replacement not supported", property_name.c_str());
				res = e_ForbiddenCommand;
				break;
			}

			if ((prop == e_name) && (node.IsPermanent()))
			{
				res = e_ForbiddenCommand;
				break;
			}

			res = checkOperationPermission((prop == e_ACL) ? ("Replace ACL") : ("Replace"), node, serverId);
			if (res != e_Ok)
			{
				break;
			}

			add_info_replace_property arg;
			arg.value = &new_value;
			arg.name = &property_name;

			if (prop != e_name)
			{
				if (!m_transaction->Data().BackupReplaceProperty(uri, property_name, new_value))
				{
                    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: backup replace property failed", e_CommandFailed);
					return e_CommandFailed;
				}
			}

			switch (prop)
			{
			case e_ACL:
			{
				node.SetProperty(e_ACL, new_value.c_str());
				break;
			}
			case e_name:
			{
				res = replaceNode(uri, new_value);
				break;
			}
			case e_title:
			{
				node.SetProperty(e_title, new_value.c_str());
				break;
			}
			default:
			{
				break;
			}
			};

			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::Copy(const URI& source, const URI& dest, const char* serverId)
	{
		LOG_(*m_logger, "ENTER >> source = %s, destination = '%s', server id = '%s'", source.c_str(), dest.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_CommandFailed;
		while (true)
		{
			Node sourceNode(source, m_dataStorage);
			if (!sourceNode.Exist())
			{
				LOG_WARNING_(*m_logger, "source node: '%s' wasn't found", source.c_str());
				res = e_NotFound;
				break;
			}
			Node destNode(dest, m_dataStorage);
			res = copyNode(sourceNode, destNode, serverId);
			break;
		}
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------

    StatusCode MOTreeManager::SetPropertyValue(const URI& uri, const String& property_name, const String& value)
    {
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', property name = '%s', property value = '%s'", uri.c_str(), property_name.c_str(), value.c_str());
		StatusCode res = e_Failed;
		Node node(uri, m_dataStorage);
		if (!node.Exist())
		{
			LOG_WARNING_(*m_logger, "Node: '%s' wasn't found", uri.c_str());
            res = e_NotFound;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		Property prop;
		if (!GetPropertyByName(property_name, prop))
		{
			LOG_WARNING_(*m_logger, "Property name: '%s' not valid", property_name.c_str());
            res = e_OptionalFeatureNotSupported;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}


		if (e_size == prop && !(node.IsLeaf()))
		{
			LOG_WARNING_(*m_logger, "Failed to set content size on interior node");
            res = e_ForbiddenCommand;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		node.SetProperty(prop, value);

        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
    }

	//-------------------------------------------------------------------------------------------

    StatusCode MOTreeManager::ExecPermitted(const URI& path, const char* serverId)
    {
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", path.c_str(), serverId ? serverId : c_RootAccessID);
        StatusCode res = e_Ok;

        Node pathNode(path, m_dataStorage);
        if (!pathNode.Exist())
        {
            LOG_WARNING_(*m_logger, "node: '%s' wasn't found", path.c_str());
            res = e_NotFound;
        }

        if (res == e_Ok)
        {
            res = checkOperationPermission("Exec", pathNode, serverId);
        }

        if (res == e_Ok)
        {
            LOG_INFO_(*m_logger, "exec permission on node %s enabled", path.c_str());
        }
        else
        {
            LOG_INFO_(*m_logger, "exec permission on node %s disabled", path.c_str());
        }

        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
        return res;
    }
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::copyNode(const Node& source, Node& dest, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> source = %s, destination = '%s', server id = '%s'", source.GetPath().c_str(), dest.GetPath().c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_CommandFailed;

		bool source_leaf = source.IsLeaf();
		bool dest_exist = dest.Exist();
		bool dest_leaf = dest_exist && dest.IsLeaf();

		if (source_leaf)
		{
    		LOG_DEBUG_(*m_logger, "source is leaf");
			if (dest_exist && dest_leaf)
			{
        		LOG_DEBUG_(*m_logger, "destination exist and is leaf");
				if ((res = checkOperationPermission("Copy", dest, serverId)) == e_Ok)
				{
					res = copyLeafSourceToExistingDestination(source, dest);
				}
			}
			else if (dest_exist && (!dest_leaf))
			{
        		LOG_DEBUG_(*m_logger, "destination exist and is not leaf");
				LOG_WARNING_(*m_logger, "Try copy leaf node to existing path which link to interior node");
				res = e_CommandFailed;
			}
			else if (!dest_exist)
			{   // need create new leaf node
        		LOG_DEBUG_(*m_logger, "destination not exist. Need create new leaf node");
				res = copyLeafSourceToNoExistingDestination(source, dest, serverId);
			}
		}
		else
		{   // source is not leaf node
    		LOG_DEBUG_(*m_logger, "source is not leaf");
			res = copyInteriorSource(source, dest.GetPath(), serverId);
		}

        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::copyLeafSourceToExistingDestination(const Node& source, Node& dest)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> source = %s, destination = %s", source.GetPath().c_str(), dest.GetPath().c_str());
		StatusCode res = e_CommandFailed;

		startTransaction(source.GetPath());

		String sourceValue;
		String destValue;
		bool bool_res = getFormatValue(source, sourceValue) && getFormatValue(dest, destValue);
		if (bool_res)
		{
			if (sourceValue == destValue)
			{
			    if (sourceValue != S_interiorNode)
			    {
			        String content;
			        bool bool_res = source.GetContent(content);
			        if (bool_res)
			        {
				        if (!m_transaction->Data().BackupReplace(dest.GetPath(), content))
				        {
		                    LOG_ERROR_(*m_logger, "backup replace failed");
					        res = e_CommandFailed;
				        }

				        dest.SetContent(content);
				        res = e_Ok;
			        }
			    }
			    else
			    {
            		LOG_ERROR_(*m_logger, "source is interior node");
			    }
			}
			else
			{
                LOG_ERROR_(*m_logger, "source and destination formats is not equal");
			}
		}
		else
		{
            LOG_ERROR_(*m_logger, "can't get source (destination) format");
		}

		finishTransaction(res);

        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::copyLeafSourceToNoExistingDestination(const Node& source, Node& dest, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> source = %s, destination = '%s', server id = '%s'", source.GetPath().c_str(), dest.GetPath().c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_CommandFailed;
		startTransaction(source.GetPath());

		while (true)
		{
			// get metadata from source
			Funambol::Item item;
			// check permission by Get command
			if ((res = Get(source.GetPath(), item, serverId)) != e_Ok)
			{
				LOG_ERROR_(*m_logger, "Can't get metadata from source: %s", source.GetPath().c_str());
				res = e_CommandFailed;
				break;
			}
			String content;
			source.GetContent(content);
			Funambol::ComplexData complex_content(content.c_str());
			item.setData(&complex_content);

			// check permission by Add command
			if ((res = Add(dest.GetPath(), item, serverId)) != e_Ok)
			{
				LOG_ERROR_(*m_logger, "Can't add destination node: %s", dest.GetPath().c_str());
				res = e_CommandFailed;
				break;
			}
			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::copyInteriorSource(const Node& source, const URI& dest, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> source = %s, destination = '%s', server id = '%s'", source.GetPath().c_str(), dest.c_str(), serverId ? serverId : c_RootAccessID);
		StatusCode res = e_CommandFailed;
		startTransaction(source.GetPath());

		while (true)
		{
			ChildrenPaths childs;
			String child_path;
			if (source.GetChildren(childs))
			{
				for (ChildrenPaths::iterator i = childs.begin(); i != childs.end(); ++i)
				{
					child_path = source.GetPath() + "/" + (*i);
					Node child(child_path, m_dataStorage);
					if (child.Exist())
					{
						if (child.IsLeaf())
						{
							Node dest_node(dest + "/" + (*i), m_dataStorage);
							res = copyLeafSourceToNoExistingDestination(child, dest_node, serverId);
						}
						else
						{
							URI child_dest = dest + "/" + (*i);
							res = copyInteriorSource(child, child_dest);
						}

						if (res != e_Ok)
						{
							break;
						}
					}
					else
					{
						LOG_ERROR_(*m_logger, "node : %s is not existing, but must be existing", child_path.c_str());
						res = e_CommandFailed;
						break;
					}
				}
			}
			break;
		}

		finishTransaction(res);
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	bool MOTreeManager::checkFormat(const Node& node, Funambol::Item& item)
	{
		String value;
		bool res = getFormatValue(node, value);
		if (!res)
		{
			return false;
		}
		return value == item.getMeta()->getFormat();
	}
	//-------------------------------------------------------------------------------------------
	bool MOTreeManager::getFormatValue(const Node& node, String& value)
	{
		bool res = node.GetProperty(e_format, value);
		if (!res)
		{
			LOG_ERROR_(*m_logger, "Failed to get format property");
			return false;
		}
		return true;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::GetChildren(const URI& uri, ChildrenPaths& children, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', server id = '%s'", uri.c_str(), serverId ? serverId : c_RootAccessID);
		Node node(uri, m_dataStorage);
		StatusCode res = node.GetChildren(children) ? e_Ok : e_CommandFailed;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::checkOperationPermission(const String& operation, const Node& node, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', operation = '%s', server id = '%s'", node.GetPath().c_str(), operation.c_str(), serverId ? serverId : c_RootAccessID);

		StatusCode res = e_PermissionDenied;

		if (serverId == 0)
		{
            LOG_DEBUG_(*m_logger, "server id represent root user. Skip checking");
			res = e_Ok;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
            return res;
		}

        if ((res = checkAccessTypePermission(operation, node, serverId)) != e_Ok)
        {
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: access type check not success", res);
            return res;
        }

		std::auto_ptr<Node> operation_node(0);

		if (operation.compare("Add") == 0)
		{
			operation_node.reset(node.GetExistingParent());
			if (!operation_node.get())
			{
                res = e_CommandFailed;
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: failed to get existing parent node", res);
				return res;
			}
		}
		if (operation.compare("Replace ACL") == 0)
		{
			if (node.IsLeaf())
			{   // special algorithm in case replacement ACL of leaf node
				operation_node.reset(GetExistingParentWithACL(&node));
				if (!operation_node.get())
				{
                    res = e_CommandFailed;
                    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: failed to get existing parent node with ACL", res);
					return res;
				}
			}
			(const_cast<String&>(operation)) = "Replace";
		}
		res = checkUnifiedOperationPermission(operation, (operation_node.get() != 0) ? (*(operation_node.get())) : (node), serverId);
		if (res != e_Ok)
		{
			if (res == e_PermissionDenied)
			{
				LOG_WARNING_(*m_logger, "Access denied for %s operation on node: '%s'", operation.c_str(), node.GetPath().c_str());
			}
			else
			{
				LOG_ERROR_(*m_logger, "Check operation permission for %s operation on node: '%s' failed", operation.c_str(), node.GetPath().c_str());
			}
		}
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::checkUnifiedOperationPermission(const String& operation, const Node& node, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', operation = '%s', server id = '%s'", node.GetPath().c_str(), operation.c_str(), serverId ? serverId : c_RootAccessID);

		StatusCode res = e_Failed;

		String strACL;
		if (!node.GetProperty(e_ACL, strACL))
		{   // check if is not empty ACL in current node
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: node has no ACL property", res);
			return res;
		}
		if (strACL.compare("") == 0)
		{   // if no acl in current node, get parent with acl
			std::auto_ptr<Node> parent_with_acl(GetExistingParentWithACL(&node));
			if (!parent_with_acl.get())
			{
                res = e_CommandFailed;
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: node ACL is empty, and get existing parent with ACL failed", res);
				return res;
			}
            LOG_DEBUG_(*m_logger, "In case of empty ACL, check ACL from parent with not empty ACL. Parent: %s", parent_with_acl->GetPath().c_str());
			parent_with_acl->GetProperty(e_ACL, strACL);
		}
		if (strACL.compare("") == 0)
		{
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d, reason: node ACL property is empty", res);
			return res;
		}
		std::vector<String> Ids;
		retrieveOperationServersIdsFromACL(strACL, operation, Ids);
		for (std::vector<String>::iterator ptr = Ids.begin(); ptr != Ids.end(); ++ptr)
		{
            LOG_DEBUG_(*m_logger, "check id:%s", ptr->c_str());
			if (((*ptr).compare(serverId) == 0) || ((*ptr).compare("*") == 0))
			{
                res = e_Ok;
                LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
				return res;
			}
		}
        res = e_PermissionDenied;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	void MOTreeManager::retrieveOperationServersIdsFromACL(const String& ACL, const String& operation, std::vector<String>& Ids)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> ACL = %s, operation = %s", ACL.c_str(), operation.c_str());
		String op = operation + "=";
		size_t pos_begin = 0, pos_end = 0;
		String command;
		while ((pos_begin != String::npos) && (pos_end != String::npos))
		{
			if ((pos_begin = ACL.find(op, pos_begin)) == String::npos)
			{
				break;
			}
			pos_begin += op.length();
			pos_end = ACL.find("&", pos_begin);
			command = ACL.substr(pos_begin, (pos_end == String::npos) ? (String::npos) : (pos_end - pos_begin));
			retrieveServerIdsFromACLCommand(command, Ids);
			if (pos_end != String::npos)
			{
				pos_begin = pos_end + 1;
			}
		}
		bool isPresentNonStarId = false;
		bool isPresentStarId = false;
		for (size_t i = 0; i < Ids.size(); ++i)
		{
			if (Ids[i].compare("*") != 0)
			{
				isPresentNonStarId = true;
			}
			else
			{
				isPresentStarId = true;
			}
			if (isPresentNonStarId && isPresentStarId)
			{
				break;
			}
		}
		if (isPresentNonStarId && isPresentStarId)
		{
			Ids.erase(std::remove(Ids.begin(), Ids.end(), "*"), Ids.end());
		}

        String log_res;
        for (std::vector<String>::const_iterator ptr = Ids.begin(); ptr != Ids.end(); ++ptr)
        {
            log_res += (*ptr);
            log_res += ' ';
        }

        LOG_DEBUG_(*m_logger, "LEAVE << Ids count: %d, Result: %s", Ids.size(), log_res.c_str());
	}
	//-------------------------------------------------------------------------------------------
	void MOTreeManager::retrieveServerIdsFromACLCommand(const String& command, std::vector<String>& Ids)
	{
		size_t pos_begin = 0, pos_end = 0;
		String Id;
		while ((pos_begin != String::npos) && (pos_end != String::npos))
		{
			pos_end = command.find("+", pos_begin);
			Id = command.substr(pos_begin, (pos_end == String::npos) ? (String::npos) : (pos_end - pos_begin));
			Ids.push_back(Id);
			if (pos_end != String::npos)
			{
				pos_begin = pos_end + 1;
			}
		}
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::addACLPropertyForNewNode(Node& node, bool isLeaf, const char* serverId)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> uri = '%s', is leaf = %d, server id = '%s'", node.GetPath().c_str(), isLeaf, serverId ? serverId : c_RootAccessID);

		StatusCode res = e_Failed;

		if (!serverId)
		{
			node.SetProperty(e_ACL, "");
            res = e_Ok;
            LOG_DEBUG_(*m_logger, "LEAVE <<Return status: %d", res);
			return res;
		}
		// check if existing parent has Replace property
		std::auto_ptr<Node> existing_parent(GetExistingParentWithACL(&node));
		if (!existing_parent.get())
		{
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE <<Return status: %d, reason: can't get existing parent with ACL", res);
			return res;
		}
		String strACL;
		if (!existing_parent->GetProperty(e_ACL, strACL))
		{
            res = e_CommandFailed;
            LOG_DEBUG_(*m_logger, "LEAVE <<Return status: %d, reason: can't extract ACL from existing parent with ACL", res);
			return res;
		}

		std::vector<String> Ids;
		retrieveOperationServersIdsFromACL(strACL, "Replace", Ids);

		bool addEmptyACL = false;
		for (std::vector<String>::iterator i = Ids.begin(); i != Ids.end(); ++i)
		{
			if ((i->compare(serverId) == 0) || (i->compare("*") == 0))
			{
				addEmptyACL = true;
				break;
			}
		}
		if (addEmptyACL)
		{   // parent has Replace for server id in ACL so ACL of new node is empty
			node.SetProperty(e_ACL, "");
		}
		else
		{
			String newACL = "";
			if (!isLeaf)
			{
				newACL =   "Add=";
				newACL = newACL + serverId;
				newACL = newACL + "&";
			}
			newACL = newACL + "Get=";
			newACL = newACL + serverId;
			newACL = newACL + "&Delete=";
			newACL = newACL + serverId;
			newACL = newACL + "&Replace=";
			newACL = newACL + serverId;
			node.SetProperty(e_ACL, newACL);
		}

        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::StartTransaction()
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::StartTransaction");

		StatusCode res = e_Failed;

		if (m_transaction->StartTransaction())
		{
			LOG_(*m_logger, "Transaction is started");
		}
		else
		{
			LOG_(*m_logger, "can't start transaction");
		    res = e_CommandFailed;
		    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		res = e_Ok;
		LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::Rollback()
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::Rollback");

		StatusCode res = e_Failed;

		if (m_transaction->Rollback())
		{
			LOG_(*m_logger, "transaction is rollbacked");
		}
		else
		{
			LOG_(*m_logger, "transaction rollback failed");
		    res = e_CommandFailed;
		    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

		res = e_Ok;
		LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	StatusCode MOTreeManager::Commit()
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::Commit");

		StatusCode res = e_Failed;

		if (m_transaction->Commit())
		{
			LOG_(*m_logger, "transaction is commited");
		}
		else
		{
			LOG_(*m_logger, "transaction commit failed");
		    res = e_CommandFailed;
		    LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
			return res;
		}

        res = e_Ok;
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
		return res;
	}
	//-------------------------------------------------------------------------------------------
	void MOTreeManager::startTransaction(const URI& uri)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::startTransaction");
		if (m_needFinishThansaction)
		{
			LOG_WARNING_(*m_logger, "There is not finished transaction.");
		}

		m_transactionURI = uri;

		m_needFinishThansaction = false;
		if (m_transaction->StartTransaction())
		{   // we currently start transaction. Need commit/rollback in method end
			LOG_(*m_logger, "Transaction is started for node with uri = '%s'", uri.c_str());
			m_needFinishThansaction = true;
		}
		LOG_DEBUG_(*m_logger, "LEAVE <<");
	}
	//-------------------------------------------------------------------------------------------
	void MOTreeManager::finishTransaction(StatusCode operationResult)
	{
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::finishTransaction");
		if (m_needFinishThansaction || (operationResult != e_Ok))
		{
			if (operationResult == e_Ok)
			{
				LOG_(*m_logger, "Transaction is finished. Status : commit transaction");
				if (!m_transaction->Commit())
				{
					LOG_ERROR_(*m_logger, "Transaction commit failed");
				}

				if (m_pch && m_pch->GetNotificationCenter())
				{
					LOG_ERROR_(*m_logger, "Notify provisioning update for URI='%s'", m_transactionURI.c_str());
 					m_pch->GetNotificationCenter()->ProvisioningUpdate(m_transactionURI.c_str());
				}
			}
			else
			{
				LOG_(*m_logger, "Transaction is finished. Status : rollback transaction");
				m_transaction->Rollback();
			}
			m_needFinishThansaction = false;
		}
		LOG_DEBUG_(*m_logger, "LEAVE <<");
	}
	//-------------------------------------------------------------------------------------------
    bool MOTreeManager::getAccessTypeTree(const StringMap& settings, ProfileComponentsHolder& pch)
    {
		LOG_DEBUG_(*m_logger, "ENTER >> MOTreeManager::getAccessTypeTree");

        bool res = false;
        const char* path = (const_cast<StringMap&>(settings).get(c_accessTypeParam)).c_str();
        //const char* path = "/etc/Funambol/DMClient/DataStorage/AccessType/StdObjs.at";
        if (path)
        {
            FILE* file = openAccessTypeFile(path);
            if (file)
            {
                if (readAccessTypeTree(file))
                {
                    res = true;
                }
                else
                {
                    LOG_WARNING_(*m_logger, "can't read access type tree from file. Path = %s", path);
                }
                fclose(file);
            }
            else
            {
                LOG_WARNING_(*m_logger, "can't open file with access type tree representation. Path = %s", path);
            }
        }
        else
        {
            LOG_WARNING_(*m_logger, "failed to read path to file with access type tree representation");
        }
        LOG_DEBUG_(*m_logger, "LEAVE << Result: %d", res);
        return res;
    }
	//-------------------------------------------------------------------------------------------
    StatusCode MOTreeManager::checkAccessTypePermission(const String& operation, const Node& node, const char* serverId)
    {
		LOG_DEBUG_(*m_logger, "ENTER >> path = '%s' operation: '%s', server id = '%s'", node.GetPath().c_str(), operation.c_str(), serverId ? serverId : c_RootAccessID);

        StatusCode res = e_Ok;

        String keyNameOptimized = node.GetPath();

        if ((node.GetPath()[0] == '.') && (node.GetPath()[1] == '/'))
        {
            keyNameOptimized = node.GetPath().substr(2);
        }
        else if ((node.GetPath()[0] == '/'))
        {
            keyNameOptimized = node.GetPath().substr(1);
        }
        if (keyNameOptimized[keyNameOptimized.length()-1] != '/')
        {
            keyNameOptimized = keyNameOptimized + '/';
        }

        AccessTypeOperation opp = ConvertFromStringToAccessTypeOperation(operation);

        String interior_name;

        size_t pos_begin = 0;
        size_t pos_end = 0;

        AccessTypeTree* current = &m_AccessTypeTree;
        while (true)
        {
            pos_end = keyNameOptimized.find('/', pos_begin);
            if (pos_end == String::npos)
            {
                break;
            }

            interior_name = keyNameOptimized.substr(pos_begin, pos_end - pos_begin);
            bool isLeaf = ((pos_end + 1) == keyNameOptimized.length());

            if (current->m_childs.size())
            {
                for (std::list<AccessTypeTree>::iterator ptr = current->m_childs.begin(); ptr != current->m_childs.end(); ++ptr)
                {
                    String tmp_name = ptr->m_Name;
                    if ((ptr->m_Name.compare(interior_name) == 0) || (ptr->m_Name.compare("*") == 0))
                    {   // we already insert this key to tree
                        current = &(*ptr);
                        if (isLeaf && (!current->m_operations.empty()))
                        {   // we have information about access type for node
                            bool permitted = false;
                            for (std::vector<AccessTypeOperation>::const_iterator op_ptr = current->m_operations.begin();
                                op_ptr != current->m_operations.end(); ++ op_ptr)
                            {
                                if (*op_ptr == opp)
                                {
                                    permitted = true;
                                    break;
                                }
                            }
                            res = (permitted) ? e_Ok : e_ForbiddenCommand;
                        }
                        break;
                    }

                    std::list<AccessTypeTree>::iterator check_finish_iterator = ptr;
                    if ((++check_finish_iterator) == current->m_childs.end())
                    {   // no children with appropriate name. No AccessType information
                        res = e_Ok;
                        break;
                    }
                }
            }
            else
            {   // no children. No AccessType information
                res = e_Ok;
                break;
            }
            pos_begin = pos_end + 1;
        }
        LOG_DEBUG_(*m_logger, "LEAVE << Return status: %d", res);
        return res;
    }
	//-------------------------------------------------------------------------------------------
    FILE* MOTreeManager::openAccessTypeFile(const String& path)
    {
		const char* ptr = path.c_str();
#ifdef PLATFORM_WINDOWS
		const size_t BUFFER_SIZE  = 32767;
		std::vector<char>  buf;
		buf.resize(BUFFER_SIZE);
		// MSDN: When using ANSI strings, the buffer size should be the string length, plus terminating null character, plus one
		::ExpandEnvironmentStringsA(path.c_str(), &buf[0], buf.size() - 2);
		ptr = &buf[0];
#endif

		LOG_DEBUG_(*m_logger, "Expanded path: %s", ptr);
        return fopen(ptr, "r");
    }
	//-------------------------------------------------------------------------------------------
    bool MOTreeManager::readAccessTypeTree(FILE* file)
    {
        Buffer key(c_maxItemPathLength);

        char* read_result = 0;
        bool res = false;

        m_AccessTypeTree.m_Name = String(".");

        while (true)
        {
            read_result = fgets ((char*)key.GetPointer(), key.Size(), file);
            if (!read_result)
            {
                if (feof(file) != 0)
                {   // end of file
                    res = true;
                    break;
                }
                else
                {   // read error
                    res = false;
                    break;
                }
            }

            read_result = (char*)key.GetPointer();
            size_t key_size = strlen(read_result);
            // delete \n from end of key
            if (read_result[key_size-1] == '\n')
            {
                read_result[key_size-1] = '\0';
                --key_size;
            }

            if (!appendAccessTypeKey(read_result))
            {
                // log error
                res = false;
                break;
            }
        }
        return res;
    }
	//-------------------------------------------------------------------------------------------
    bool MOTreeManager::appendAccessTypeKey(const String& key)
    {
        size_t pos_end = 0;

        pos_end = key.find("=");
        if (pos_end == String::npos)
        {
            // log error
            return false;
        }

        String key_name = key.substr(0, pos_end);
        String key_operations = key.substr(pos_end + 1);

        std::vector<AccessTypeOperation> operations;
        if (!formAccessTypeOperationsFromKey(key_operations, operations))
        {
            // log error
            return false;
        }

        if (!addAccessTypeKeyToTree(key_name, operations))
        {
            // log error
            return false;
        }

        return true;
    }
	//-------------------------------------------------------------------------------------------
    bool MOTreeManager::formAccessTypeOperationsFromKey(
        const String& strOperations, std::vector<AccessTypeOperation>& operations)
    {
        size_t pos_begin = 0;
        size_t pos_end = 0;

        String strOperationsWithEndDelimiter = strOperations + "&";

        String strOperation;
        while (true)
        {
            pos_end = strOperationsWithEndDelimiter.find("&", pos_begin);
            if (pos_end == String::npos)
            {
                return true;
            }

            strOperation = strOperationsWithEndDelimiter.substr(pos_begin, pos_end - pos_begin);

            if (strOperation.compare("Get") == 0)
            {
                operations.push_back(e_atGet);
            }
            else if (strOperation.compare("Add") == 0)
            {
                operations.push_back(e_atAdd);
            }
            else if (strOperation.compare("Replace") == 0)
            {
                operations.push_back(e_atReplace);
            }
            else if (strOperation.compare("Delete") == 0)
            {
                operations.push_back(e_atDelete);
            }
            else if (strOperation.compare("Exec") == 0)
            {
                operations.push_back(e_atExec);
            }
            else
            {
                // warning
            }

            pos_begin = pos_end + 1;
        }

        return true;
    }
	//-------------------------------------------------------------------------------------------
    bool MOTreeManager::addAccessTypeKeyToTree(
        const String& keyName, const std::vector<AccessTypeOperation>& operations)
    {
        size_t pos_begin = 0;
        size_t pos_end = 0;

        String keyNameOptimized = keyName;

        if ((keyName[0] == '.') && (keyName[1] == '/'))
        {
            keyNameOptimized = keyName.substr(2);
        }
        else if ((keyName[0] == '/'))
        {
            keyNameOptimized = keyName.substr(1);
        }

        if (keyNameOptimized[keyNameOptimized.length()-1] != '/')
        {
            keyNameOptimized = keyNameOptimized + '/';
        }

        String interior_name;

        AccessTypeTree* current = &m_AccessTypeTree;
        while (true)
        {
            pos_end = keyNameOptimized.find('/', pos_begin);
            if (pos_end == String::npos)
            {
                break;
            }

            interior_name = keyNameOptimized.substr(pos_begin, pos_end - pos_begin);
            bool isLeaf = ((pos_end + 1) == keyNameOptimized.length());

            if (current->m_childs.size())
            {
                for (std::list<AccessTypeTree>::iterator ptr = current->m_childs.begin(); ptr != current->m_childs.end(); ++ptr)
                {
                    if (ptr->m_Name.compare(interior_name) == 0)
                    {   // we already insert this ket to tree
                        current = &(*ptr);
                        if (isLeaf)
                        {
                            current->m_operations = operations;
                        }
                        break;
                    }

                    std::list<AccessTypeTree>::iterator check_finish_iterator = ptr;
                    if ((++check_finish_iterator) == current->m_childs.end())
                    {   // key not presented in tree
                        AccessTypeTree insert_key;
                        insert_key.m_Name = interior_name;
                        if (isLeaf)
                        {
                            insert_key.m_operations = operations;
                        }
                        // childs is empty for newly inserted key in tree
                        current = &(*(current->m_childs.insert(ptr, insert_key)));
                        break;
                    }
                }
            }
            else
            {   // key not presented in tree
                AccessTypeTree insert_key;
                insert_key.m_Name = interior_name;
                if (isLeaf)
                {
                    insert_key.m_operations = operations;
                }
                // childs is empty for newly inserted key in tree
                current->m_childs.push_back(insert_key);
                current = &current->m_childs.front();
            }

            pos_begin = pos_end + 1;
        }
        return true;
    }
	//-------------------------------------------------------------------------------------------
    MOTreeManager::AccessTypeOperation MOTreeManager::ConvertFromStringToAccessTypeOperation(const String& operation)
    {
        if (operation.compare("Get") == 0)
        {
            return e_atGet;
        }
        else if (operation.compare("Add") == 0)
        {
            return e_atAdd;
        }
        else if (operation.compare("Replace") == 0)
        {
            return e_atReplace;
        }
        else if (operation.compare("Delete") == 0)
        {
            return e_atDelete;
        }
        else if (operation.compare("Exec") == 0)
        {
            return e_atExec;
        }

        return e_atGet;
    }

	//-------------------------------------------------------------------------------------------

    void MOTreeManager::collectDRMD(const StringMap& settings)
    {
		LOG_DEBUG_(*m_logger, "ENTER >> collectDRMD");

        const char* DRMDCollectingStatus = (const_cast<StringMap&>(settings).get(c_DRDMTestEnvironment)).c_str();
        if (DRMDCollectingStatus && (strlen(DRMDCollectingStatus) == 1) && (DRMDCollectingStatus[0] == '1'))
        {
            IExecutionQueue* queue = m_pch->GetExecutionQueue();
            if (queue)
            {
                int duration = 10;
                String serverID = "";
                String origID = "0";

                NS_DM_Diagnostics::CollectDiagnosticsCommand* cmd =
                    new NS_DM_Diagnostics::CollectDiagnosticsCommand(m_pch, duration, serverID, origID);
                if (cmd)
                {
            		LOG_DEBUG_(*m_logger, "Add CollectDiagnosticsCommand to queue");
                    if (!queue->Add(*cmd))
                    {
                        delete cmd;
                        LOG_ERROR_(*m_logger, "Failed to add command to queue");
                    }
                }
                else
                {
                    LOG_ERROR_(*m_logger, "Failed to create instance of CollectDiagnosticsCommand command");
                }
            }
            else
            {
                LOG_ERROR_(*m_logger, "Failed to get instance of execution queue");
            }
        }
        else
        {
			LOG_INFO_(*m_logger, "Initial DRMD collecting disabled");
        }

		LOG_DEBUG_(*m_logger, "LEAVE << collectDRMD");
    }

	//-------------------------------------------------------------------------------------------

	void CompleteItem(const Funambol::Item& item)
	{
		Funambol::Item& tmp_item = const_cast<Funambol::Item&>(item);
		if (tmp_item.getMeta() == 0)
		{
			Funambol::Meta metaData;
			metaData.setFormat("chr");
			metaData.setType("text/plain");
			tmp_item.setMeta(&metaData);
		}
	}
	//-------------------------------------------------------------------------------------------

}
