/*
* Copyright 2009 Funambol, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $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 "DeviceAdapter/Diagnostics/CollectDiagnosticsCommand.h"
#include "TimeStamp.h"

#include <memory>
#include <algorithm>

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_DumpPath = "DumpPath";

    const char* const c_RootPathForDump = "./";

    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), m_transactionDepth(0)
    {
    }
    //-------------------------------------------------------------------------------------------

    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(std::nothrow) MOTreeManagerTransaction;
        if (m_transaction != (MOTreeManagerTransaction*)NULL)
        {
            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;
        }

        setDumpPath(settings);

        m_isInited = true;

        LOG_DEBUG_(*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)
    {
        LOG_DEBUG_(*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_DEBUG_(*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_DEBUG_(*m_logger, "Leaf node with uri = '%s' has content = '%s'", uri.c_str(), fitString(content.c_str()));
        }
        else
        {
            LOG_DEBUG_(*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, bool keepData, const char* serverId)
    {
        if (uri.empty())
        {
            LOG_ERROR_(*m_logger, "URI is empty");
            return e_Failed;
        }
        
        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;
        }
        if (!keepData) item.setData(NULL);
        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());
                const URI parentUri = uri + ((uri[uri.length()-1] != '/') ? S_seperator : "");
                for (ChildrenPaths::const_iterator iter = children.begin(); iter != end; ++iter)
                {
                    GetAttributeStructData(parentUri + *iter, items, keepData, 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)
    {
        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());
        if(xml == NULL)
        {
            LOG_WARNING_(*m_logger,"new Funambol::StringBuffer");
        }
        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();
        if(nodeXml == NULL)
        {
            LOG_WARNING_(*m_logger,"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();
        if(nodeXml == NULL)
        {
            LOG_WARNING_(*m_logger,"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());
        if(RTPPropertiesXml == NULL)
        {
            LOG_WARNING_(*m_logger,"new Funambol::StringBuffer");
        }

        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)
                    {
                        formattedVal = "";
                        if (!value.empty())
                            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:
                {
                    value = "";
                    res = node.GetProperty(e_size, value); // RTPProperties::Size
                    LOG_INFO_(*m_logger, "\"Size\" property is required, value = '%s'", value.c_str());
                    if (res)
                    {
                        appendValue(RTPPropertiesXml, S_propertyName[e_size], value.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_DEBUG_(*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_interiorNodeFormat);
            node.SetProperty(e_type, S_interiorNodeDefType);
            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_DEBUG_(*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, S_leafNodeDefFormat);
            }

            const bool isLeaf = node.IsLeaf();

            // set type of node
            chr_value = tmpItem.getMeta()->getType();
            if (!chr_value)
            {
                chr_value = (isLeaf) ? S_leafNodeDefType : S_interiorNodeDefType;
            }
            node.SetProperty(e_type, chr_value);

            // deprecated. Must be deleted after testing addACLPropertyForNewNode
            // set ACL of leaf node. Must be empty value
            // node.SetProperty(e_ACL, "");

            addACLPropertyForNewNode(node, isLeaf, serverId);

            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_DEBUG_(*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)
    {
        LOG_DEBUG_(*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_UnsupportedMediaTypeOrFormat;
                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_DEBUG_(*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_DEBUG_(*m_logger, "ENTER >> source = '%s', destination = '%s', server id = '%s'", source.c_str(), dest.c_str(), serverId ? serverId : c_RootAccessID);

        if (!StartTransaction())
        {
            LOG_ERROR_(*m_logger, "can't start transaction");
            return e_Failed;
        }

        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;
        }

        if (res == e_Ok)
        {
            res = Commit();
            if (res != e_Ok)
            {
                LOG_ERROR_(*m_logger, "commit of successful Copy operation failed");
            }
        }
        else
        {
            if (Rollback() != e_Ok)
            {
                LOG_ERROR_(*m_logger, "rollback of unsuccessful Copy operation failed");
            }
        }

        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 = copyLeafSourceToNotExistingDestination(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_interiorNodeFormat)
                {
                    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::copyLeafSourceToNotExistingDestination(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 = copyLeafSourceToNotExistingDestination(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;
        }
        
        if (value != item.getMeta()->getFormat())
        {
            LOG_ERROR_(*m_logger, "check format for node : '%s' failed, current format: '%s', new format: '%s'", 
                node.GetName().c_str(), value.c_str(), item.getMeta()->getFormat());
        }

        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;
            }
        }
        else 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() == NULL)
            {
                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;
            }
        }

        //
        // If an ACL entry contains both a wild card, '*', and a <server-identifier>, 
        // the access right granted by the <server-identifier> is overridden by the wild card. 
        //
        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)
        {
            // So remove '*' if operation is allowed for any specific serverID 
            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() == NULL)
        {
            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");

        if (m_transactionDepth++ > 0)
        {
            return e_Ok; // we already in transaction
        }

        StatusCode res = e_Failed;

        if (m_transaction->StartTransaction())
        {
            LOG_DEBUG_(*m_logger, "Transaction is started");
        }
        else
        {
            LOG_DEBUG_(*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");

        if (--m_transactionDepth > 0)
        {
            return e_Ok; // we already in transaction. Finish transaction in calling function
        }

        StatusCode res = e_Failed;

        if (m_transaction->Rollback())
        {
            LOG_DEBUG_(*m_logger, "transaction is rollbacked");
        }
        else
        {
            LOG_DEBUG_(*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");

        if (--m_transactionDepth > 0)
        {
            return e_Ok; // we already in transaction. Finish transaction in calling function
        }

        StatusCode res = e_Failed;

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

        res = e_Ok;

        dump();

        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_DEBUG_(*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_DEBUG_(*m_logger, "Transaction is finished. Status : commit transaction");
                if (!m_transaction->Commit())
                {
                    LOG_ERROR_(*m_logger, "Transaction commit failed");
                }
            }
            else
            {
                LOG_DEBUG_(*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 ((keyNameOptimized.length() > 2) && ((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::setDumpPath(const StringMap& settings)
    {
        const char* dumpPath = (const_cast<StringMap&>(settings).get(c_DumpPath)).c_str();
        m_dumpPath = (dumpPath) ? dumpPath : "";
    }

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

    void MOTreeManager::dump()
    {
        if (!m_dumpPath.empty())
        {
            String name;
            if (GetUniqueTimeStampForFileNaming(name))
            {
                RequiredProperties props;
                props.push_back(e_TNDS_ACL);
                props.push_back(e_TNDS_format);
                props.push_back(e_TNDS_name);
                props.push_back(e_TNDS_size);
                props.push_back(e_TNDS_title);
                props.push_back(e_TNDS_tstamp);
                props.push_back(e_TNDS_type);
                props.push_back(e_TNDS_verNo);
                props.push_back(e_TNDS_value);

                Funambol::StringBuffer* nodes = 0;
                if ((GetAttributeTNDS(c_RootPathForDump, props, nodes, 0) == e_Ok) && nodes)
                {
                    FILE* file = fopen((m_dumpPath + name + ".xml").c_str(), "w");
                    if (file)
                    {
                        if (fwrite(nodes->c_str(), 1, nodes->length(), file) != nodes->length())
                        {
                            LOG_WARNING_(*m_logger, "Failed to write dump to file");
                        }
                        fclose(file);
                    }
                    else
                    {
                        LOG_WARNING_(*m_logger, "Failed to open dump file for write. Path: '%s'", (m_dumpPath + name).c_str());
                    }
                    Funambol::deleteStringBuffer(&nodes);
                }
                else
                {
                    LOG_WARNING_(*m_logger, "Failed to get xml representation of whole tree");
                }
            }
            else
            {
                LOG_WARNING_(*m_logger, "Failed to get unique timestamp representation for dump file naming");
            }
        }
    }

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

    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);
        }
    }
    //-------------------------------------------------------------------------------------------

}
