Chat-O-Matic/libs/libmsn/p2p.cpp

1414 lines
57 KiB
C++

/*
* 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 "notificationserver.h"
#include "errorcodes.h"
#include "externals.h"
#include "util.h"
#include "p2p.h"
#include "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);
}
}