|
|
|
/*
|
|
|
|
dispatcher.cpp - msn p2p protocol
|
|
|
|
|
|
|
|
Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
|
|
|
|
Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
|
|
|
|
|
|
|
|
*************************************************************************
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
*************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dispatcher.h"
|
|
|
|
#include "incomingtransfer.h"
|
|
|
|
#include "outgoingtransfer.h"
|
|
|
|
|
|
|
|
#if MSN_WEBCAM
|
|
|
|
#include "webcam.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using P2P::Dispatcher;
|
|
|
|
using P2P::Message;
|
|
|
|
using P2P::TransferContext;
|
|
|
|
using P2P::IncomingTransfer;
|
|
|
|
using P2P::OutgoingTransfer;
|
|
|
|
|
|
|
|
#include "msnswitchboardsocket.h"
|
|
|
|
|
|
|
|
// Kde includes
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kmdcodec.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <tdetempfile.h>
|
|
|
|
|
|
|
|
// TQt includes
|
|
|
|
#include <tqdatastream.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqtextcodec.h>
|
|
|
|
#include <tqtextstream.h>
|
|
|
|
|
|
|
|
// Kopete includes
|
|
|
|
#include <kopetechatsession.h> // Just for getting the contact
|
|
|
|
#include <kopeteaccount.h>
|
|
|
|
#include <kopetetransfermanager.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
Dispatcher::Dispatcher(TQObject *parent, const TQString& contact, const TQStringList &ip)
|
|
|
|
: TQObject(parent) , m_contact(contact) , m_callbackChannel(0l) , m_ip(ip)
|
|
|
|
{}
|
|
|
|
|
|
|
|
Dispatcher::~Dispatcher()
|
|
|
|
{
|
|
|
|
kdDebug(14140) << k_funcinfo << endl;
|
|
|
|
|
|
|
|
if(m_callbackChannel)
|
|
|
|
{
|
|
|
|
delete m_callbackChannel;
|
|
|
|
m_callbackChannel = 0l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::detach(TransferContext* transfer)
|
|
|
|
{
|
|
|
|
m_sessions.remove(transfer->m_sessionId);
|
|
|
|
transfer->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString Dispatcher::localContact()
|
|
|
|
{
|
|
|
|
return m_contact;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::requestDisplayIcon(const TQString& from, const TQString& msnObject)
|
|
|
|
{
|
|
|
|
TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
|
|
|
|
TransferContext* current =
|
|
|
|
new IncomingTransfer(from, this, sessionId);
|
|
|
|
|
|
|
|
current->m_branch = P2P::Uid::createUid();
|
|
|
|
current->m_callId = P2P::Uid::createUid();
|
|
|
|
current->setType(P2P::UserDisplayIcon);
|
|
|
|
current->m_object = msnObject;
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId, current);
|
|
|
|
|
|
|
|
kdDebug(14140) << k_funcinfo << "Requesting, " << msnObject << endl;
|
|
|
|
|
|
|
|
TQString context = TQString::fromUtf8(KCodecs::base64Encode(msnObject.utf8()));
|
|
|
|
// NOTE remove the \0 character automatically
|
|
|
|
// appended to a TQCString.
|
|
|
|
context.replace("=", TQString());
|
|
|
|
TQString content =
|
|
|
|
"EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n"
|
|
|
|
"SessionID: " + TQString::number(sessionId) + "\r\n"
|
|
|
|
"AppID: 1\r\n"
|
|
|
|
"Context: " + context + "\r\n"
|
|
|
|
"\r\n";
|
|
|
|
// Send the sending client an invitation message.
|
|
|
|
current->sendMessage(INVITE, content);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::sendFile(const TQString& path, TQ_INT64 fileSize, const TQString& to)
|
|
|
|
{
|
|
|
|
// Create a new transfer context that will handle
|
|
|
|
// the file transfer.
|
|
|
|
TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
|
|
|
|
TransferContext *current =
|
|
|
|
new OutgoingTransfer(to, this, sessionId);
|
|
|
|
current->m_branch = P2P::Uid::createUid();
|
|
|
|
current->m_callId = P2P::Uid::createUid();
|
|
|
|
current->setType(P2P::File);
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId, current);
|
|
|
|
|
|
|
|
// Set the transfer context file.
|
|
|
|
current->m_file = new TQFile(path);
|
|
|
|
// Create the file context data.
|
|
|
|
TQString context;
|
|
|
|
|
|
|
|
TQByteArray header(638);
|
|
|
|
header.fill('\0');
|
|
|
|
TQDataStream writer(header, IO_WriteOnly);
|
|
|
|
writer.setByteOrder(TQDataStream::LittleEndian);
|
|
|
|
|
|
|
|
// Write the header length to the stream.
|
|
|
|
writer << (TQ_INT32)638;
|
|
|
|
// Write client version to the stream.
|
|
|
|
writer << (TQ_INT32)3;
|
|
|
|
// Write the file size to the stream.
|
|
|
|
writer << fileSize;
|
|
|
|
// Write the file transfer flag to the stream.
|
|
|
|
// TODO support file preview. For now disable file preview.
|
|
|
|
writer << (TQ_INT32)1;
|
|
|
|
// Write the file name in utf-16 to the stream.
|
|
|
|
TQTextStream ts(header, IO_WriteOnly);
|
|
|
|
ts.setEncoding(TQTextStream::RawUnicode);
|
|
|
|
ts.device()->at(20);
|
|
|
|
ts << path.section('/', -1);
|
|
|
|
// NOTE Background Sharing base64 [540..569]
|
|
|
|
// TODO add support for background sharing.
|
|
|
|
// Write file exchange type to the stream.
|
|
|
|
// NOTE File - 0xFFFFFFFF
|
|
|
|
// NOTE Background Sharing - 0xFFFFFFFE
|
|
|
|
writer.device()->at(570);
|
|
|
|
writer << (TQ_UINT32)0xFFFFFFFF;
|
|
|
|
|
|
|
|
// Encode the file context header to base64 encoding.
|
|
|
|
context = TQString::fromUtf8(KCodecs::base64Encode(header));
|
|
|
|
|
|
|
|
// Send an INVITE message to the recipient.
|
|
|
|
TQString content = "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\n"
|
|
|
|
"SessionID: " + TQString::number(sessionId) + "\r\n"
|
|
|
|
"AppID: 2\r\n"
|
|
|
|
"Context: " + context + "\r\n"
|
|
|
|
"\r\n";
|
|
|
|
current->sendMessage(INVITE, content);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::sendImage(const TQString& /*fileName*/, const TQString& /*to*/)
|
|
|
|
{
|
|
|
|
// TODO kdDebug(14140) << k_funcinfo << endl;
|
|
|
|
// TQFile imageFile(fileName);
|
|
|
|
// if(!imageFile.open(IO_ReadOnly))
|
|
|
|
// {
|
|
|
|
// kdDebug(14140) << k_funcinfo << "Error opening image file."
|
|
|
|
// << endl;
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// OutgoingTransfer *outbound =
|
|
|
|
// new OutgoingTransfer(to, this, 64);
|
|
|
|
//
|
|
|
|
// outbound->sendImage(imageFile.readAll());
|
|
|
|
}
|
|
|
|
|
|
|
|
#if MSN_WEBCAM
|
|
|
|
void Dispatcher::startWebcam(const TQString &/*myHandle*/, const TQString &msgHandle, bool wantToReceive)
|
|
|
|
{
|
|
|
|
TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
|
|
|
|
Webcam::Who who= wantToReceive ? Webcam::wViewer : Webcam::wProducer;
|
|
|
|
TransferContext* current =
|
|
|
|
new Webcam(who, msgHandle, this, sessionId);
|
|
|
|
|
|
|
|
current->m_branch = P2P::Uid::createUid();
|
|
|
|
current->m_callId = P2P::Uid::createUid();
|
|
|
|
current->setType(P2P::WebcamType);
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId, current);
|
|
|
|
|
|
|
|
// {4BD96FC0-AB17-4425-A14A-439185962DC8} <- i want to show you my webcam
|
|
|
|
// {1C9AA97E-9C05-4583-A3BD-908A196F1E92} <- i want to see your webcam
|
|
|
|
TQString GUID= (who==Webcam::wProducer) ? "4BD96FC0-AB17-4425-A14A-439185962DC8" : "1C9AA97E-9C05-4583-A3BD-908A196F1E92" ;
|
|
|
|
|
|
|
|
TQString content="EUF-GUID: {"+GUID+"}\r\n"
|
|
|
|
"SessionID: "+ TQString::number(sessionId)+"\r\n"
|
|
|
|
"AppID: 4\r\n"
|
|
|
|
"Context: ewBCADgAQgBFADcAMABEAEUALTQBFADIAQwBBAC0ANAA0ADAAMAAtAEEARTQAwADMALQA4ADgARgBGADgANTQBCADkARgA0AEUAOAB9AA==\r\n\r\n";
|
|
|
|
|
|
|
|
// context is the base64 of the utf16 of {B8BE70DE-E2CA-4400-AE03-88FF85B9F4E8}
|
|
|
|
|
|
|
|
current->sendMessage( INVITE , content );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Dispatcher::slotReadMessage(const TQString &from, const TQByteArray& stream)
|
|
|
|
{
|
|
|
|
P2P::Message receivedMessage =
|
|
|
|
m_messageFormatter.readMessage(stream);
|
|
|
|
|
|
|
|
receivedMessage.source = from;
|
|
|
|
|
|
|
|
if(receivedMessage.contentType == "application/x-msnmsgrp2p")
|
|
|
|
{
|
|
|
|
if((receivedMessage.header.dataSize == 0)/* && ((receivedMessage.header.flag & 0x02) == 0x02)*/)
|
|
|
|
{
|
|
|
|
TransferContext *current = 0l;
|
|
|
|
TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
|
|
|
|
for(; it != m_sessions.end(); it++)
|
|
|
|
{
|
|
|
|
if(receivedMessage.header.ackSessionIdentifier == it.data()->m_identifier){
|
|
|
|
current = it.data();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(current){
|
|
|
|
// Inform the transfer object of the acknowledge.
|
|
|
|
current->m_ackSessionIdentifier = receivedMessage.header.identifier;
|
|
|
|
current->m_ackUniqueIdentifier = receivedMessage.header.ackSessionIdentifier;
|
|
|
|
current->acknowledged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdDebug(14140) << k_funcinfo
|
|
|
|
<< "no transfer context with identifier, "
|
|
|
|
<< receivedMessage.header.ackSessionIdentifier
|
|
|
|
<< endl;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_messageBuffer.contains(receivedMessage.header.identifier))
|
|
|
|
{
|
|
|
|
kdDebug(14140) << k_funcinfo
|
|
|
|
<< TQString("retrieving buffered messsage, %1").arg(receivedMessage.header.identifier)
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
// The message was split, try to reconstruct the message
|
|
|
|
// with this received piece.
|
|
|
|
Message bufferedMessage = m_messageBuffer[receivedMessage.header.identifier];
|
|
|
|
// Remove the buffered message.
|
|
|
|
m_messageBuffer.remove(receivedMessage.header.identifier);
|
|
|
|
|
|
|
|
bufferedMessage.body.resize(bufferedMessage.body.size() + receivedMessage.header.dataSize);
|
|
|
|
for(TQ_UINT32 i=0; i < receivedMessage.header.dataSize; i++){
|
|
|
|
// Add the remaining message data to the buffered message.
|
|
|
|
bufferedMessage.body[receivedMessage.header.dataOffset + i] = receivedMessage.body[i];
|
|
|
|
}
|
|
|
|
bufferedMessage.header.dataSize += receivedMessage.header.dataSize;
|
|
|
|
bufferedMessage.header.dataOffset = 0;
|
|
|
|
|
|
|
|
receivedMessage = bufferedMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispatch the received message.
|
|
|
|
dispatch(receivedMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::dispatch(const P2P::Message& message)
|
|
|
|
|
|
|
|
{
|
|
|
|
TransferContext *messageHandler = 0l;
|
|
|
|
|
|
|
|
if(message.header.sessionId > 0)
|
|
|
|
{
|
|
|
|
if(m_sessions.contains(message.header.sessionId)){
|
|
|
|
messageHandler = m_sessions[message.header.sessionId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQString body =
|
|
|
|
TQCString(message.body.data(), message.header.dataSize);
|
|
|
|
TQRegExp regex("SessionID: ([0-9]*)\r\n");
|
|
|
|
if(regex.search(body) > 0)
|
|
|
|
{
|
|
|
|
TQ_UINT32 sessionId = regex.cap(1).toUInt();
|
|
|
|
if(m_sessions.contains(sessionId)){
|
|
|
|
// Retrieve the message handler associated with the specified session Id.
|
|
|
|
messageHandler = m_sessions[sessionId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Otherwise, try to retrieve the message handler
|
|
|
|
// based on the acknowlegded unique identifier.
|
|
|
|
if(m_sessions.contains(message.header.ackUniqueIdentifier)){
|
|
|
|
messageHandler =
|
|
|
|
m_sessions[message.header.ackUniqueIdentifier];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!messageHandler)
|
|
|
|
{
|
|
|
|
// If the message handler still has not been found,
|
|
|
|
// try to retrieve the handler based on the call id.
|
|
|
|
regex = TQRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
|
|
|
|
regex.search(body);
|
|
|
|
TQString callId = regex.cap(1);
|
|
|
|
|
|
|
|
TransferContext *current = 0l;
|
|
|
|
TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
|
|
|
|
for(; it != m_sessions.end(); it++)
|
|
|
|
{
|
|
|
|
current = it.data();
|
|
|
|
if(current->m_callId == callId){
|
|
|
|
messageHandler = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(messageHandler){
|
|
|
|
// Process the received message using the
|
|
|
|
// retrieved registered handler.
|
|
|
|
messageHandler->m_ackSessionIdentifier = message.header.identifier;
|
|
|
|
messageHandler->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
|
|
|
|
messageHandler->processMessage(message);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// There are no objects registered, with the retrieved session Id,
|
|
|
|
// to handle the received message; default to this dispatcher.
|
|
|
|
|
|
|
|
if(message.header.totalDataSize > message.header.dataOffset + message.header.dataSize)
|
|
|
|
{
|
|
|
|
// The entire message has not been received;
|
|
|
|
// buffer the recevied portion of the original message.
|
|
|
|
kdDebug(14140) << k_funcinfo
|
|
|
|
<< TQString("Buffering messsage, %1").arg(message.header.identifier)
|
|
|
|
<< endl;
|
|
|
|
m_messageBuffer.insert(message.header.identifier, message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString body =
|
|
|
|
TQCString(message.body.data(), message.header.dataSize);
|
|
|
|
kdDebug(14140) << k_funcinfo << "received, " << body << endl;
|
|
|
|
|
|
|
|
if(body.startsWith("INVITE"))
|
|
|
|
{
|
|
|
|
// Retrieve the branch, call id, and session id.
|
|
|
|
// These fields will be used later on in the p2p
|
|
|
|
// transaction.
|
|
|
|
TQRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
|
|
|
|
regex.search(body);
|
|
|
|
TQString branch = regex.cap(1);
|
|
|
|
regex = TQRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
|
|
|
|
regex.search(body);
|
|
|
|
TQString callId = regex.cap(1);
|
|
|
|
regex = TQRegExp("SessionID: ([0-9]*)\r\n");
|
|
|
|
regex.search(body);
|
|
|
|
TQString sessionId = regex.cap(1);
|
|
|
|
// Retrieve the contact that requested the session.
|
|
|
|
regex = TQRegExp("From: <msnmsgr:([^>]*)>");
|
|
|
|
regex.search(body);
|
|
|
|
TQString from = regex.cap(1);
|
|
|
|
// Retrieve the application identifier which
|
|
|
|
// is used to determine what type of session
|
|
|
|
// is being requested.
|
|
|
|
regex = TQRegExp("AppID: ([0-9]*)\r\n");
|
|
|
|
regex.search(body);
|
|
|
|
TQ_UINT32 applicationId = regex.cap(1).toUInt();
|
|
|
|
|
|
|
|
if(applicationId == 1 || applicationId == 11 || applicationId == 12 )
|
|
|
|
{ //the AppID is 12 since Messenger 7.5
|
|
|
|
// A contact has requested a session to download
|
|
|
|
// a display icon (User Display Icon or CustomEmotion).
|
|
|
|
|
|
|
|
regex = TQRegExp("Context: ([0-9a-zA-Z+/=]*)");
|
|
|
|
regex.search(body);
|
|
|
|
TQCString msnobj;
|
|
|
|
|
|
|
|
// Decode the msn object from base64 encoding.
|
|
|
|
KCodecs::base64Decode(regex.cap(1).utf8() , msnobj);
|
|
|
|
kdDebug(14140) << k_funcinfo << "Contact requested, "
|
|
|
|
<< msnobj << endl;
|
|
|
|
|
|
|
|
// Create a new transfer context that will handle
|
|
|
|
// the user display icon transfer.
|
|
|
|
TransferContext *current =
|
|
|
|
new OutgoingTransfer(from, this, sessionId.toUInt());
|
|
|
|
current->m_branch = branch;
|
|
|
|
current->m_callId = callId;
|
|
|
|
current->setType(P2P::UserDisplayIcon);
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId.toUInt(), current);
|
|
|
|
|
|
|
|
// Determine the display icon being requested.
|
|
|
|
TQString fileName = objectList.contains(msnobj)
|
|
|
|
? objectList[msnobj]
|
|
|
|
: m_pictureUrl;
|
|
|
|
TQFile *source = new TQFile(fileName);
|
|
|
|
// Try to open the source file for reading.
|
|
|
|
// If an error occurs, send an internal
|
|
|
|
// error message to the recipient.
|
|
|
|
if(!source->open(IO_ReadOnly))
|
|
|
|
{
|
|
|
|
current->error();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
current->m_file = source;
|
|
|
|
// Acknowledge the session request.
|
|
|
|
current->acknowledge(message);
|
|
|
|
|
|
|
|
current->m_ackSessionIdentifier = message.header.identifier;
|
|
|
|
current->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
|
|
|
|
// Send a 200 OK message to the recipient.
|
|
|
|
TQString content = TQString("SessionID: %1\r\n\r\n").arg(sessionId);
|
|
|
|
current->sendMessage(OK, content);
|
|
|
|
}
|
|
|
|
else if(applicationId == 2)
|
|
|
|
{
|
|
|
|
// A contact has requested a session to
|
|
|
|
// send a file.
|
|
|
|
|
|
|
|
kdDebug(14140) << k_funcinfo << "File transfer invitation." << endl;
|
|
|
|
|
|
|
|
// Create a new transfer context that will handle
|
|
|
|
// the file transfer.
|
|
|
|
TransferContext *transfer =
|
|
|
|
new IncomingTransfer(from, this, sessionId.toUInt());
|
|
|
|
transfer->m_branch = branch;
|
|
|
|
transfer->m_callId = callId;
|
|
|
|
transfer->setType(P2P::File);
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId.toUInt(), transfer);
|
|
|
|
|
|
|
|
regex = TQRegExp("Context: ([0-9a-zA-Z+/=]*)");
|
|
|
|
regex.search(body);
|
|
|
|
TQByteArray context;
|
|
|
|
|
|
|
|
// Decode the file context from base64 encoding.
|
|
|
|
KCodecs::base64Decode(regex.cap(1).utf8(), context);
|
|
|
|
TQDataStream reader(context, IO_ReadOnly);
|
|
|
|
reader.setByteOrder(TQDataStream::LittleEndian);
|
|
|
|
//Retrieve the file info from the context field.
|
|
|
|
// File Size [8..15] Int64
|
|
|
|
reader.device()->at(8);
|
|
|
|
TQ_INT64 fileSize;
|
|
|
|
reader >> fileSize;
|
|
|
|
// Flag [15..18] Int32
|
|
|
|
// 0x00 File transfer with preview data.
|
|
|
|
// 0x01 File transfer without preview data.
|
|
|
|
// 0x02 Background sharing.
|
|
|
|
TQ_INT32 flag;
|
|
|
|
reader >> flag;
|
|
|
|
kdDebug(14140) << flag << endl;
|
|
|
|
// FileName UTF16 (Unicode) [19..539]
|
|
|
|
TQByteArray bytes(520);
|
|
|
|
reader.readRawBytes(bytes.data(), bytes.size());
|
|
|
|
TQTextStream ts(bytes, IO_ReadOnly);
|
|
|
|
ts.setEncoding(TQTextStream::Unicode);
|
|
|
|
TQString fileName;
|
|
|
|
fileName = ts.readLine().utf8();
|
|
|
|
|
|
|
|
emit incomingTransfer(from, fileName, fileSize);
|
|
|
|
|
|
|
|
kdDebug(14140) <<
|
|
|
|
TQString("%1, %2 bytes.").arg(fileName, TQString::number(fileSize))
|
|
|
|
<< endl
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
// Get the contact that is sending the file.
|
|
|
|
Kopete::Contact *contact = getContactByAccountId(from);
|
|
|
|
|
|
|
|
if(contact)
|
|
|
|
{
|
|
|
|
// Acknowledge the file invitation message.
|
|
|
|
transfer->acknowledge(message);
|
|
|
|
|
|
|
|
transfer->m_ackSessionIdentifier = message.header.identifier;
|
|
|
|
transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
|
|
|
|
|
|
|
|
TQObject::connect(Kopete::TransferManager::transferManager(), TQT_SIGNAL(accepted(Kopete::Transfer*, const TQString&)), transfer, TQT_SLOT(slotTransferAccepted(Kopete::Transfer*, const TQString&)));
|
|
|
|
TQObject::connect(Kopete::TransferManager::transferManager(), TQT_SIGNAL(refused(const Kopete::FileTransferInfo&)), transfer, TQT_SLOT(slotTransferRefused(const Kopete::FileTransferInfo&)));
|
|
|
|
|
|
|
|
// Show the file transfer accept/decline dialog.
|
|
|
|
Kopete::TransferManager::transferManager()->askIncomingTransfer(contact, fileName, fileSize, TQString(), sessionId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdWarning(14140) << fileName << " from " << from
|
|
|
|
<< " has failed; could not retrieve contact from contact list."
|
|
|
|
<< endl;
|
|
|
|
transfer->m_ackSessionIdentifier = message.header.identifier;
|
|
|
|
transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
|
|
|
|
transfer->sendMessage(ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(applicationId == 4)
|
|
|
|
{
|
|
|
|
#if MSN_WEBCAM
|
|
|
|
regex = TQRegExp("EUF-GUID: \\{([0-9a-zA-Z\\-]*)\\}");
|
|
|
|
regex.search(body);
|
|
|
|
TQString GUID=regex.cap(1);
|
|
|
|
|
|
|
|
kdDebug(14140) << k_funcinfo << "webcam " << GUID << endl;
|
|
|
|
|
|
|
|
Webcam::Who who;
|
|
|
|
if(GUID=="4BD96FC0-AB17-4425-A14A-439185962DC8")
|
|
|
|
{ //that mean "I want to send MY webcam"
|
|
|
|
who=Webcam::wViewer;
|
|
|
|
}
|
|
|
|
else if(GUID=="1C9AA97E-9C05-4583-A3BD-908A196F1E92")
|
|
|
|
{ //that mean "I want YOU to send YOUR webcam"
|
|
|
|
who=Webcam::wProducer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ //unknown GUID
|
|
|
|
//current->error();
|
|
|
|
kdWarning(14140) << k_funcinfo << "Unknown GUID " << GUID << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TransferContext *current = new P2P::Webcam(who, from, this, sessionId.toUInt());
|
|
|
|
current->m_branch = branch;
|
|
|
|
current->m_callId = callId;
|
|
|
|
|
|
|
|
// Add the transfer to the list.
|
|
|
|
m_sessions.insert(sessionId.toUInt(), current);
|
|
|
|
// Acknowledge the session request.
|
|
|
|
current->acknowledge(message);
|
|
|
|
TQTimer::singleShot(0,current, TQT_SLOT(askIncommingInvitation()) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(message.header.sessionId == 64)
|
|
|
|
{
|
|
|
|
// A contact has sent an inkformat (handwriting) gif.
|
|
|
|
// NOTE The entire message body is UTF16 encoded.
|
|
|
|
TQString body = "";
|
|
|
|
for (TQ_UINT32 i=0; i < message.header.totalDataSize; i++){
|
|
|
|
if (message.body[i] != TQChar('\0')){
|
|
|
|
body += TQChar(message.body[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)");
|
|
|
|
regex.search(body);
|
|
|
|
TQString contentType = regex.cap(1);
|
|
|
|
|
|
|
|
if(contentType == "image/gif")
|
|
|
|
{
|
|
|
|
IncomingTransfer transfer(message.source, this, message.header.sessionId);
|
|
|
|
transfer.acknowledge(message);
|
|
|
|
|
|
|
|
regex = TQRegExp("base64:([0-9a-zA-Z+/=]*)");
|
|
|
|
regex.search(body);
|
|
|
|
TQString base64 = regex.cap(1);
|
|
|
|
TQByteArray image;
|
|
|
|
// Convert from base64 encoding to byte array.
|
|
|
|
KCodecs::base64Decode(base64.utf8(), image);
|
|
|
|
// Create a temporary file to store the image data.
|
|
|
|
KTempFile *ink = new KTempFile(locateLocal("tmp", "inkformatgif-" ), ".gif");
|
|
|
|
ink->setAutoDelete(true);
|
|
|
|
// Save the image data to disk.
|
|
|
|
ink->file()->writeBlock(image);
|
|
|
|
ink->file()->close();
|
|
|
|
displayIconReceived(ink, "inkformatgif");
|
|
|
|
ink = 0l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispatcher::messageAcknowledged(unsigned int correlationId, bool fullReceive)
|
|
|
|
{
|
|
|
|
if(fullReceive)
|
|
|
|
{
|
|
|
|
TransferContext *current = 0l;
|
|
|
|
TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
|
|
|
|
for(; it != m_sessions.end(); it++)
|
|
|
|
{
|
|
|
|
current = it.data();
|
|
|
|
if(current->m_transactionId == correlationId)
|
|
|
|
{
|
|
|
|
// Inform the transfer object of the acknowledge.
|
|
|
|
current->readyWrite();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Kopete::Contact* Dispatcher::getContactByAccountId(const TQString& accountId)
|
|
|
|
{
|
|
|
|
Kopete::Contact *contact = 0l;
|
|
|
|
if(parent())
|
|
|
|
{
|
|
|
|
// Retrieve the contact from the current chat session context.
|
|
|
|
Kopete::ChatSession *session = dynamic_cast<Kopete::ChatSession*>(parent()->parent());
|
|
|
|
if(session)
|
|
|
|
{
|
|
|
|
contact = session->account()->contacts()[accountId];
|
|
|
|
session->setCanBeDeleted(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return contact;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dispatcher::CallbackChannel::CallbackChannel(MSNSwitchBoardSocket *switchboard)
|
|
|
|
{
|
|
|
|
m_switchboard = switchboard;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dispatcher::CallbackChannel::~CallbackChannel()
|
|
|
|
{}
|
|
|
|
|
|
|
|
TQ_UINT32 Dispatcher::CallbackChannel::send(const TQByteArray& stream)
|
|
|
|
{
|
|
|
|
return m_switchboard->sendCommand("MSG", "D", true, stream, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Dispatcher::CallbackChannel* Dispatcher::callbackChannel()
|
|
|
|
{
|
|
|
|
if(m_callbackChannel == 0l){
|
|
|
|
MSNSwitchBoardSocket *callback = dynamic_cast<MSNSwitchBoardSocket *>(parent());
|
|
|
|
if(callback == 0l) return 0l;
|
|
|
|
m_callbackChannel = new Dispatcher::CallbackChannel(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_callbackChannel;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "dispatcher.moc"
|