/*
 * switchboardserver.cpp
 * libmsn
 *
 * Created by Mark Rowe on Mon Mar 22 2004.
 * Refactored by Tiago Salem Herrmann on 08/2007.
 * Copyright (c) 2004 Mark Rowe. All rights reserved.
 * Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <msn/switchboardserver.h>
#include <msn/notificationserver.h>
#include <msn/errorcodes.h>
#include <msn/externals.h>
#include <msn/util.h>

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <algorithm>
#include <cassert>
#include <sstream>

namespace MSN
{
    std::map<std::string, void (SwitchboardServerConnection::*)(std::vector<std::string> &)> SwitchboardServerConnection::commandHandlers;
    std::map<std::string, void (SwitchboardServerConnection::*)(std::vector<std::string> &, std::string, std::string)> SwitchboardServerConnection::messageHandlers;
    
    SwitchboardServerConnection::SwitchboardServerConnection(AuthData & auth_, NotificationServerConnection & n)
        : Connection(), auth(auth_), _connectionState(SB_DISCONNECTED), notificationServer(n)
    {
        registerCommandHandlers();
        registerMessageHandlers();
    }
    
    SwitchboardServerConnection::~SwitchboardServerConnection()
    {
        if (this->connectionState() != SB_DISCONNECTED)
            this->disconnect();
    }
     
    Connection *SwitchboardServerConnection::connectionWithSocket(void * sock)
    {
        if (this->sock == sock)
            return this;

/*  std::list<FileTransferConnectionP2P *> & list2 = _fileTransferConnectionsP2P;
        std::list<FileTransferConnectionP2P *>::iterator j = list2.begin();
        
        for (; j != list2.end(); j++)
        {
            if ((*j)->sock == fd)
                return *i;
        }
    */
        return NULL;
    }

    void SwitchboardServerConnection::sendEmoticon(std::string alias, std::string file)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);

        myNotificationServer()->msnobj.addMSNObject(file,2);
        std::string msnobject;
        myNotificationServer()->msnobj.getMSNObjectXML(file, 2, msnobject);

        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-mms-emoticon\r\n\r\n";
        msg_ << alias << "\t" << msnobject << "\t";
        size_t msg_length = msg_.str().size();

        buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str();
        write(buf_);
    }

    void SwitchboardServerConnection::sendFile(MSN::fileTransferInvite ft)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);
        p2p.sendFile(*this,ft);
    }

    void SwitchboardServerConnection::addFileTransferConnectionP2P(FileTransferConnectionP2P *c)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        _fileTransferConnectionsP2P.push_back(c);
    }

    void SwitchboardServerConnection::removeFileTransferConnectionP2P(FileTransferConnectionP2P *c)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        _fileTransferConnectionsP2P.remove(c);
    }

    template <class _Tp>
    class hasCookieOf
    {
        const std::string & cookie;
public:
        hasCookieOf(const std::string & __i) : cookie(__i) {};
        bool operator()(const _Tp &__x) { return __x->cookie == cookie; }
    };
    
    void SwitchboardServerConnection::addCallback(SwitchboardServerCallback callback,
                                                   int trid, void *data)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTING);        
        this->callbacks[trid] = std::make_pair(callback, data);
    }

    void SwitchboardServerConnection::removeCallback(int trid)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTING);        
        this->callbacks.erase(trid);
    }

    void SwitchboardServerConnection::addP2PCallback(SwitchboardServerCallback2 callback,
                                                   int trid, unsigned int sessionID)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTING);        
        this->callbacks2[trid] = std::make_pair(callback, sessionID);
    }

    void SwitchboardServerConnection::removeP2PCallback(int trid)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTING);        
        this->callbacks2.erase(trid);
    }

    void SwitchboardServerConnection::registerMessageHandlers()
    {
        if (messageHandlers.size() == 0)
        {
            messageHandlers["text/plain"]                            = &SwitchboardServerConnection::message_plain;
            messageHandlers["text/x-msmsgsinvite"]                   = &SwitchboardServerConnection::message_invitation;
            messageHandlers["text/x-msmsgscontrol"]                  = &SwitchboardServerConnection::message_typing_user;
            messageHandlers["application/x-msnmsgrp2p"]              = &SwitchboardServerConnection::message_p2p;
            messageHandlers["text/x-msnmsgr-datacast"]               = &SwitchboardServerConnection::message_datacast;
            messageHandlers["text/x-mms-emoticon"]                   = &SwitchboardServerConnection::message_emoticon;
            messageHandlers["text/x-mms-animemoticon"]               = &SwitchboardServerConnection::message_emoticon;
            messageHandlers["image/gif"]                             = &SwitchboardServerConnection::message_ink;
            messageHandlers["application/x-ms-ink"]                  = &SwitchboardServerConnection::message_ink;
        }
    }

    void SwitchboardServerConnection::registerCommandHandlers()
    {
        if (commandHandlers.size() == 0)
        {
            commandHandlers["BYE"] = &SwitchboardServerConnection::handle_BYE;
            commandHandlers["JOI"] = &SwitchboardServerConnection::handle_JOI;
            commandHandlers["NAK"] = &SwitchboardServerConnection::handle_NAK;
            commandHandlers["MSG"] = &SwitchboardServerConnection::handle_MSG;
        }
    }

    void SwitchboardServerConnection::message_plain(std::vector<std::string> & args, std::string mime, std::string body)
    {
        Message msg = Message(body, mime);
        
        this->myNotificationServer()->externalCallbacks.gotInstantMessage(this,args[1], decodeURL(args[2]), &msg);
    }

    void SwitchboardServerConnection::message_emoticon(std::vector<std::string> & args, std::string mime, std::string body)
    {
        std::vector<std::string> emoticons = splitString(body,"\t");
        for(unsigned int i=0; i < emoticons.size(); )
        {
            // avoid errors with clients which do not respect the protocol
            if(i+2 > emoticons.size()) return;
                this->myNotificationServer()->externalCallbacks.gotEmoticonNotification(this,args[1], emoticons[i] , emoticons[i+1]);
            i+=2; // move to the next one
        }
    }

    void SwitchboardServerConnection::message_invitation(std::vector<std::string> & args, std::string mime, std::string body)
    {
    }

    void SwitchboardServerConnection::message_typing_user(std::vector<std::string> & args, std::string mime, std::string body)
    {
        this->myNotificationServer()->externalCallbacks.buddyTyping(this, args[1], decodeURL(args[2]));        
    }   

    void SwitchboardServerConnection::message_ink(std::vector<std::string> & args, std::string mime, std::string body)
    {
        std::string image = body.substr(body.find("base64:")+7);
        this->myNotificationServer()->externalCallbacks.gotInk(this, args[1], image);
    }   

    void SwitchboardServerConnection::message_p2p(std::vector<std::string> & args, std::string mime, std::string body)
    {
        p2p.handleP2Pmessage(*this,args,mime,body);
    }   
    void SwitchboardServerConnection::message_datacast(std::vector<std::string> & args, std::string mime, std::string body)
    {
        Message::Headers headers = Message::Headers(body);
        int id = decimalFromString(headers["ID"]);
        switch(id)
        {
            case 1:
                this->myNotificationServer()->externalCallbacks.gotNudge(this, args[1]);
                break;
            case 2:
                this->myNotificationServer()->externalCallbacks.gotWinkNotification(this, args[1], headers["Data"]);
                break;
            case 3:
                this->myNotificationServer()->externalCallbacks.gotVoiceClipNotification(this, args[1], headers["Data"]);
                break;
            case 4:
                this->myNotificationServer()->externalCallbacks.gotActionMessage(this, args[1], headers["Data"]);
                break;
        }
    }

    void SwitchboardServerConnection::dispatchCommand(std::vector<std::string> & args)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        std::map<std::string, void (SwitchboardServerConnection::*)(std::vector<std::string> &)>::iterator i = commandHandlers.find(args[0]);
        if (i != commandHandlers.end())
            (this->*commandHandlers[args[0]])(args);
    }
    
    int SwitchboardServerConnection::sendMessage(const Message *msg)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);        
        std::string s = msg->asString();
        
        std::ostringstream buf_;
        buf_ << "MSG " << this->trID << " A " << (int) s.size() << "\r\n" << s;
        this->write(buf_);
        this->addCallback(&SwitchboardServerConnection::callback_messageACK, this->trID, NULL);
        return this->trID++;
    }

    int SwitchboardServerConnection::sendMessage(const std::string & body)
    { 
        Message msg(body, "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n");
        return this->sendMessage(&msg);
    }    

    void SwitchboardServerConnection::sendKeepAlive()
    {
        this->assertConnectionStateIsAtLeast(SB_READY);
        std::string s("MIME-Version: 1.0\r\nContent-Type: text/x-keepalive\r\n\r\n");
        std::ostringstream buf_;
        buf_ << "MSG " << this->trID++ << " U " << (int) s.size() << "\r\n" << s;
        this->write(buf_);
    }    

    void SwitchboardServerConnection::handle_BYE(std::vector<std::string> & args)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        std::list<Passport> & list = this->users;
        std::list<Passport>::iterator i;
        
        this->myNotificationServer()->externalCallbacks.buddyLeftConversation(this, args[1]);
        
        for (i = list.begin(); i != list.end(); i++)
        {
            if (*i == args[1])
            {
                list.remove(*i);
                break;
            }
        }    
        
        if (this->users.empty() || (args.size() > 3 && args[3] == "1"))
        {
            this->disconnect();
        }
    }
    
    void SwitchboardServerConnection::handle_JOI(std::vector<std::string> & args)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        if (args[1] == this->auth.username)
            return;
        
        if (this->auth.sessionID.empty() && this->connectionState() == SB_WAITING_FOR_USERS)
            this->setConnectionState(SB_READY);
        
        this->users.push_back(args[1]);
        this->myNotificationServer()->externalCallbacks.buddyJoinedConversation(this, args[1], decodeURL(args[2]), 0);
    }
    
    void SwitchboardServerConnection::handle_NAK(std::vector<std::string> & args)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        this->myNotificationServer()->externalCallbacks.failedSendingMessage(this);
    }
    
    void SwitchboardServerConnection::handle_MSG(std::vector<std::string> & args)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        int msglen;
        std::string msg;
        std::string mime;
        std::string body;
        size_t tmp;
        
        msglen = decimalFromString(args[3]);
        msg = this->readBuffer.substr(0, msglen);
        this->readBuffer = this->readBuffer.substr(msglen);
        
        body = msg.substr(msg.find("\r\n\r\n") + 4);
        mime = msg.substr(0, msg.size() - body.size());

        std::string contentType;
        Message::Headers headers = Message::Headers(mime);

        std::string chunks = headers["Chunks"];
        if(!chunks.empty())
        {
            // First packet of MultiPacket Data;
            MultiPacketSession session;
            std::string messageID = headers["Message-ID"];
            session.chunks = decimalFromString(chunks);
            session.mime = mime;
            session.receivedChunks = 1;
            session.body += body;
            if(session.chunks != 1)
            {
                MultiPacketSessions[messageID] = session;
                return;
            }

        } 
        else
        {
            std::string chunk = headers["Chunk"];
            if(!chunk.empty())
            {
                // part of multipacket data
                std::string messageID = headers["Message-ID"];
                MultiPacketSession session = MultiPacketSessions[messageID];
                session.body += body;
                session.receivedChunks++;
                if(session.chunks != session.receivedChunks)
                {
                    MultiPacketSessions[messageID] = session;
                    return;
                }
                else
                {
                    MultiPacketSessions.erase(messageID);
                    body = session.body;
                    mime = session.mime;
                    headers = Message::Headers(mime);
                }
            }
        }
        contentType = headers["Content-Type"];

        if ((tmp = contentType.find("; charset")) != std::string::npos)
            contentType = contentType.substr(0, tmp);
        
        std::map<std::string, void (SwitchboardServerConnection::*)(std::vector<std::string> &, std::string, std::string)>::iterator i = messageHandlers.find(contentType);
        if (i != messageHandlers.end())
            (this->*(messageHandlers[contentType]))(args, mime, body);
    }

    void SwitchboardServerConnection::sendAction(std::string action)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);        
        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n";
        msg_ << "ID: 4\r\n";
        msg_ << "Data: " << action << "\r\n" ;
        size_t msg_length = msg_.str().size();
        
        buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str();
        
        write(buf_);        
    }

    void SwitchboardServerConnection::sendVoiceClip(std::string msnobject)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);
        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n";
        msg_ << "ID: 3\r\n";
        msg_ << "Data: " << msnobject << "\r\n\r\n";
        size_t msg_length = msg_.str().size();

        buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str();

        write(buf_);
    }
 
    void SwitchboardServerConnection::sendWink(std::string msnobject)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);
        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n";
        msg_ << "ID: 2\r\n";
        msg_ << "Data: " << msnobject << "\r\n\r\n";
        size_t msg_length = msg_.str().size();

        buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str();

        write(buf_);
    }

    void SwitchboardServerConnection::sendNudge()
    {
        this->assertConnectionStateIsAtLeast(SB_READY);        
        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-msnmsgr-datacast\r\n\r\n";
        msg_ << "ID: 1\r\n";
        size_t msg_length = msg_.str().size();
        
        buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str();
        
        write(buf_);        
    }

    void SwitchboardServerConnection::sendInk(std::string image)
    {
        this->assertConnectionStateIsAtLeast(SB_READY);

        if(users.size() == 1)
        {
            p2p.sendInk(*this, image);
//              return;
        }

        std::string body("base64:"+image);
        bool one_packet = false;

        if(body.size() <= 1202) // if we need more than 1 packet, then use multipacket
            one_packet = true ;

        if(one_packet)
        {
            std::ostringstream buf_, msg_;
            msg_ << "MIME-Version: 1.0\r\n";
            msg_ << "Content-Type: image/gif\r\n\r\n";
            msg_ << body;

            size_t msg_length = msg_.str().size();
            buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str();
            write(buf_);
            return;
        }
        else
        {
            std::istringstream body_stream(body);
            std::string messageid = new_branch();
            std::vector<std::string> chunks;
            // spliting the message
            while(!body_stream.eof())
            {
                char *part = new char[1203];
                memset(part,0,1203);
                body_stream.read((char*)part, 1202);
                std::string part1(part);
                chunks.push_back(part1);
                delete [] part;
            }

            // sending the first one
            std::ostringstream buf_, msg_;
            msg_ << "MIME-Version: 1.0\r\n";
            msg_ << "Content-Type: image/gif\r\n";
            msg_ << "Message-ID: " << messageid << "\r\n";
            msg_ << "Chunks: " << chunks.size() << "\r\n\r\n";
            msg_ << chunks.front();

            size_t msg_length = msg_.str().size();
            buf_ << "MSG " << this->trID++ << " N " << (int) msg_length << "\r\n" << msg_.str();
            write(buf_);

            std::vector<std::string>::iterator i = chunks.begin();

            for(int num=0; i!=chunks.end(); i++, num++)
            {
                if(i == chunks.begin())
                    continue;

                std::ostringstream buf2_, msg2_;
                msg2_ << "Message-ID: " << messageid << "\r\n";
                msg2_ << "Chunk: " << num << "\r\n\r\n";
                msg2_ << (*i);

                size_t msg_length2 = msg2_.str().size();
                buf2_ << "MSG " << this->trID++ << " N " << (int) msg_length2 << "\r\n" << msg2_.str();
                write(buf2_);
            }
        }
    }
 
    void SwitchboardServerConnection::sendTypingNotification()
    {
        this->assertConnectionStateIsAtLeast(SB_READY);        
        std::ostringstream buf_, msg_;
        msg_ << "MIME-Version: 1.0\r\n";
        msg_ << "Content-Type: text/x-msmsgscontrol\r\n";
        msg_ << "TypingUser: " << this->auth.username << "\r\n";
        msg_ << "\r\n";
        msg_ << "\r\n";
        size_t msg_length = msg_.str().size();
        
        buf_ << "MSG " << this->trID++ << " U " << (int) msg_length << "\r\n" << msg_.str();
        
        write(buf_);        
    }
    
    void SwitchboardServerConnection::inviteUser(Passport userName)
    {
        this->assertConnectionStateIsAtLeast(SB_WAITING_FOR_USERS);        
        std::ostringstream buf_;
        buf_ << "CAL " << this->trID++ << " " << userName << "\r\n";
        write(buf_);        
    }
    
    void SwitchboardServerConnection::connect(const std::string & hostname, unsigned int port)
    {
        this->assertConnectionStateIs(SB_DISCONNECTED);

        if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(hostname, port, &this->connected)) == NULL)
        {
            this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to switchboard server");
            return;
        }
        
        this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1, false);
        this->setConnectionState(SB_CONNECTING);
        
        if (this->connected)
            this->socketConnectionCompleted();
        
        std::ostringstream buf_;
        if (this->auth.sessionID.empty())
        {
            buf_ << "USR " << this->trID << " " << this->auth.username << " " << this->auth.cookie << "\r\n";
            if (this->write(buf_) != buf_.str().size())
                return;
            this->addCallback(&SwitchboardServerConnection::callback_InviteUsers, this->trID, NULL);
        } 
        else
        {
            buf_ << "ANS " << this->trID << " " << this->auth.username << " " << this->auth.cookie << " " << this->auth.sessionID << "\r\n";
            if (this->write(buf_) != buf_.str().size())
                return;
            this->myNotificationServer()->externalCallbacks.gotNewConnection(this);
            this->addCallback(&SwitchboardServerConnection::callback_AnsweredCall, this->trID, NULL);
        }
        
        this->trID++;    
    }
    
    void SwitchboardServerConnection::disconnect()
    {
        if (this->connectionState() == SB_DISCONNECTED)
            return;

        notificationServer.removeSwitchboardConnection(this);
        this->myNotificationServer()->externalCallbacks.closingConnection(this);
        
        std::list<FileTransferConnectionP2P *> list2 = _fileTransferConnectionsP2P;
        std::list<FileTransferConnectionP2P *>::iterator j = list2.begin();
        for (; j != list2.end(); j++)
        {
            removeFileTransferConnectionP2P(*j);
        }

        this->callbacks.clear();
        Connection::disconnect();
        this->setConnectionState(SB_DISCONNECTED);
    }    
    
    void SwitchboardServerConnection::socketConnectionCompleted()
    {
        this->assertConnectionStateIs(SB_CONNECTING);
        Connection::socketConnectionCompleted();
        this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock);
        this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0, false);
        this->setConnectionState(SB_WAITING_FOR_USERS);
    }
    
    void SwitchboardServerConnection::handleIncomingData()
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);        
        while (this->isWholeLineAvailable())
        {                
            std::vector<std::string> args = this->getLine();
            if (args[0] == "MSG" || args[0] == "NOT")
            {
                int dataLength = decimalFromString(args[3]);
                if (this->readBuffer.find("\r\n") + 2 + dataLength > this->readBuffer.size())
                    return;
            }
            this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n") + 2);
            
            int trid = 0;
            
            if (args.size() > 1)
            {
                try
                {
                    trid = decimalFromString(args[1]);
                }
                catch (...)
                {
                }
            }
            
            if (!this->callbacks.empty() && trid > 0)
            {
                if (this->callbacks.find(trid) != this->callbacks.end())
                {
                    (this->*(this->callbacks[trid].first))(args, trid, this->callbacks[trid].second);
                    continue;
                }
            }
            if (!this->callbacks2.empty() && trid > 0)
            {
                if (this->callbacks2.find(trid) != this->callbacks2.end())
                {
                    (this->*(this->callbacks2[trid].first))(args, trid, this->callbacks2[trid].second);
                    continue;
                }
            }
           
            if (isdigit(args[0][0]))
                this->showError(decimalFromString(args[0]));
            else
                this->dispatchCommand(args);
        }
    }
    
    void SwitchboardServerConnection::callback_messageACK(std::vector<std::string> & args, int trid, void *)
    {
        this->removeCallback(trid);
        this->myNotificationServer()->externalCallbacks.gotMessageSentACK(this, trid);
    }

    void SwitchboardServerConnection::callback_InviteUsers(std::vector<std::string> & args, int trid, void *)
    {    
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        this->removeCallback(trid);
        
        if (args.size() < 3)
        {
            this->showError(decimalFromString(args[0]));
            this->disconnect();
            return;
        }

        if(args[2] != "OK")
        {
                this->showError(decimalFromString(args[0]));
                this->disconnect();
                return;
        }
        
        this->myNotificationServer()->externalCallbacks.gotSwitchboard(this, this->auth.tag);
        this->myNotificationServer()->externalCallbacks.gotNewConnection(this);
    }
    
    void SwitchboardServerConnection::callback_continueTransfer(std::vector<std::string> & args, int trid, unsigned int sessionID)
    {
        this->removeP2PCallback(trid);
        p2p.handle_MSGACKReceived(*this,sessionID, args[1]);
    }

    void SwitchboardServerConnection::fileTransferResponse(unsigned int sessionID, std::string filename, bool response)
    {
        p2p.handle_fileTransferResponse(*this,sessionID, filename, response);
    }

    void SwitchboardServerConnection::callback_AnsweredCall(std::vector<std::string> & args, int trid, void * data)
    {
        this->assertConnectionStateIs(SB_WAITING_FOR_USERS);        
        if (args.size() >= 3 && args[0] == "ANS" && args[2] == "OK")
            return;
        
        if (isdigit(args[0][0]))
        {
            this->removeCallback(trid);
            this->showError(decimalFromString(args[0]));
            this->disconnect();
            return;
        }
        
        if (args.size() >= 6 && args[0] == "IRO")
        {
            if (args[4] == this->auth.username)
                return;
            
            this->users.push_back(args[4]);
            this->myNotificationServer()->externalCallbacks.buddyJoinedConversation(this, args[4], decodeURL(args[5]), 1);
            if (args[2] == args[3])
            {
                this->removeCallback(trid);
                this->setConnectionState(SB_READY);
            }
        }
    }
    
    void SwitchboardServerConnection::requestEmoticon(unsigned int id, std::string filename, std::string msnobject, std::string alias)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        p2p.requestEmoticon(*this,id, filename, msnobject, alias);
    }

    void SwitchboardServerConnection::requestWink(unsigned int id, std::string filename, std::string msnobject)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        p2p.requestWink(*this,id, filename, msnobject);
    }

    void SwitchboardServerConnection::requestVoiceClip(unsigned int id, std::string filename, std::string msnobject)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        p2p.requestVoiceClip(*this,id, filename, msnobject);
    }

    void SwitchboardServerConnection::requestDisplayPicture(unsigned int id, std::string filename, std::string msnobject)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        p2p.requestDisplayPicture(*this,id, filename, msnobject);
    }

    void SwitchboardServerConnection::cancelFileTransfer(unsigned int id)
    {
        this->assertConnectionStateIsAtLeast(SB_CONNECTED);
        p2p.cancelTransfer(*this,id);
    }
}
