//
// C++ Implementation: c6xcapclient
//
// Description:
//
//
// Author: Giorgio A. <openc6@hotmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "c6xcapclient.h"
#include "c6httpconnection.h"
#include <stdlib.h>
#include <qdatetime.h>
#include <qurl.h>
#include <qcstring.h>
#include "c6logger.h"
extern "C"
{
#include "base64.h"
}

#define XCAP_SIG "XCAP"
#define VIRGILIO_DOMAIN(user) QString(user)+"@c6.virgilio.it"

static int getRandomNumber()
{
    QTime midnight( 0, 0, 0 );
    srand(midnight.msecsTo(QTime::currentTime()));
    return(rand() % 65535);
}

void C6XCAPClient::setCommonHeader(QHttpRequestHeader &header)
{
    header.setValue("Authorization",QString("Basic ")+_base64user);
    header.setValue("User-Agent","File Session");
    header.setValue("Host", "xcap.community.virgilio.it");
    header.setValue("Connection","Keep-Alive");
    header.setValue("Cache-Control","no-cache");
}

C6XCAPClient::C6XCAPClient():
        _xcapState(XCAP_UNDEFINED_STATE)
{
    _buddyRequest = new C6HttpConnection("http://xcap.community.virgilio.it");
    connect(_buddyRequest,SIGNAL(bufferFull(const QString&,C6HttpConnection*)),this,SLOT(buddyRequestReady(const QString&,C6HttpConnection*)));
}


C6XCAPClient::~C6XCAPClient()
{
    delete _buddyRequest;
}

void C6XCAPClient::setUserPassword(QString const &nick,QString const &password)
{
    QString tmp = nick+":"+password;
    char *out = 0;
    
    _currentUser = nick;
    size_t len = base64_encode_alloc(const_cast<char *>(tmp.latin1()),
                                     tmp.length(),&out);
    if (out != 0)
    {
        _base64user = QString::fromLatin1(out,len);
        QUrl::encode(_base64user);      
    }
}


void C6XCAPClient::buddyRequestReady(const QString& filename,C6HttpConnection*)
{
    QByteArray data = _buddyRequest->getBuffer();
    XCAP_STATE localState = _xcapState;
    
    setXCAPState(XCAP_STEADY_STATE);
    switch(localState)
    {
    case XCAP_REQUEST_LIST_STATE:
        emit buddyListReady(data);
        requestBuddyAuth();
        break;

    case XCAP_AUTH_STATE:
        emit buddyAuthReady(data);
        requestBuddyClientConfig();
        break;

    case XCAP_CONFIG_STATE:
        emit buddyConfigReady(data);
        break;

    case XCAP_ADD_BUDDY_STATE:
        checkForXCAPError(data);
        break;

    case XCAP_ADD_GROUP_LIST_STATE:
        checkForXCAPError(data);
        break;

    case XCAP_REMOVE_GROUP_STATE:
        checkForXCAPError(data);
        break;

    case XCAP_REMOVE_BUDDY_STATE:
        checkForXCAPError(data);
        break;
        
    case XCAP_AUTH_CONFIG_STATE:
        checkForXCAPError(data);
        break;
        
    case XCAP_CONFIG_INIT:      
    case XCAP_BUDDY_INIT:
    case XCAP_AUTH_INIT:
        checkForXCAPError(data);
        break;               
        
    default:
        break;
    }
    
    emit xcapResponseOK(localState);
}

void C6XCAPClient::requestBuddyClientConfig()
{
    setXCAPState(XCAP_CONFIG_STATE);

    QString random = QString::number(getRandomNumber());

    QHttpRequestHeader header("GET", "/services/client-configuration/users/"
                              +_currentUser+"/config.xml?"+random);
    header.setContentType("application/client-configuration+xml");

    setCommonHeader(header);
    _buddyRequest->request(header);
}

void C6XCAPClient::requestBuddyAuth()
{
    setXCAPState(XCAP_AUTH_STATE);

    QString random = QString::number(getRandomNumber());

    QHttpRequestHeader header("GET", "/services/auth-config-c6/users/"
                              +_currentUser+"/auth.xml?"+random);
    header.setContentType("application/auth-config-c6+xml");
    setCommonHeader(header);
    _buddyRequest->request(header);
}

void C6XCAPClient::requestBuddyList()
{
    setXCAPState(XCAP_REQUEST_LIST_STATE);
    QString random = QString::number(getRandomNumber());

    QHttpRequestHeader header("GET","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml?"+random);
    header.setContentType("application/resource-lists+xml");

    setCommonHeader(header);
    _buddyRequest->request(header);
}

void C6XCAPClient::addBuddy(QString const &group,QString const &nick)
{
    setXCAPState(XCAP_ADD_BUDDY_STATE);

    QString body = getEntryString(nick);

    QString encodedGroup = group;
    QUrl::encode(encodedGroup);

    QString nodeSelector = "resource-lists/list%5B@name=%22Lista%20Amici%22%5D/list%5B@name=%22"
                           +encodedGroup+"%22%5D/entry%5B@uri=%22sip:"
                           +VIRGILIO_DOMAIN(nick)+"%22%5D";

    QHttpRequestHeader header("PUT","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml/~~/"+nodeSelector);

    header.setContentType("application/xcap-el+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);

    _buddyRequest->request(header,data);
}

void C6XCAPClient::addBuddyToBlackList(QString const &nick)
{
    setXCAPState(XCAP_ADD_BUDDY_BLACK_LIST_STATE);
    addBuddyToList(BLACK_LIST,nick);  
}

void C6XCAPClient::removeBuddy(QString const &group,QString const &nick)
{
    setXCAPState(XCAP_REMOVE_BUDDY_STATE);

    QString body = getEntryString(nick);

    QString encodedGroup = group;
    QUrl::encode(encodedGroup);

    QString nodeSelector = "resource-lists/list%5B@name=%22Lista%20Amici%22%5D/list%5B@name=%22"
                           +encodedGroup+"%22%5D/entry%5B@uri=%22sip:"
                           +VIRGILIO_DOMAIN(nick)+"%22%5D";

    QHttpRequestHeader header("DELETE","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml/~~/"+nodeSelector);

    setCommonHeader(header);

    _buddyRequest->request(header);
}

void C6XCAPClient::removeBuddyFromBlackList(QString const &nick)
{
    removeBuddyFromList(BLACK_LIST,nick); 
}


/*!
    \fn C6XCAPClient::setXCAPState(XCAP_STATE state)
 */
void C6XCAPClient::setXCAPState(XCAP_STATE state)
{
    _xcapState = state;
}



/*!
    \fn C6XCAPClient::getEntryString(QString const & nick)
 */
QString C6XCAPClient::getEntryString(QString const & user)
{
    return("<entry uri=\"sip:"+VIRGILIO_DOMAIN(user)+"\">\r\n<display-name>"
           +user+"</display-name>\r\n</entry>\r\n");
}



/*!
    \fn C6XCAPClient::getListString(QString const & entry)
 */
QString C6XCAPClient::getListString(QString const &group,QString const & entry)
{
    return("<list name=\""+group+"\">\r\n"+entry+
           "\r\n</list>\r\n");
}


/*!
    \fn C6XCAPClient::addBuddyList(QString const &group,QStringList const &nick)
 */
void C6XCAPClient::addBuddyList(QString const &group,QStringList const &nick)
{
    setXCAPState(XCAP_ADD_GROUP_LIST_STATE);

    QString entries;

    for (unsigned int i=0; i < nick.count(); i++)
    {
        entries+=getEntryString(nick[i])+"\n";
    }

    QString body = getListString(group,entries);
    QString encodedGroup = group;
    QUrl::encode(encodedGroup);

    QString nodeSelector = "resource-lists/list%5B@name=%22Lista%20Amici%22%5D/list%5B@name=%22"
                           +encodedGroup+"%22%5D";

    QHttpRequestHeader header("PUT","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml/~~/"+nodeSelector);


    header.setContentType("application/xcap-el+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);

    _buddyRequest->request(header,data);
}

void C6XCAPClient::checkForXCAPError(QByteArray &data)
{
    QString line;
    QTextStream ts(data,IO_ReadOnly);

    while (!ts.atEnd())
    {
        line+=ts.readLine();
    }

    if (line.contains("xcap-error"))
    {
        C6Logger::getInstance().debugPrint(XCAP_SIG,"%s\n",line.latin1());
    }
}

/*!
    \fn C6XCAPClient::addGroup(QString const & group)
 */
void C6XCAPClient::addGroup(QString const &group)
{
    QStringList dummy;
    addBuddyList(group,dummy);
}


/*!
    \fn C6XCAPClient::removeGroup(QString const & group)
 */
void C6XCAPClient::removeGroup(QString const & group)
{
    setXCAPState(XCAP_REMOVE_GROUP_STATE);

    QString encodedGroup = group;
    QUrl::encode(encodedGroup);

    QString nodeSelector = "resource-lists/list%5B@name=%22Lista%20Amici%22%5D/list%5B@name=%22"
                           +encodedGroup+"%22%5D";

    QHttpRequestHeader header("DELETE","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml/~~/"+nodeSelector);

    header.setContentType("application/xcap-att+xml");
    setCommonHeader(header);
    _buddyRequest->request(header);
}


/*!
    \fn C6XCAPClient::renameGroup(QString const &old,QString const &new)
 */
void C6XCAPClient::renameGroup(QString const &oldGroup,QString const &newGroup)
{
    setXCAPState(XCAP_REMOVE_GROUP_STATE);

    QString body = newGroup;
    QString encodedGroup = oldGroup;
    QUrl::encode(encodedGroup);

    QString nodeSelector = "resource-lists/list%5B@name=%22Lista%20Amici%22%5D/list%5B@name=%22"
                           +encodedGroup+"%22%5D/@name";

    QHttpRequestHeader header("PUT","/services/resource-lists/users/"
                              +_currentUser+"/buddy.xml/~~/"+nodeSelector);

    setCommonHeader(header);
    header.setContentType("application/xcap-att+xml");
    header.setContentLength(body.length());

    QByteArray data = body.utf8();
    data.resize(data.size()-1);

    _buddyRequest->request(header,data);
}


/*!
    \fn C6XCAPClient::setConfiguration(CONFIG_MODE mode)
 */
void C6XCAPClient::setConfiguration(CONFIG_MODE mode)
{
    setXCAPState(XCAP_AUTH_CONFIG_STATE);

    QString body = getConfigurationMode(mode);
    QString nodeSelector = "client-configuration/warnings";

    QHttpRequestHeader header("PUT","/services/client-configuration/users/"
                              +_currentUser+"/config.xml/~~/"+nodeSelector);

    setCommonHeader(header);
    header.setContentType("application/xcap-el+xml");
    header.setContentLength(body.length());

    QByteArray data = body.utf8();
    data.resize(data.size()-1);

    _buddyRequest->request(header,data);
}



/*!
    \fn C6XCAPClient::getConfigurationMode(AUTH_MODE mode)
 */
QString C6XCAPClient::getConfigurationMode(CONFIG_MODE mode)
{
    QString modeString;

    switch(mode)
    {
    case NOWARN:
        modeString = "nowarning";
        break;
    case WARN:
        modeString = "warning";      
        break;
    case PERMISSION:
        modeString = "permission";
        break;
    default:
        break;
    }

    return("<warnings>\n\t"+modeString+"\n</warnings>");
}

/*!
    \fn C6XCAPClient::getAuthorizationMode(AUTH_MODE mode)
 */
QString C6XCAPClient::getOfflineMode(OFFLINE_MODE mode)
{
    QString modeString;

    switch(mode)
    {
        case EMAIL:
            modeString = "mail";
            break;
        case REPORT:
            modeString = "messenger";         
            break;
        default:
            break;
    }

    return("<reachability>\n\t"+modeString+"\n</reachability>");
}


/*!
    \fn C6XCAPClient::getConfigurationModeValue(QString mode)
 */
int C6XCAPClient::getConfigurationModeValue(QString mode)
{
    CONFIG_MODE ret = UNDEFINED_CONFIG;
        
    if (mode.compare("nowarning") == 0)
    {
        ret =  NOWARN;
    }
    else 
    {      
        if (mode.compare("warning") == 0)
        {
            ret = WARN;         
        }
        else
        {         
            if (mode.compare("permission") == 0)
            {
                ret = PERMISSION;            
            }
        }               
    } 
    
    return(ret);                
}


/*!
    \fn C6XCAPClient::C6XCAPClient::getOfflineModeValue(QString mode)
 */
int C6XCAPClient::getOfflineModeValue(QString mode)
{
    OFFLINE_MODE ret = UNDEFINED_OFFLINE;
       
    if (mode.compare("mail") == 0)
    {
        ret  = EMAIL;            
    }
    else
    {
        if (mode.compare("messenger") == 0)
        {
            ret = REPORT;            
        }    
    }           
    
    return(ret);   
}


/*!
    \fn C6XCAPClient::setOfflineMessage(OFFLINE_MODE mode)
 */
void C6XCAPClient::setOfflineMessage(OFFLINE_MODE mode)
{
    setXCAPState(XCAP_AUTH_CONFIG_STATE);

    QString body = getOfflineMode(mode);
    QString nodeSelector = "client-configuration/reachability";

    QHttpRequestHeader header("PUT","/services/client-configuration/users/"
            +_currentUser+"/config.xml/~~/"+nodeSelector);

    setCommonHeader(header);
    header.setContentType("application/xcap-el+xml");
    header.setContentLength(body.length());

    QByteArray data = body.utf8();
    data.resize(data.size()-1);

    _buddyRequest->request(header,data);
}


/*!
    \fn C6XCAPClient::initBuddyList()
 */
void C6XCAPClient::initBuddyList()
{
   setXCAPState(XCAP_BUDDY_INIT);
   
   QString xmlPayload = "<list name=\"Amici\"/>\r\n";
   setBuddyList(xmlPayload);             
}

/*!
    \fn C6XCAPClient::setBuddyList()
 */
void C6XCAPClient::setBuddyList(QString const &xmlPayload)
{
    QString body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
            "<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\">\r\n"
            "<list name=\"Lista Amici\">\r\n"+xmlPayload+"</list>\r\n</resource-lists>";
    
    QHttpRequestHeader header("PUT","/services/resource-lists/users/"
            +_currentUser+"/buddy.xml");

    header.setContentType("application/resource-lists+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);
    _buddyRequest->request(header,data);    
}

/*!
    \fn C6XCAPClient::initAuthorization()
 */
void C6XCAPClient::initAuthorization()
{
    QStringList black;
    QStringList white;
       
    setXCAPState(XCAP_AUTH_INIT);
    initAuthorization(black,white,NOWARN); 
}


/*!
    \fn C6XCAPClient::initConfiguration()
 */
void C6XCAPClient::initConfiguration()
{
    setXCAPState(XCAP_CONFIG_INIT);
    
    QString body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
            "<client-configuration>\r\n\t<reachability>mail</reachability>\r\n\t"
            "<warnings>nowarning</warnings>\r\n</client-configuration>";

    QHttpRequestHeader header("PUT","/services/client-configuration/users/"
            +_currentUser+"/config.xml");

    header.setContentType("application/client-configuration+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);
   
   _buddyRequest->request(header,data);    
}



/*!
    \fn C6XCAPClient::setBuddyLists(QMap const& list,QStringList const& blacklist)
 */
void C6XCAPClient::setBuddyLists(XCAPListType list,QStringList const& blacklist,
                QStringList const& whitelist,CONFIG_MODE mode)
{
    XCAPListType::Iterator tmp;
    QString xmlPayload; 
    QString nicks;
    
    for (tmp = list.begin(); tmp != list.end(); tmp++)
    {
        QStringList nicklist = tmp.data();         
        nicks = "";
            
        for (int i=0; i < nicklist.count(); i++)
        {
            nicks+=getEntryString(nicklist[i]);    
        }
           
        xmlPayload.append(getListString(tmp.key(),nicks));
    }    
    
    setBuddyList(xmlPayload);          
    initAuthorization(blacklist,whitelist,mode);    
}


/*!
    \fn C6XCAPClient::initAuthorization(QStringList const &black,QStringList const &white)
 */
void C6XCAPClient::initAuthorization(QStringList const &black,QStringList const &white,CONFIG_MODE mode)
{
    QString blackList;
    QString whiteList;        
    
    for (unsigned int i=0; i < black.count(); i++)
    {
        blackList+=getEntryString(black[i]);    
    } 
      
    for (unsigned int i=0; i < white.count(); i++)
    {
        whiteList+=getEntryString(white[i]);    
    }           
    
    if (black.count() > 0)
    {
        blackList.prepend("<blacklist>");
        blackList.append("</blacklist>");        
    }     
    else
    {
        blackList = "<blacklist/>";     
    }          
    
    if (white.count() > 0)
    {
        whiteList.prepend("<whitelist>");
        whiteList.append("</whitelist>");        
    }             
    else
    {
        whiteList = "<whitelist/>";     
    }    
    
    QString modeStr = (mode == PERMISSION) ? "whitelist" : "all";   
    QString body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
            "<auth-config-c6>\r\n<mode>"+modeStr+"</mode>\r\n"+whiteList+
            +blackList+"\r\n</auth-config-c6>"; 

    QHttpRequestHeader header("PUT","/services/auth-config-c6/users/"
            +_currentUser+"/auth.xml");

    header.setContentType("application/auth-config-c6+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);
   
    _buddyRequest->request(header,data);    
}



/*!
    \fn C6XCAPClient::getConfigurationModeString(CONFIG_MODE mode)
 */
QString C6XCAPClient::getConfigurationModeString(CONFIG_MODE mode)
{
    QString ret;
        
    switch(mode)
    {
        case NOWARN: 
            ret = "nowarning";
            break;
        case WARN:
            ret = "warning";
            break;
        case PERMISSION:
            ret = "permission";
            break;
        default:
            break;                                                               
    }          
    
    return(ret);        
}


/*!
    \fn C6XCAPClient::addToWhiteList(QString const &)
 */
void C6XCAPClient::addBuddyToWhiteList(QString const & nick)
{
    setXCAPState(XCAP_ADD_BUDDY_WHITE_LIST_STATE);
    addBuddyToList(WHITE_LIST,nick);     
}


/*!
    \fn C6XCAPClient::addBuddyToList(LIST_TYPE type,QString const & nick)
 */
void C6XCAPClient::addBuddyToList(LIST_TYPE type,QString const & nick)
{
    QString listType = (type == WHITE_LIST) ? "whitelist" : "blacklist";    
    QString body = getEntryString(nick);
    
    QString nodeSelector = "auth-config-c6/"+listType+"/entry%5B@uri=%22sip:"
            +VIRGILIO_DOMAIN(nick)+"%22%5D";

    QHttpRequestHeader header("PUT","/services/auth-config-c6/users/"
            +_currentUser+"/auth.xml/~~/"+nodeSelector);

    header.setContentType("application/xcap-el+xml");
    header.setContentLength(body.length());
    setCommonHeader(header);

    QByteArray data = body.utf8();
    data.resize(data.size()-1);
    _buddyRequest->request(header,data);
}


/*!
    \fn C6XCAPClient::removeBuddyFromWhiteList(QString const & nick)
 */
void C6XCAPClient::removeBuddyFromWhiteList(QString const & nick)
{
    removeBuddyFromList(WHITE_LIST,nick); 
}


/*!
    \fn C6XCAPClient::removeBuddyFromList(LIST_TYPE type,QString const & nick)
 */
void C6XCAPClient::removeBuddyFromList(LIST_TYPE type,QString const & nick)
{
    setXCAPState(XCAP_REMOVE_BUDDY_STATE);
    QString listType = (type == WHITE_LIST) ? "whitelist" : "blacklist";    
    
    QString body = getEntryString(nick);

    QString nodeSelector = "auth-config-c6/"+listType+"/entry%5B@uri=%22sip:"
            +VIRGILIO_DOMAIN(nick)+"%22%5D";

    QHttpRequestHeader header("DELETE","/services/auth-config-c6/users/"
            +_currentUser+"/auth.xml/~~/"+nodeSelector);

    header.setContentType("application/xcap-el+xml");

    setCommonHeader(header);

    _buddyRequest->request(header);  
}
