/*
 * 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 <http/URL.h>
#include "common/identifiers.h"
#include "commontypes.h"
#include "Errors.h"
#include "daemon/ProfileComponentsHolder.h"
#include "Logger/LoggerMacroses.h"
#include "treemanager/IMOTreeManager.h"
#include "treemanager/TreeManagerUtils.h"
#include "serverexchange/IServerExchangeManager.h"
#include "serverexchange/commands/AccessConfigCommand.h"


using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Communication;

const char* const c_LogName = "DMAccountCommand";


DMAccountCommand::DMAccountCommand(ProfileComponentsHolder &pch) : PCHAware(pch), m_treeManager(*pch.GetMOTreeManager())
{
}


bool DMAccountCommand::Execute()
{
GDLDEBUG("Enter");

    IMOTreeManager::ChildrenPaths accounts;
    ConnInfoList connections;

    if (TMUtils::IsDMAccNode(m_treeManager, URI_DMACC))
    {
        ConnectionInfo *pConnInfo = NULL;
        FStringBuffer uri(URI_DMACC);
        if (pConnInfo = readAccount(uri))
        {
            connections.push_back(ConnectionInfoPtr(pConnInfo));
        }
    }
    else if (e_Ok == m_pch.GetMOTreeManager()->GetChildren(URI_DMACC, accounts, NULL))
    {
        for (size_t i=0; i<accounts.size(); ++i)
        {
            String accname = accounts[i];
            FStringBuffer uriAccount0(URI_DMACC);
            uriAccount0.append("/").append(accname.c_str()); // this is uri ./DMAcc/X
            ConnectionInfo *pConnInfo = NULL;
/***
          if (TMUtils::IsDMAccNode(m_treeManager, uriAccount0.c_str()) && (pConnInfo = readAccount(uriAccount0)))
            {
                pConnInfo->accname = accname;
                connections.push_back(ConnectionInfoPtr(pConnInfo));
            }
***/
            bool isnode = DMAccountCommand::IsDMAccNode(m_treeManager, uriAccount0.c_str());
            if(isnode)
            {
                GDLDEBUG("IsDMAccNode: true");
                pConnInfo = readAccount(uriAccount0);
                if(pConnInfo)
                {
                    GDLDEBUG("pConnInfo: not null");
                    pConnInfo->accname = accname;
                    connections.push_back(ConnectionInfoPtr(pConnInfo));
                }
                else
                {
                    GDLDEBUG("pConnInfo: null");
                }
            }
            else
            {
                GDLDEBUG("IsDMAccNode: false");
            }
        }
    }

    m_pch.GetServerExchangeManager()->SetConnectionsInfo(connections);

GDLDEBUG("Leave");

    return true;
}


ConnectionInfo * DMAccountCommand::readAccount(FStringBuffer &uriAccount)
{
GDLDEBUG("Enter");

    GDLDEBUG("  read account '%s'", uriAccount.c_str() ? uriAccount.c_str() : "");
    FStringBuffer outvalue;
    
    FStringBuffer srvaddr;
    FStringBuffer srvaddrtype;
    FStringBuffer srvport;

    FStringBuffer srvCredAddr;
    FStringBuffer clCredAddr;
    FStringBuffer authlevel;
    FStringBuffer authtype;
    FStringBuffer nonce;
    FStringBuffer password;
    FStringBuffer authname;

    bool ret = false;
    ConnectionInfo * pConnInfo = new(std::nothrow) ConnectionInfo;
    if(pConnInfo == (ConnectionInfo*)NULL)
    {
        GDLWARN("new ConnectionInfo");
    }
    Funambol::AccessConfig &ac = pConnInfo->acconfig;
    ac.setServerAuthRequired(false);
    
    do {
        if (!getLeafValue(uriAccount, "AppID", outvalue))
        {
            GDLERROR("read acc info: failed to retrieve AppID %s", uriAccount.c_str());
            break; // do not tolerate absence of AppID node
        }
        pConnInfo->appID = outvalue.c_str();
        outvalue.reset();
        
        
        if (!getLeafValue(uriAccount, "ServerID",  outvalue))
        {
            GDLERROR("read acc info: failed to retrieve ServerID %s", uriAccount.c_str());
            break; // do not tolerate absence of ServerID node
        }
        pConnInfo->SetServerID(outvalue.c_str());
        outvalue.reset();

        
        getLeafValue(uriAccount, "AAuthPref", outvalue);
        if (outvalue.length())
        {
            pConnInfo->prefferedAuth = outvalue.c_str();
            outvalue.reset();
        }
        
        
        getLeafValue(uriAccount, "Name", outvalue);
        if (outvalue.length())
        {
            pConnInfo->name = outvalue.c_str();
            outvalue.reset();
        }
        
        
        getLeafValue(uriAccount, "PrefConRef", outvalue);
        if (outvalue.length())
        {
            pConnInfo->prefferedConRef = outvalue.c_str();
            outvalue.reset();
        }

        
        if (!getServerNode(uriAccount, m_uriServerNode))
        {
            GDLERROR("read acc info: failed to retrieve server address node in %s", uriAccount.c_str());
            break; // do not tolerate absence of required nodes
        }

        if (!getLeafValue(m_uriServerNode, "Addr", srvaddr))
        {
            GDLERROR("read acc info: required Addr node in %s is absent", m_uriServerNode.c_str());
            break; // do not tolerate absence of required nodes
        }

        if (!getLeafValue(m_uriServerNode, "AddrType", srvaddrtype))
        {
            GDLERROR("read acc info: required AddrType node in %s is absent", m_uriServerNode.c_str());
            break;
        }


        // read client cred node
        if (!getAuthenticationNode(uriAccount, clCredAddr, CLCRED))
        {
            GDLERROR("Failed to retrieve client authentication node in %s", uriAccount.c_str());
            break; // do not tolerate absence of required client cred
        }
        else
        {
            if (getLeafValue(clCredAddr, "AAuthData", nonce))
                ac.setClientNonce(nonce);

            if (getLeafValue(clCredAddr, "AAuthName", authname))
                ac.setUsername(authname);

            if (getLeafValue(clCredAddr, "AAuthSecret", password))
                ac.setPassword(password);

            if (getLeafValue(clCredAddr, "AAuthType", authtype))
            {
                if (!strcmp("BASIC", authtype))
                    ac.setClientAuthType(AUTH_TYPE_BASIC);
                else if (!strcmp("DIGEST", authtype))
                    ac.setClientAuthType(AUTH_TYPE_MD5);
                else if (!strcmp("HMAC", authtype))
                    ac.setClientAuthType(AUTH_TYPE_MAC);
                else
                {
                    GDLERROR("Config contains unsupported value of AAuthType: %s", authtype.c_str());
                    GDLERROR("Working value set to DIGEST");
                    ac.setClientAuthType(AUTH_TYPE_MD5);
                }
            }
        }

        // read server cred node
        if (!getAuthenticationNode(uriAccount, srvCredAddr, SRVCRED))
        {
            GDLERROR("read acc info: : failed to retrieve server authentication node in %s", uriAccount.c_str());
            break; // do not tolerate absence of required server cred
        }
        else
        {
            if (getLeafValue(srvCredAddr, "AAuthData", nonce))
                ac.setServerNonce(nonce);

            if (getLeafValue(srvCredAddr, "AAuthName", authname))
                ac.setServerID(authname);

            if (getLeafValue(srvCredAddr, "AAuthSecret", password))
                ac.setServerPWD(password);

            if (getLeafValue(srvCredAddr, "AAuthType", authtype))
            {
                if (!strcmp("BASIC", authtype))
                    ac.setServerAuthType(AUTH_TYPE_BASIC);
                else if (!strcmp("DIGEST", authtype))
                    ac.setServerAuthType(AUTH_TYPE_MD5);
                else if (!strcmp("HMAC", authtype))
                    ac.setServerAuthType(AUTH_TYPE_MAC);
                else
                {
                    GDLERROR("Config contains unsupported value of AAuthType: %s", authtype.c_str());
                    GDLERROR("Working value set to DIGEST");
                    ac.setServerAuthType(AUTH_TYPE_MD5);
                }
            }
        }

        getServerPort(srvport);

        if (srvport.empty())
        {
            ac.setSyncURL(srvaddr);
        }
        else
        {
            Funambol::URL url;
            url.setURL(srvaddr.c_str());
            url.port = atoi(srvport.c_str());

            FStringBuffer urlstring;
            urlstring.append(url.protocol).append("://").append(url.host);
            if (url.port)
                urlstring.append(":").append(url.port);
            urlstring.append(url.resource);

            ac.setSyncURL(urlstring.c_str());
        }
        ret = true;
    }
    while (false);

    if (!ret)
        SAFE_DELETE(pConnInfo);

GDLDEBUG("Leave");

    return pConnInfo;
}


// @param   uriAccount  is a ./DMAcc/<X> uri
// @result              is a ./DMAcc/<X>/AppAddr/<[0]> uri
bool DMAccountCommand::getServerNode(FStringBuffer & uriAccount, FStringBuffer & result)
{
    FStringBuffer uriAppAddr(uriAccount);//m_uriServerNode);
    uriAppAddr.append("/AppAddr");  // ./DMAcc/<X>/AppAddr

    IMOTreeManager::ChildrenPaths paths;
    if (e_Ok == m_pch.GetMOTreeManager()->GetChildren(uriAppAddr.c_str(), paths, NULL)) // perform in root mode
    {
        if (!paths.empty())
        {
            // TODO - first entry is taken; consider criteries of choosing other servers
            result.assign(uriAppAddr).append("/").append(paths[0].c_str());
            return true;
        }
    }
    return false;
}


// @param   uriAccount  is a ./DMAcc/<X> uri
// @result              is a ./DMAcc/<X>/AppAuth/<[node with SRVCRED uri]>
bool DMAccountCommand::getAuthenticationNode(FStringBuffer & uriAccount, FStringBuffer & result, const char *authname)
{
    if (!authname) return false;
    FStringBuffer uriAppAuth(uriAccount);
    uriAppAuth.append("/AppAuth");  // ./DMAcc/<X>/AppAuth

    IMOTreeManager::ChildrenPaths paths;
    if (e_Ok == m_pch.GetMOTreeManager()->GetChildren(uriAppAuth.c_str(), paths, NULL))  // perform in root mode
    {
        if (!paths.empty())
        {
            IMOTreeManager::ChildrenPaths::iterator i = paths.begin();
            for (; i != paths.end(); ++i)
            {
                FStringBuffer uri_alevel = uriAppAuth.c_str();
                uri_alevel.append("/").append((*i).c_str());
                uri_alevel.append("/AAuthLevel"); // ./DMAcc/<X>/AppAuth/<X>/AAuthLevel
                String value;
                if (e_Ok == m_pch.GetMOTreeManager()->GetValue(uri_alevel.c_str(), value, NULL))
                {
                    if (!strcmp(authname, value.c_str()))
                    {
                        result.assign(uriAppAuth.c_str()).append("/").append((*i).c_str());//i->c_str());
                        return true;
                    }
                }
            }
        }
    }
    return false;
}


// @param   c_UriServerNode is a ./DMAcc/<X>/AppAddr/<X> uri
// @result                  is a ./DMAcc/<X>/AppAddr/<X>/Port/<[0]>/PortNbr uri
void DMAccountCommand::getServerPort(FStringBuffer &result)
{
    FStringBuffer uriPort(m_uriServerNode);
    uriPort.append("/Port");    // ./DMAcc/<X>/AppAddr/<X>/Port

    IMOTreeManager::ChildrenPaths paths;
    if (e_Ok == m_pch.GetMOTreeManager()->GetChildren(uriPort.c_str(), paths, NULL))
    {
        if (!paths.empty())
        {
            // TODO - first entry is taken; consider criteries of choosing other servers
            uriPort.append("/").append(paths[0].c_str()).append("/PortNbr");

            String value;
            if (e_Ok == m_pch.GetMOTreeManager()->GetValue(uriPort.c_str(), value, NULL)) // perform in root mode
            {
                result.assign(value.c_str());
            }
        }
    }
}


// @param       uriNode some uri in MO tree
// @leafname    name of leaf in  uriNode
// @result      value of the s a uriNode/propname
bool DMAccountCommand::getLeafValue(FStringBuffer & uriNode, const char * leafname, FStringBuffer &result)
{
    FStringBuffer uriAuthProperty(uriNode);
    uriAuthProperty.append("/").append(leafname);   // uriNode/propname

    String value;
    if (e_Ok == m_pch.GetMOTreeManager()->GetValue(uriAuthProperty.c_str(), value, NULL)) // perform in root mode
    {
        result.assign(value.c_str());
        return true;
    }
    return false;
}


bool DMAccountCommand::setLeafValue(FStringBuffer & uriNode, const char * leafname, FStringBuffer &value)
{
    FStringBuffer uriAuthProperty(uriNode);
    uriAuthProperty.append("/").append(leafname);   // uriNode/propname
    
    String uri(uriAuthProperty.c_str());
    Funambol::Item item;
    Funambol::Meta meta;
    meta.setFormat("chr");
    meta.setType("text/plain");
    item.setMeta(&meta);
    Funambol::ComplexData cdata(value.c_str());
    item.setData(&cdata);
    
    return (e_Ok == m_pch.GetMOTreeManager()->Replace(uri, item));
}


//bool DMAccountCommand::isDMAccNode(const char *uri)
//{
//  String nodeType;
//  if (e_Ok == m_pch.GetMOTreeManager()->GetPropertyValue(uri, "Type", nodeType, NULL))
//  {
//      return !strcmp(NODE_TYPE_DMACC, nodeType.c_str());
//  }
//  return false;
//}

bool DMAccountCommand::IsDMAccNode(IMOTreeManager &tm, const char *uri)
{
    String nodeType;
/****
    if (e_Ok == tm.GetPropertyValue(uri, "Type", nodeType, NULL))
    {
        return !strcmp(NODE_TYPE_DMACC, nodeType.c_str());
    }
****/
StatusCode sc=tm.GetPropertyValue(uri, "Type", nodeType, NULL);
GDLDEBUG("IsDMAccNode: %s,%d", uri,sc);
if(sc==e_Ok){
    int t1=strcmp(MOID_DMACC, nodeType.c_str());
    GDLDEBUG("strcmp: (%s,%s) is %d",MOID_DMACC,nodeType.c_str(),t1);
     if(t1==0){
          return true;
     }else{
          return false;
      }
}

    return false;
}
