/*
 * p2p.cpp
 * libmsn
 *
 * Created by Tiago Salem Herrmann on 08/2007.
 * 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/notificationserver.h>
#include <msn/errorcodes.h>
#include <msn/externals.h>
#include <msn/util.h>
#include <msn/p2p.h>
#include <msn/xmlParser.h>

#include <cctype>
#include <iostream>
#include <fstream>
#include <vector>

#include <string.h>

namespace MSN {

    P2P::P2P()
    {
        rand_helper = 1;
    }
    P2P::~P2P()
    {
    }


    void P2P::handleP2Pmessage(MSN::SwitchboardServerConnection &conn, std::vector<std::string> & args, std::string mime, std::string body)
    {
        Message::Headers headers = Message::Headers(mime);
        p2pPacket packet;
        // not for me, ignore it
        if(headers["P2P-Dest"] != conn.myNotificationServer()->myPassport)
            return;

        std::istringstream header(body,std::ios::binary);

        header.read((char*)&packet.p2pHeader.sessionID, sizeof(packet.p2pHeader.sessionID));
        header.read((char*)&packet.p2pHeader.identifier, sizeof(packet.p2pHeader.identifier));
        header.read((char*)&packet.p2pHeader.dataOffset, sizeof(packet.p2pHeader.dataOffset));
        header.read((char*)&packet.p2pHeader.totalDataSize, sizeof(packet.p2pHeader.totalDataSize));
        header.read((char*)&packet.p2pHeader.messageLength, sizeof(packet.p2pHeader.messageLength));
        header.read((char*)&packet.p2pHeader.flag, sizeof(packet.p2pHeader.flag));
        header.read((char*)&packet.p2pHeader.ackID, sizeof(packet.p2pHeader.ackID));
        header.read((char*)&packet.p2pHeader.ackUID, sizeof(packet.p2pHeader.ackUID));
        header.read((char*)&packet.p2pHeader.ackDataSize, sizeof(packet.p2pHeader.ackDataSize));
        char *c = new char[packet.p2pHeader.messageLength];
        header.read(c, packet.p2pHeader.messageLength);
        std::string content(c, packet.p2pHeader.messageLength);
        packet.body = content;
        delete [] c;
        header.read((char*)&packet.p2pFooter.appID, sizeof(packet.p2pFooter.appID));
        
        if(packet.p2pHeader.flag==FLAG_ACK)
        {
            handle_p2pACK(conn, packet);
            return;
        }

        if(packet.p2pHeader.sessionID == 0x40 &&
                little2big_endian(packet.p2pFooter.appID)==3) // INK, oh god!
        {
            p2pSession session;
            session.to = args[1];
            if(!packet.p2pHeader.dataOffset) // first packet
            {
                session.ink = packet.body;
                startedSessions[packet.p2pHeader.sessionID] = session;
                return;
            }
            else
            {
                if (packet.p2pHeader.dataOffset+packet.p2pHeader.messageLength
                        == packet.p2pHeader.totalDataSize)
                {
                    session = startedSessions[packet.p2pHeader.sessionID];
                    session.ink += packet.body;
                    sendACK(conn, packet, session);
                    startedSessions.erase(packet.p2pHeader.sessionID);
                    // TODO - THIS IS UGLY!
                    U8 *d1 = new U8[packet.p2pHeader.totalDataSize];
                    U8 *a = new U8[packet.p2pHeader.totalDataSize];
                    a[0]='\0';
                    a++;
                    const char *f = session.ink.c_str();
                    memcpy(a,f,session.ink.size());
                    a--;
                    _ucs2_utf8(d1, a, session.ink.size());
                    char *c = new char[packet.p2pHeader.totalDataSize+2];
                    char *g = new char[packet.p2pHeader.totalDataSize+2];
                    sprintf(c,"%s",(char*)d1);
                    sprintf(g,"%s",(char*)(d1+strlen((char*)d1)+1));

                    std::string d2(c);
                    std::string d3(g);
                    delete [] a;
                    delete [] c;
                    delete [] d1;
                    delete [] g;

                    conn.message_ink(args, d2, d3);
                    return;
                }
                if (packet.p2pHeader.dataOffset+packet.p2pHeader.messageLength
                        < packet.p2pHeader.totalDataSize)
                {
                    session = startedSessions[packet.p2pHeader.sessionID];
                    session.ink += packet.body;
                    startedSessions[packet.p2pHeader.sessionID] = session;
                    return;
                }
            }
            return;
        }

        // in these conditions, the packet is data, receive it to a file
        if(packet.p2pHeader.sessionID &&
               (packet.p2pHeader.flag == FLAG_FILE_DATA ||
                packet.p2pHeader.flag == FLAG_FILE_DATA2 ||
                packet.p2pHeader.flag == FLAG_DATA_EMOTICONS)) 
        {
            // we need to ensure we have a started session
            if(!startedSessions.count(packet.p2pHeader.sessionID))
                return;

            startedSessions[packet.p2pHeader.sessionID].step = STEP_RECEIVING;
            receiveP2PData(conn,packet);
            return;
        }

        if(packet.p2pHeader.sessionID && packet.p2pFooter.appID)
        {
            // we need to ensure we have a started session
            if(!startedSessions.count(packet.p2pHeader.sessionID))
                return;

            // data preparation, always?
            p2pSession session = startedSessions[packet.p2pHeader.sessionID];
            sendACK(conn, packet, session);
            return;
        }

        if(packet.p2pFooter.appID==APP_NONE) // when 0, assembly all the data before processing
        {
            // reassembly the packet
            if(packet.p2pHeader.messageLength < packet.p2pHeader.totalDataSize) // just part of the packet
            {
                if(pendingP2PMsg.count(packet.p2pHeader.identifier)==0) // it is the first part
                {
                    pendingP2PMsg[packet.p2pHeader.identifier]=packet;
                    return;
                }
                p2pPacket pkt_part = pendingP2PMsg[packet.p2pHeader.identifier];

                // not the first part
                pkt_part.body+=packet.body;

                if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset < 
                        packet.p2pHeader.totalDataSize)
                {
                    pendingP2PMsg[packet.p2pHeader.identifier]=pkt_part;
                    // this is not the last part, wait for the last one
                    return;
                }

                // shouldn't be reached
                if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset > 
                        packet.p2pHeader.totalDataSize)
                {
                    pendingP2PMsg.erase(packet.p2pHeader.identifier);
                    return;
                }
                if(packet.p2pHeader.messageLength+packet.p2pHeader.dataOffset == 
                        packet.p2pHeader.totalDataSize)
                {
                    packet=pkt_part;
                    pendingP2PMsg.erase(packet.p2pHeader.identifier);
                }
            }
        }

        if(!packet.body.find("INVITE"))
        {
            handle_INVITE(conn,packet);
        }
        else if (!packet.body.find("MSNSLP/1.0 200 OK"))
        {
            handle_200OK(conn,packet);
        }
        else if (!packet.body.find("BYE"))
        {
            handle_BYE(conn,packet);
        }
        else if(!packet.body.find("MSNSLP/1.0 603 Decline") || !packet.body.find("MSNSLP/1.0 603 DECLINE"))
        {
            handle_603Decline(conn,packet);
        }
/*        std::cout << "session id: " << packet.p2pHeader.sessionID << std::endl;
        std::cout << "identifier: " << packet.p2pHeader.identifier << std::endl;
        std::cout << "dataOffset: " << packet.p2pHeader.dataOffset << std::endl;
        std::cout << "totalDataSize: " << packet.p2pHeader.totalDataSize << std::endl;
        std::cout << "messageLength: " << packet.p2pHeader.messageLength << std::endl;
        std::cout << "flag: " << packet.p2pHeader.flag << std::endl;
        std::cout << "ackID: " << packet.p2pHeader.ackID << std::endl;
        std::cout << "ackUID: " << packet.p2pHeader.ackUID << std::endl;
        std::cout << "ackDataSize: " << packet.p2pHeader.ackDataSize << std::endl;
        std::cout << "footer: " << packet.p2pFooter.appID << std::endl << std::endl;
*/
    }

    void P2P::sendACK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session)
    {
        p2pPacket ack_pkt;

        std::ostringstream msghdr;
        std::ostringstream footer;
        std::ostringstream header;
        std::ostringstream full_msg;

        session.currentIdentifier++;
        if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier
            session.currentIdentifier++;

        // assembly the ack packet
        ack_pkt.p2pHeader.sessionID = packet.p2pHeader.sessionID;
        ack_pkt.p2pHeader.identifier = session.currentIdentifier;
        ack_pkt.p2pHeader.dataOffset = 0;
        ack_pkt.p2pHeader.totalDataSize = packet.p2pHeader.totalDataSize;
        ack_pkt.p2pHeader.messageLength = 0;
        ack_pkt.p2pHeader.flag = FLAG_ACK;
        ack_pkt.p2pHeader.ackID = packet.p2pHeader.identifier;
        ack_pkt.p2pHeader.ackUID = packet.p2pHeader.ackID;
        ack_pkt.p2pHeader.ackDataSize = packet.p2pHeader.totalDataSize;

        ack_pkt.p2pFooter.appID = APP_NONE;

        msghdr <<"MIME-Version: 1.0\r\n"
            "Content-Type: application/x-msnmsgrp2p\r\n"
            "P2P-Dest: " << conn.users.front() << "\r\n\r\n";

        header.write((char*)&ack_pkt.p2pHeader.sessionID, sizeof(ack_pkt.p2pHeader.sessionID));
        header.write((char*)&ack_pkt.p2pHeader.identifier, sizeof(ack_pkt.p2pHeader.identifier));
        header.write((char*)&ack_pkt.p2pHeader.dataOffset, sizeof(ack_pkt.p2pHeader.dataOffset));
        header.write((char*)&ack_pkt.p2pHeader.totalDataSize, sizeof(ack_pkt.p2pHeader.totalDataSize));
        header.write((char*)&ack_pkt.p2pHeader.messageLength, sizeof(ack_pkt.p2pHeader.messageLength));
        header.write((char*)&ack_pkt.p2pHeader.flag, sizeof(ack_pkt.p2pHeader.flag));
        header.write((char*)&ack_pkt.p2pHeader.ackID, sizeof(ack_pkt.p2pHeader.ackID));
        header.write((char*)&ack_pkt.p2pHeader.ackUID, sizeof(ack_pkt.p2pHeader.ackUID));
        header.write((char*)&ack_pkt.p2pHeader.ackDataSize, sizeof(ack_pkt.p2pHeader.ackDataSize));

        footer.write((char*)&ack_pkt.p2pFooter.appID,sizeof(ack_pkt.p2pFooter.appID));

        full_msg << msghdr.str() << header.str() << footer.str();

        std::ostringstream buf_;
        buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n";
        buf_ << full_msg.str();

        if (conn.write(buf_) != buf_.str().size())
            return;
/*            std::cout << "session id: " << ack_pkt.p2pHeader.sessionID << std::endl;
            std::cout << "identifier: " << ack_pkt.p2pHeader.identifier << std::endl;
            std::cout << "dataOffset: " << ack_pkt.p2pHeader.dataOffset << std::endl;
            std::cout << "totalDataSize: " << ack_pkt.p2pHeader.totalDataSize << std::endl;
            std::cout << "messageLength: " << ack_pkt.p2pHeader.messageLength << std::endl;
            std::cout << "flag: " << ack_pkt.p2pHeader.flag << std::endl;
            std::cout << "ackID: " << ack_pkt.p2pHeader.ackID << std::endl;
            std::cout << "ackUID: " << ack_pkt.p2pHeader.ackUID << std::endl;
            std::cout << "ackDataSize: " << ack_pkt.p2pHeader.ackDataSize << std::endl;
            std::cout << "footer: " << ack_pkt.p2pFooter.appID << std::endl << std::endl;
*/

    }

    void P2P::receiveP2PData(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {
        // check if there is no session
        if(!startedSessions.count(packet.p2pHeader.sessionID))
            return;

        p2pSession session = startedSessions[packet.p2pHeader.sessionID];
        if(!session.in_stream && STEP_RECEIVING_FINISHED)
            return;

        if(!session.in_stream->is_open())
        {
            startedSessions[packet.p2pHeader.sessionID].totalDataSize = packet.p2pHeader.totalDataSize;
            session.in_stream->open(session.filename.c_str(), std::ios::binary);
        }

        if(packet.body.length())
            session.in_stream->write(packet.body.c_str(), packet.body.length());

        // notify upper layer the current progress
        if(session.appID == APP_FILE_TRANSFER)
            conn.myNotificationServer()->externalCallbacks.fileTransferProgress(&conn, session.sessionID,session.in_stream->tellp(), packet.p2pHeader.totalDataSize);

        if((unsigned int)packet.p2pHeader.totalDataSize <= session.in_stream->tellp())
        {
            session.in_stream->close();
            session.step = STEP_RECEIVING_FINISHED;
            delete session.in_stream; // end of line
            session.in_stream=NULL;
            sendACK(conn, packet, session);
            startedSessions[packet.p2pHeader.sessionID]=session;

            if(session.appID == APP_DISPLAY_PICTURE ||
               session.appID == APP_DISPLAY_PICTURE2)
            {
                conn.myNotificationServer()->externalCallbacks.gotContactDisplayPicture(&conn, conn.users.front(), session.filename );
            }
            else
            {
                switch(session.typeTransfer)
                {
                    case APP_VOICE_CLIP:
                        libmsn_Siren7_DecodeVoiceClip(session.filename);
                        conn.myNotificationServer()->externalCallbacks.gotVoiceClipFile(&conn, session.sessionID, session.filename);
                        break;
                    case APP_EMOTICON:
                        conn.myNotificationServer()->externalCallbacks.gotEmoticonFile(&conn, session.sessionID, session.emoticonAlias, session.filename);
                        break;
                    case APP_WINK:
                        conn.myNotificationServer()->externalCallbacks.gotWinkFile(&conn, session.sessionID, session.filename);
                        break;
                }
                if(session.appID == APP_FILE_TRANSFER)
                    conn.myNotificationServer()->externalCallbacks.fileTransferSucceeded(&conn, session.sessionID);
            }

            if(session.appID != APP_FILE_TRANSFER)
            {
                send_BYE(conn, packet, session);
                this->addCallback(&P2P::handle_BYEACK, session.sessionID, packet.p2pHeader.ackID);
            }
        }
    }

    void P2P::sendP2PData(MSN::SwitchboardServerConnection &conn, p2pSession &session, p2pPacket &packet)
    {
        p2pPacket pkt_part = session.tempPacket;
        char part[1202];
        std::ostringstream msghdr;
        std::ostringstream footer;
        std::ostringstream header;
        std::ostringstream full_msg;

        msghdr <<"MIME-Version: 1.0\r\n"
            "Content-Type: application/x-msnmsgrp2p\r\n"
            "P2P-Dest: " << conn.users.front() << "\r\n\r\n";
        if(session.tempPacket.p2pHeader.ackID==0) // it means.. first packet
        {
            session.currentIdentifier++;
            if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier
                session.currentIdentifier++;

            session.tempPacket.p2pHeader.sessionID = session.sessionID;
            session.tempPacket.p2pHeader.identifier = session.currentIdentifier;

            if(session.appID == APP_FILE_TRANSFER)
                session.tempPacket.p2pHeader.flag = FLAG_FILE_DATA;
            else
                session.tempPacket.p2pHeader.flag = FLAG_DATA_PICTURE;

            session.tempPacket.p2pHeader.dataOffset = 0;
            session.tempPacket.p2pHeader.totalDataSize = FileSize(session.filename.c_str());
            session.tempPacket.p2pHeader.messageLength = 0;
            session.tempPacket.p2pHeader.ackUID = 0;
            session.tempPacket.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
            session.tempPacket.p2pHeader.ackDataSize=0;
            // swap to big endian
            session.tempPacket.p2pFooter.appID = little2big_endian(session.appID);
            // scheduling the action to do when it is finished
            this->addCallback(&P2P::handle_DataACK, session.sessionID, session.tempPacket.p2pHeader.ackID);
        }

        pkt_part = session.tempPacket;
        if(!session.out_stream)
            return;

        if(!session.out_stream->is_open())
            session.out_stream->open(session.filename.c_str(), std::ios::binary);

        pkt_part.p2pHeader.dataOffset = session.out_stream->tellg();
        session.out_stream->read(part, 1100);

        if(!session.out_stream->gcount())// nothing to read, go away
        {
            session.out_stream->close();
            delete session.out_stream;
            session.out_stream = NULL;
            startedSessions[session.sessionID]=session;
            if(session.appID == APP_FILE_TRANSFER)
                conn.myNotificationServer()->externalCallbacks.fileTransferSucceeded(&conn, session.sessionID);
            return;
        }

        pkt_part.p2pHeader.messageLength = session.out_stream->gcount();

        if(session.appID == APP_FILE_TRANSFER)
            conn.myNotificationServer()->externalCallbacks.fileTransferProgress(&conn, session.sessionID, pkt_part.p2pHeader.dataOffset, pkt_part.p2pHeader.totalDataSize);

        std::string a(part, pkt_part.p2pHeader.messageLength);
        std::istringstream temp_msg(a);

        header.write((char*)&pkt_part.p2pHeader.sessionID, sizeof(pkt_part.p2pHeader.sessionID));
        header.write((char*)&pkt_part.p2pHeader.identifier, sizeof(pkt_part.p2pHeader.identifier));
        header.write((char*)&pkt_part.p2pHeader.dataOffset, sizeof(pkt_part.p2pHeader.dataOffset));
        header.write((char*)&pkt_part.p2pHeader.totalDataSize, sizeof(pkt_part.p2pHeader.totalDataSize));
        header.write((char*)&pkt_part.p2pHeader.messageLength, sizeof(pkt_part.p2pHeader.messageLength));
        header.write((char*)&pkt_part.p2pHeader.flag, sizeof(pkt_part.p2pHeader.flag));
        header.write((char*)&pkt_part.p2pHeader.ackID, sizeof(pkt_part.p2pHeader.ackID));
        header.write((char*)&pkt_part.p2pHeader.ackUID, sizeof(pkt_part.p2pHeader.ackUID));
        header.write((char*)&pkt_part.p2pHeader.ackDataSize, sizeof(pkt_part.p2pHeader.ackDataSize));

        footer.write((char*)&pkt_part.p2pFooter.appID,sizeof(pkt_part.p2pFooter.appID));

        full_msg << msghdr.str() << header.str() << temp_msg.str() << footer.str();

        std::ostringstream buf_;
        buf_ << "MSG " << conn.trID << " D " << full_msg.str().size() << "\r\n";
        buf_ << full_msg.str();

        if (conn.write(buf_) != buf_.str().size())
            return;

        session.tempPacket = pkt_part;

        startedSessions[session.sessionID]=session;
        // call callback_continueTransfer when ack for this packet is received
        conn.addP2PCallback(&SwitchboardServerConnection::callback_continueTransfer, conn.trID++, session.sessionID);
    }

    void P2P::sendP2PPacket(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session)
    {
        std::ostringstream msghdr;
        std::istringstream msg(packet.body);
        std::ostringstream footer;

        if(session.to.empty())
            session.to = conn.users.front();

        msghdr <<"MIME-Version: 1.0\r\n"
            "Content-Type: application/x-msnmsgrp2p\r\n"
            "P2P-Dest: " << conn.users.front() << "\r\n\r\n";

        footer.write((char*)&packet.p2pFooter.appID,sizeof(packet.p2pFooter.appID));

        session.currentIdentifier++;
        if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier
            session.currentIdentifier++;

        packet.p2pHeader.identifier = session.currentIdentifier;

        // split big messages in many packets
        char temp_buf[1201];
        while(!msg.eof())
        {
            std::ostringstream header;
            std::ostringstream full_msg;
            packet.p2pHeader.dataOffset = msg.tellg();
            msg.read(temp_buf,1200);

            if(msg.gcount()==0)// nothing to read, go away
                break;

            packet.p2pHeader.totalDataSize = msg.str().size();
            packet.p2pHeader.messageLength = msg.gcount();

            std::string a(temp_buf, msg.gcount());
            std::istringstream temp_msg(a);

            header.write((char*)&packet.p2pHeader.sessionID, sizeof(packet.p2pHeader.sessionID));
            header.write((char*)&packet.p2pHeader.identifier, sizeof(packet.p2pHeader.identifier));
            header.write((char*)&packet.p2pHeader.dataOffset, sizeof(packet.p2pHeader.dataOffset));
            header.write((char*)&packet.p2pHeader.totalDataSize, sizeof(packet.p2pHeader.totalDataSize));
            header.write((char*)&packet.p2pHeader.messageLength, sizeof(packet.p2pHeader.messageLength));
            header.write((char*)&packet.p2pHeader.flag, sizeof(packet.p2pHeader.flag));
            header.write((char*)&packet.p2pHeader.ackID, sizeof(packet.p2pHeader.ackID));
            header.write((char*)&packet.p2pHeader.ackUID, sizeof(packet.p2pHeader.ackUID));
            header.write((char*)&packet.p2pHeader.ackDataSize, sizeof(packet.p2pHeader.ackDataSize));

            full_msg << msghdr.str() << header.str() << temp_msg.str() << footer.str();

            std::ostringstream buf_;
            buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n";
            buf_ << full_msg.str();

            if (conn.write(buf_) != buf_.str().size())
                return;
        }
    }

    void P2P::handle_session_changes(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session)
    {
        std::string body;
        std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
        msg[1]+="\r\n"; // it is really needed :(
        Message::Headers header_slp = Message::Headers(msg[0]);
        Message::Headers header_app = Message::Headers(msg[1]);
        switch(session.appID)
        {
            case APP_FILE_TRANSFER:
            {
                session.CSeq = decimalFromString(header_slp["CSeq"]);
                session.Bridges = header_app["Bridges"];
                session.NetID = decimalFromString(header_app["NetID"]);
                session.ConnType = header_app["Conn-Type"];
                session.ICF = header_app["ICF"];
                session.UPnPNat = header_app["UPnPNat"];
                session.Listening = header_app["Listening"];

                session.IPv4InternalAddrs = header_app["IPv4Internal-Addrs"];
                session.IPv4InternalPort = header_app["IPv4Internal-Port"];
                session.IPv4ExternalAddrs = header_app["IPv4External-Addrs"];
                session.IPv4ExternalPort = header_app["IPv4External-Port"];

                if(session.step == STEP_RECEIVING)
                {
                    return;
                }

                // direct connection, sending client is waiting connection.
                if(session.Listening == "true")
                {
/*                  // TODO - implement this part
                    bool a;
                    unsigned int port = decimalFromString(session.IPv4ExternalPort);
                    session.fileTransfer = new FileTransferConnectionP2P(conn, session);
                    if(!session.fileTransfer)
                        return;

                    session.fileTransfer->setDirection(FileTransferConnectionP2P::MSNFTP_RECV);
                    session.fileTransfer->setPerspective(FileTransferConnectionP2P::MSNFTP_CLIENT);
                    session.fileTransfer->sock = conn.myNotificationServer()->externalCallbacks.connectToServer(session.IPv4ExternalAddrs, port, &a);
                    conn.addFileTransferConnectionP2P(session.fileTransfer);

                    if (session.fileTransfer->sock < 0)
                    {
                        // if not possible to connect, stop
                        return;
                    }
                    std::ostringstream body2;
                    char b=4;
                    body2.write(&b,1);
                    body2.write("foo\0",4);

                    conn.myNotificationServer()->externalCallbacks.registerSocket(session.fileTransfer->sock,1,1);
                    session.fileTransfer->write(body2.str());*/
                }
                else if (conn.myNotificationServer()->direct_connection)
                {
                    // direct connection, we are waiting connection.
                    // direct_connection means we are directly connected
                    // to the internet
                    body=    "Bridge: TCPv1\r\n"
                        "Listening: true\r\n"
                        "Nonce: {00000000-0000-0000-0000-000000000000}\r\n";
                }
                else
                {
                    // switchboard file transfer, the slowest mode
                    body=    "Bridge: TCPv1\r\n"
                        "Listening: false\r\n"
                        "Nonce: {00000000-0000-0000-0000-000000000000}\r\n";
                }

                send_200OK(conn, session, body);
            }
        }
    }

    void P2P::handle_INVITE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {
        p2pSession session;
        std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
        msg[1]+="\r\n"; // this is really needed :(
        Message::Headers header_slp = Message::Headers(msg[0]);
        Message::Headers header_app = Message::Headers(msg[1]);

        session.to = header_slp["From"];
        session.to = splitString(header_slp["From"], ":")[1];
        session.to = splitString(session.to, ">")[0];
        session.from = header_slp["To"];
        session.from = splitString(header_slp["To"], ":")[1];
        session.from = splitString(session.from, ">")[0];

        session.Via = header_slp["Via"];
        session.CSeq = decimalFromString(header_slp["CSeq"]);
        session.CallID = header_slp["Call-ID"];
        session.ContentType = header_slp["Content-Type"];

        std::map<unsigned int, p2pSession>::iterator i = startedSessions.begin();
        for(; i != startedSessions.end(); i++)
        {
            if((*i).second.CallID == session.CallID)
            {
                // this isn't a new session, since I already have this callid
                p2pSession old_session = (*i).second;
                sendACK(conn, packet, session);
                old_session.Via = session.Via;
                old_session.CSeq = session.CSeq;
                old_session.ContentType = session.ContentType;
                // handle the changes received in this invitation packet
                handle_session_changes(conn, packet, old_session);
                return;
            }
        }
        //session.fromPassport = packet.fromPassport;
        session.sessionID = decimalFromString(header_app["SessionID"]);
        session.appID = decimalFromString(header_app["AppID"]);
        session.Context = header_app["Context"];

        // new connection goes below
        session.out_stream = new std::ifstream;
        session.tempPacket.p2pHeader.ackID=0;
        session.in_stream = NULL;
        session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++;
        session.baseIdentifier = session.currentIdentifier; // we need to keep this one
        sendACK(conn, packet, session);
        session.baseIdentifier++;
        session.step = STEP_ACK_INVITATION_SENT;

        switch(session.appID)
        {
            case APP_WEBCAM:
            {
                if(header_app["EUF-GUID"] == "{4BD96FC0-AB17-4425-A14A-439185962DC8}")
                {
                    //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID);
                    std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
                    send_200OK(conn, session, body);
                }
                if(header_app["EUF-GUID"] == "{1C9AA97E-9C05-4583-A3BD-908A196F1E92}")
                {
                    //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID);
                }
                break;
            }
            case APP_EMOTICON:
            case APP_DISPLAY_PICTURE:
            case APP_DISPLAY_PICTURE2:
            case APP_VOICE_CLIP:
            {
                // I dont know why, just following the rules.
                // I think this is the better place to do this
                session.currentIdentifier-=4;

                std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
                send_200OK(conn,session,body); // always accept display picture
                break;
            }
            case APP_FILE_TRANSFER:
            {
                session.currentIdentifier-=1;
                session.sending=false;
                startedSessions[session.sessionID]=session;
                std::istringstream context(b64_decode(session.Context.c_str()), std::ios::binary);

                std::string preview;
                unsigned int header_size;
                unsigned int type;
                bool has_preview =true;

                context.read((char*)&header_size, sizeof(unsigned int));
    
                context.seekg(0, std::ios::beg);
                context.seekg(16);
                context.read((char*)&type, sizeof(unsigned int));
            
                context.seekg(0, std::ios::end);
                // type == 1 means no preview
                if(header_size != context.tellg() && type != 1)
                    has_preview=true;

                context.seekg(0, std::ios::beg);
                context.seekg(19);

                U8 *filenameutf16 = new U8[520];
                U8 *filenameutf8 = new U8[520];
                context.read((char*)filenameutf16, 520);
                _ucs2_utf8(filenameutf8, filenameutf16, 520);
                std::string filename((char*)filenameutf8);
                delete [] filenameutf16;
                delete [] filenameutf8;

                unsigned long long filesize;
                context.seekg(8);
                context.read((char*)&filesize, sizeof(unsigned long long));

                if(has_preview)
                {
                    context.seekg(header_size);
                    int size2 = b64_decode(session.Context.c_str()).size() - header_size;
                    char *a = new char[size2];
                    context.read((char*)a, size2);
                    preview = b64_encode(a,size2);
                    delete [] a;
                }
                fileTransferInvite ft;
                ft.type = type;
                ft.sessionId = session.sessionID;
                ft.userPassport = conn.    users.front();
                ft.filename = filename;
                ft.filesize = filesize;
                ft.preview = preview;

                conn.myNotificationServer()->externalCallbacks.askFileTransfer(&conn, ft);
                return;
            }
        }
    }

    void P2P::handle_200OK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {
        p2pSession session;
        std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
        msg[1]+="\r\n"; // this is really needed :(
        Message::Headers header_slp = Message::Headers(msg[0]);
        Message::Headers header_app = Message::Headers(msg[1]);

        unsigned int tmp_session = decimalFromString(header_app["SessionID"]);

        if(!tmp_session)
            return;

        // we need to ensure we have a started session
        if(!startedSessions.count(tmp_session))
            return;

        session = startedSessions[tmp_session];

        sendACK(conn, packet, session);
        if(session.appID == APP_FILE_TRANSFER)
        {
            sendP2PData(conn, session, packet);
            conn.myNotificationServer()->externalCallbacks.fileTransferInviteResponse(&conn, tmp_session, true);
        }
    }

    void P2P::handle_603Decline(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {
        p2pSession session;
        std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
        msg[1]+="\r\n"; // this is really needed :(
        Message::Headers header_slp = Message::Headers(msg[0]);
        Message::Headers header_app = Message::Headers(msg[1]);

        unsigned int tmp_session = decimalFromString(header_app["SessionID"]);

        if(!tmp_session)
            return;

        // we need to ensure we have a started session
        if(!startedSessions.count(tmp_session))
            return;

        session = startedSessions[tmp_session];

        conn.myNotificationServer()->externalCallbacks.fileTransferInviteResponse(&conn, tmp_session, false);
    }

    void P2P::handle_BYE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {
        p2pSession session;
        std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
        if (msg.size() < 2)
        {
            std::cout << "P2P::handle_BYE ERROR size: " << msg.size() << " < 2" << std::endl;
            std::cout << "\'" << packet.body << "\'" << std::endl;
            return;
        }
        msg[1]+="\r\n"; // this is really needed :(
        Message::Headers header_slp = Message::Headers(msg[0]);
        Message::Headers header_app = Message::Headers(msg[1]);

        session.to = header_slp["From"];
        session.to = splitString(header_slp["From"], ":")[1];
        session.to = splitString(session.to, ">")[0];
        session.from = header_slp["To"];
        session.from = splitString(header_slp["To"], ":")[1];
        session.from = splitString(session.from, ">")[0];

        session.CSeq = decimalFromString(header_slp["CSeq"]);
        session.CallID = header_slp["Call-ID"];
        session.Via = header_slp["Via"];

        session.sessionID = decimalFromString(header_app["SessionID"]);
        session.appID = decimalFromString(header_app["AppID"]);
        session.Context = header_app["Context"];
        
        std::map<unsigned int, p2pSession>::iterator i = startedSessions.begin();
        for(; i != startedSessions.end(); i++)
        {
            if((*i).second.CallID == session.CallID)
            {
                sendACK(conn, packet, (*i).second);
                if((*i).second.in_stream && 
                    (unsigned int)(*i).second.totalDataSize > (*i).second.in_stream->tellp())
                {
                    if((*i).second.appID == APP_FILE_TRANSFER)
                        conn.myNotificationServer()->externalCallbacks.fileTransferFailed(&conn, (*i).second.sessionID, MSN::FILE_TRANSFER_ERROR_USER_CANCELED);
                }
                if(!(*i).second.in_stream && (*i).second.appID == APP_FILE_TRANSFER)
                {
                    if((*i).second.appID == APP_FILE_TRANSFER)
                        conn.myNotificationServer()->externalCallbacks.fileTransferFailed(&conn, (*i).second.sessionID, MSN::FILE_TRANSFER_ERROR_USER_CANCELED);
                }
                if((*i).second.in_stream)
                {
                    if((*i).second.in_stream->is_open())
                        (*i).second.in_stream->close();

                    delete (*i).second.in_stream;
                    (*i).second.in_stream = NULL;
                }
                startedSessions.erase((*i).second.sessionID);
                return;
            }
        }
    }

    void P2P::handle_p2pACK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
    {

            if (!this->callbacks.empty() && packet.p2pHeader.ackUID > 0)
            {
                if (this->callbacks.find(packet.p2pHeader.ackUID) != this->callbacks.end())
                {
                    (this->*(this->callbacks[packet.p2pHeader.ackUID].first))(conn,this->callbacks[packet.p2pHeader.ackUID].second, packet);
                }
            }
    }

    void P2P::send_200OK(MSN::SwitchboardServerConnection &conn, p2pSession &session, std::string body)
    {
        p2pPacket packet;

        std::ostringstream body2;
        body2.write("\0",1);
        std::string body_final("\r\n"+body+body2.str());

        if(session.ContentType == "application/x-msnmsgr-transreqbody")
            session.ContentType="application/x-msnmsgr-transrespbody";

        std::string content("MSNSLP/1.0 200 OK\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: "+ toStr(++session.CSeq) + "\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: "+session.ContentType +"\r\n"
                "Content-Length: "+ toStr(body_final.length())+ "\r\n"
                +body_final);

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;

        sendP2PPacket(conn, packet, session);

        session.step = STEP_200OK_SENT;

        startedSessions[session.sessionID]=session;

        this->addCallback(&P2P::handle_200OKACK, session.sessionID, packet.p2pHeader.ackID);
    }

    void P2P::send_BYE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session)
    {
        std::ostringstream body;
        body.write("\r\n\0",3);
        std::string content("BYE MSNMSGR:"+session.to+" MSNSLP/1.0\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: 0\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionclosebody\r\n"
                "Content-Length: "+ toStr(body.str().size())+ "\r\n"
                +body.str());

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;

        sendP2PPacket(conn, packet, session);

        session.step = STEP_BYE_SENT;

        startedSessions[session.sessionID]=session;

//        this->addCallback(&P2P::handle_200OKACK, session.sessionID, packet.p2pHeader.ackID);
    }


    void P2P::send_603Decline(MSN::SwitchboardServerConnection &conn, p2pSession &session)
    {
        p2pPacket packet;

        std::ostringstream body2;
        body2.write("\0",1);
        std::string body("\r\nSessionID: "+ toStr(session.sessionID)+"\r\n"+body2.str());
        std::string content("MSNSLP/1.0 603 Decline\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: "+ toStr(++session.CSeq) + "\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
                "Content-Length: "+ toStr(body.length())+ "\r\n"
                +body);

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;

        sendP2PPacket(conn, packet, session);

        session.step = STEP_603DECLINE_SENT;

        startedSessions[session.sessionID]=session;

        this->addCallback(&P2P::handle_603DeclineACK, session.sessionID, packet.p2pHeader.ackID);
    }


    void P2P::addCallback(P2PCallbacks callback, unsigned int sessionID, unsigned int ackID)
    {
        this->callbacks[ackID] = std::make_pair(callback,sessionID);
    }

    void P2P::removeCallback(unsigned int ackID)
    {
        this->callbacks.erase(ackID);
    }

    void P2P::handle_603DeclineACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
        this->removeCallback(packet.p2pHeader.ackUID);
        startedSessions.erase(sessionID);
    }

    void P2P::handle_200OKACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
        p2pPacket pkt_prep;

        this->removeCallback(packet.p2pHeader.ackUID);
        if (startedSessions.find(sessionID) == startedSessions.end())
        {
            return;
        }
        p2pSession session = startedSessions[sessionID];
        session.step = STEP_200OK_ACK_SENT;
        
        switch(session.appID)
        {
            case APP_EMOTICON:
            case APP_WEBCAM:
            case APP_DISPLAY_PICTURE:
            case APP_DISPLAY_PICTURE2:
            case APP_VOICE_CLIP:
            {
                pkt_prep.p2pHeader.sessionID = sessionID;
                pkt_prep.p2pHeader.flag = FLAG_NOP;
                pkt_prep.p2pHeader.identifier = session.currentIdentifier;
                pkt_prep.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
                pkt_prep.p2pHeader.ackUID = 0;
                pkt_prep.p2pHeader.ackDataSize = 0;
                // big endian
                pkt_prep.p2pFooter.appID = little2big_endian(session.appID);

                std::ostringstream content;
                content.write("\0\0\0\0",4);
                pkt_prep.body = content.str();

                sendP2PPacket(conn, pkt_prep, session);
                session.step = STEP_DATA_PREPARATION_SENT;
                session.typeTransfer = (p2pTransferObj)session.appID;

                startedSessions[sessionID]=session;

                this->addCallback(&P2P::handle_DataPreparationACK, session.sessionID, pkt_prep.p2pHeader.ackID);
                break;
            }
            case APP_FILE_TRANSFER:
            {
                // great, on 200OK from file transfer the packet does not 
                // have appID
                // We should wait now an inviting for direct connection
            }
        }
    }

    void P2P::handle_DataPreparationACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
        this->removeCallback(packet.p2pHeader.ackUID);
        p2pSession session = startedSessions[sessionID];
        session.step = STEP_SENDING;
        std::string filepath;
        filepath += b64_decode(session.Context.c_str()); // prevents empty context
        if(filepath.length())
        {
            if(!conn.myNotificationServer()->msnobj.getMSNObjectRealPath(b64_decode(session.Context.c_str()), session.filename))
            {
                send_603Decline(conn,session);
                return;
            }
        }
        else
        {
            send_603Decline(conn,session);
            return;
        }
        sendP2PData(conn, session, packet);
    }

    void P2P::handle_MSGACKReceived(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string fromPassport)
    {
        p2pPacket packet;
        if(startedSessions.find(sessionID) == startedSessions.end())
            return;

        p2pSession session = startedSessions[sessionID];
        sendP2PData(conn, session, packet);
    }

    void P2P::handle_fileTransferResponse(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, bool response)
    {
        p2pSession session = startedSessions[sessionID];
        session.filename = filename;
        if(response) // user accepted
        {
            session.in_stream = new std::ofstream;
            std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
            send_200OK(conn, session, body);
        }
        else // user rejected
        {
            // I dont want to receive your file, blergh
            send_603Decline(conn,session);
        }
    }

    void P2P::handle_DataACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
        this->removeCallback(packet.p2pHeader.ackUID);

        p2pPacket pkt_bye;

        std::string branch = new_branch();

        p2pSession session = startedSessions[sessionID];
        session.step = STEP_DATA_TRANSFER_ACK;

        std::ostringstream body;
        body.write("\r\n\0",3);
        std::string content("BYE MSNMSGR:"+session.to+" MSNSLP/1.0\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: MSNSLP/1.0/TLP ;branch="+branch+"\r\n"
                "CSeq: 0\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionclosebody\r\n"
                "Content-Length: "+ toStr(body.str().length())+ "\r\n"
                +body.str());

        pkt_bye.p2pHeader.sessionID = 0;
        pkt_bye.p2pHeader.identifier = session.currentIdentifier;
        pkt_bye.p2pHeader.flag = FLAG_NOP;
        pkt_bye.p2pHeader.dataOffset = 0;
        pkt_bye.p2pHeader.totalDataSize = content.length();
        pkt_bye.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        pkt_bye.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        pkt_bye.p2pHeader.ackUID = 0;
        pkt_bye.p2pHeader.ackDataSize=0;

        pkt_bye.body = content;

        pkt_bye.p2pFooter.appID = APP_NONE;

        sendP2PPacket(conn, pkt_bye, session);

        session.step = STEP_BYE_SENT;

        startedSessions[session.sessionID]=session;

        this->addCallback(&P2P::handle_BYEACK, session.sessionID, pkt_bye.p2pHeader.ackID);
    }

    void P2P::handle_BYEACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
        this->removeCallback(packet.p2pHeader.ackUID);
    }

    void P2P::requestDisplayPicture(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject)
    {
        p2pSession session;
        session.Context = b64_encode(msnobject.c_str(), msnobject.size());
        session.CSeq = 0;
        session.sessionID = sessionID;
        session.filename = filename;
        session.CallID = new_branch();
        session.to = conn.users.front() ;
        session.from = conn.myNotificationServer()->myPassport;
        session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++;
        session.baseIdentifier = session.currentIdentifier;
        session.Via = "MSNSLP/1.0/TLP ;branch=";
        session.Via+= new_branch();
        session.appID = APP_DISPLAY_PICTURE2;
        p2pPacket packet;
        std::ostringstream body2;
        body2.write("\0",1);
        std::string body("\r\n"
                "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n"
                "SessionID: "+ toStr(session.sessionID)+"\r\n"
                "AppID: 1\r\n"
                "Context: " + session.Context + "\r\n"+body2.str());
        std::string content("INVITE MSNMSGR:"+ session.to +" MSNSLP/1.0\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: "+ toStr(session.CSeq++) + "\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
                "Content-Length: "+ toStr(body.length())+ "\r\n"
                +body);

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;

        session.in_stream = new std::ofstream;

        sendP2PPacket(conn, packet, session);

        session.currentIdentifier = session.currentIdentifier - 3;

        startedSessions[session.sessionID]=session;
    }

    void P2P::requestFile(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject, p2pTransferObj obj)
    {
        p2pSession session;
        if(startedSessions.find(sessionID) != startedSessions.end())
            session = startedSessions[sessionID];

        session.Context = b64_encode(msnobject.c_str(), msnobject.size());
        session.CSeq = 0;
        session.sessionID = sessionID;
        session.filename = filename;
        session.CallID = new_branch();
        session.to = conn.users.front() ;
        session.from = conn.myNotificationServer()->myPassport;
        session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++;
        session.baseIdentifier = session.currentIdentifier;
        session.Via = "MSNSLP/1.0/TLP ;branch=";
        session.Via+= new_branch();
        session.typeTransfer = obj;

        p2pPacket packet;
        std::ostringstream body2;
        body2.write("\0",1);
        std::string body("\r\n"
                "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n"
                "SessionID: "+ toStr(session.sessionID)+"\r\n"
                "AppID: 1\r\n"
                "Context: " + session.Context + "\r\n"+body2.str());
        std::string content("INVITE MSNMSGR:"+ session.to +" MSNSLP/1.0\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: "+ toStr(session.CSeq++) + "\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
                "Content-Length: "+ toStr(body.length())+ "\r\n"
                +body);

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;

        session.in_stream = new std::ofstream;

        sendP2PPacket(conn, packet, session);

        session.currentIdentifier = session.currentIdentifier - 3;

        startedSessions[session.sessionID]=session;
    }

    void P2P::requestEmoticon(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject, std::string alias)
    {
        p2pSession session;
        session.emoticonAlias = alias;
        startedSessions[sessionID]=session;
        requestFile(conn, sessionID, filename, msnobject, APP_EMOTICON);
    }

    void P2P::requestVoiceClip(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject)
    {
        requestFile(conn, sessionID, filename, msnobject, APP_VOICE_CLIP);
    }

    void P2P::requestWink(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, std::string filename, std::string msnobject)
    {
        requestFile(conn, sessionID, filename, msnobject, APP_WINK);
    }

    void P2P::handle_INVITE_ACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
    {
    }

    std::string build_file_transfer_context(MSN::fileTransferInvite ft)
    {
        std::ostringstream context;
        unsigned int hlength=0x27E;
        unsigned int msnversion=0x03;
        unsigned long long filesize = FileSize(ft.filename.c_str());
        unsigned int type = ft.type;
        char filename[520];
        char unknown1[30];
        unsigned int unknown2 = (ft.type == FILE_TRANSFER_BACKGROUND_SHARING) ||
               (ft.type == FILE_TRANSFER_BACKGROUND_SHARING_CUSTOM) ? 0xFFFFFE : 0xFFFFFF ;
        char unknown3[64];

        memset(&filename,0,520);
        memset(&unknown1,0,30);
        memset(&unknown3,0,64);

        // TODO - convert filename to ucs2
        U8 *filenameutf8 = new U8[520];
        U8 *filenameutf16 = new U8[521];
        memset(filenameutf8, 0, 520);
        memset(filenameutf16, 0, 521);
        memcpy(filenameutf8, ft.friendlyname.c_str(), ft.friendlyname.size());
        _utf8_ucs2(filenameutf16, filenameutf8);
        filenameutf16++;

        context.write((char*)&hlength, sizeof(unsigned int));
        context.write((char*)&msnversion, sizeof(unsigned int));
        context.write((char*)&filesize, sizeof(unsigned long long));
        context.write((char*)&type, sizeof(unsigned int));
        context.write((char*)filenameutf16, 520);
        context.write((char*)&unknown1, 30);
        context.write((char*)&unknown2, sizeof(unsigned int));
        context.write((char*)&unknown3, 64);

        filenameutf16--;
        delete [] filenameutf16;
        delete [] filenameutf8;

        if(ft.type == FILE_TRANSFER_WITH_PREVIEW && ft.preview.size())
            context.write((char*)b64_decode(ft.preview.c_str()).c_str(), b64_decode(ft.preview.c_str()).size());

        return b64_encode(context.str().c_str(), context.str().size());
    }

    void P2P::sendFile(MSN::SwitchboardServerConnection &conn, MSN::fileTransferInvite ft)
    {
        p2pSession session;
        session.Context = build_file_transfer_context(ft);
        session.CSeq = 0;
        session.sessionID = ft.sessionId;
        session.filename = ft.filename;
        session.CallID = new_branch();
        session.to = conn.users.front();
        session.from = conn.myNotificationServer()->myPassport;
        session.currentIdentifier = rand()%0x8FFFFFF0 + rand_helper++;
        session.baseIdentifier = session.currentIdentifier;
        session.Via = "MSNSLP/1.0/TLP ;branch=";
        session.Via+= new_branch();
        session.tempPacket.p2pHeader.ackID=0;
        session.in_stream = NULL;

        p2pPacket packet;
        std::ostringstream body2;
        body2.write("\0",1);
        std::string other_passport = conn.users.front();
        std::string body("\r\n"
                "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\n"
                "SessionID: "+ toStr(session.sessionID)+"\r\n"
                "SChannelState: 0\r\n"
                "Capabilities-Flags: 1\r\n"
                "AppID: 2\r\n"
                "Context: " + session.Context + "\r\n"+body2.str());
        std::string content("INVITE MSNMSGR:"+ other_passport +" MSNSLP/1.0\r\n"
                "To: <msnmsgr:"+session.to+">\r\n"
                "From: <msnmsgr:"+session.from+">\r\n"
                "Via: "+session.Via+"\r\n"
                "CSeq: "+ toStr(session.CSeq++) + "\r\n"
                "Call-ID: "+session.CallID+"\r\n"
                "Max-Forwards: 0\r\n"
                "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
                "Content-Length: "+ toStr(body.length())+ "\r\n"
                +body);

        packet.p2pHeader.sessionID = 0;
        packet.p2pHeader.identifier = session.currentIdentifier;
        packet.p2pHeader.flag = FLAG_NOP;
        packet.p2pHeader.dataOffset = 0;
        packet.p2pHeader.totalDataSize = content.length();
        packet.p2pHeader.messageLength = 0; // filled  inside sendP2PPacket()
        packet.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
        packet.p2pHeader.ackUID = 0;
        packet.p2pHeader.ackDataSize=0;

        packet.body = content;

        packet.p2pFooter.appID = APP_NONE;
        session.appID = APP_FILE_TRANSFER;

        session.out_stream = new std::ifstream;

        sendP2PPacket(conn, packet, session);

        startedSessions[session.sessionID]=session;
    }

    void P2P::sendInk(MSN::SwitchboardServerConnection &conn, std::string image)
    {
        std::list<Passport>::iterator i = conn.users.begin();
        // for each user

    }
    void P2P::cancelTransfer(MSN::SwitchboardServerConnection &conn, unsigned int sessionID)
    {
        p2pSession session;
        p2pPacket packet;
        if(startedSessions.find(sessionID) == startedSessions.end())
            return;

        session = startedSessions[sessionID];
        send_BYE(conn, packet, session);
        startedSessions.erase(sessionID);
    }
}
