You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/msn/p2p.cpp

413 lines
10 KiB

/*
p2p.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 "p2p.h"
#include "dispatcher.h"
using P2P::TransferContext;
using P2P::Message;
using P2P::MessageType;
using P2P::TransferType;
#include <stdlib.h>
// Kde includes
#include <kbufferedsocket.h>
#include <kdebug.h>
// Qt includes
#include <tqfile.h>
// Kopete includes
#include <kopetetransfermanager.h>
TQString P2P::Uid::createUid()
{
return (TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ TQString::number(rand()%0xAAFF+0x1111, 16) + "-"
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)).upper();
}
TransferContext::TransferContext(const TQString &contact, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
: TQObject(dispatcher) ,
m_sessionId(sessionId) ,
m_identifier(0) ,
m_file(0) ,
m_transactionId (0),
m_ackSessionIdentifier (0) ,
m_ackUniqueIdentifier ( 0 ),
m_transfer ( 0l) ,
m_baseIdentifier(rand()%0x0FFFFFF0 + 4),
m_dispatcher (dispatcher),
m_isComplete (false) ,
m_offset(0),
m_totalDataSize(0),
m_recipient(contact),
m_sender(dispatcher->localContact()),
m_socket(0),
m_state ( Invitation)
{
m_type = File ; //uh, why??? -Olivier
}
TransferContext::~TransferContext()
{
m_transfer = 0l;
if(m_file){
delete m_file;
m_file = 0l;
}
}
void TransferContext::acknowledge(const Message& message)
{
kdDebug(14140) << k_funcinfo << m_dispatcher<< endl;
Message outbound;
outbound.header.sessionId = message.header.sessionId;
if(m_identifier == 0){
m_identifier = m_baseIdentifier;
}
// else if(m_state == Finished && m_direction == Incoming){
// m_identifier = m_baseIdentifier - 1;
// }
else if(m_state == Finished && m_direction == Outgoing){
m_identifier = m_baseIdentifier + 1;
}
else
++m_identifier;
outbound.header.identifier = m_identifier;
outbound.header.dataOffset = 0l;
outbound.header.totalDataSize = message.header.totalDataSize;
outbound.header.dataSize = 0;
// if(m_type == UserDisplayIcon && m_state == Finished){
// if(m_direction == Outgoing){
// outbound.header.flag = 0x40;
// }
// }
// else
outbound.header.flag = 2;
outbound.header.ackSessionIdentifier = message.header.identifier;
outbound.header.ackUniqueIdentifier = message.header.ackSessionIdentifier;
outbound.header.ackDataSize = message.header.totalDataSize;
// NOTE outbound.body is null by default
outbound.applicationIdentifier = 0l;
outbound.destination = m_recipient;
TQByteArray stream;
// Write the acknowledge message to the stream.
m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
if(!m_socket)
{
// Send the acknowledge message.
m_dispatcher->callbackChannel()->send(stream);
}
else
{
// Send acknowledge message directly.
m_socket->writeBlock(stream.data(), stream.size());
}
}
void TransferContext::error()
{
kdDebug(14140) << k_funcinfo << endl;
sendMessage(ERROR);
m_dispatcher->detach(this);
}
void TransferContext::sendData(const TQByteArray& bytes)
{
Message outbound;
outbound.header.sessionId = m_sessionId;
outbound.header.identifier = m_identifier;
outbound.header.dataOffset = m_offset;
if(m_file){
outbound.header.totalDataSize = m_file->size();
}
else
outbound.header.totalDataSize = m_totalDataSize;
outbound.header.dataSize = bytes.size();
if(m_type == UserDisplayIcon){
outbound.header.flag = 0x20;
}
else if(m_type == P2P::File){
outbound.header.flag = 0x01000030;
}
else outbound.header.flag = 0;
outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4;
outbound.header.ackUniqueIdentifier = 0;
outbound.header.ackDataSize = 0l;
outbound.body = bytes;
outbound.applicationIdentifier = (uint)m_type;
outbound.destination = m_recipient;
TQByteArray stream;
m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
if(!m_socket)
{
// Send the data message.
m_transactionId = m_dispatcher->callbackChannel()->send(stream);
}
else
{
// Send data directly.
m_socket->writeBlock(stream.data(), stream.size());
}
}
void TransferContext::sendDataPreparation()
{
kdDebug(14140) << k_funcinfo << endl;
Message outbound;
outbound.header.sessionId = m_sessionId;
outbound.header.identifier = ++m_identifier;
outbound.header.dataOffset = 0;
outbound.header.totalDataSize = 4;
outbound.header.dataSize = 4;
outbound.header.flag = 0;
outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4;
outbound.header.ackUniqueIdentifier = 0;
outbound.header.ackDataSize = 0l;
TQByteArray bytes(4);
bytes.fill('\0');
outbound.body = bytes;
outbound.applicationIdentifier = 1;
outbound.destination = m_recipient;
TQByteArray stream;
m_messageFormatter.writeMessage(outbound, stream);
// Send the receiving client the data prepartion message.
m_dispatcher->callbackChannel()->send(stream);
}
void TransferContext::sendMessage(MessageType type, const TQString& content, Q_INT32 flag, Q_INT32 appId)
{
Message outbound;
if(appId != 0){
outbound.header.sessionId = m_sessionId;
}
else
outbound.header.sessionId = 0;
if(m_identifier == 0){
m_identifier = m_baseIdentifier;
}
else if(m_state == Invitation && m_direction == P2P::Outgoing && m_type == UserDisplayIcon)
{
m_identifier -= 3;
}
else if(m_state == Invitation && m_direction == P2P::Incoming && m_type == File)
{
m_identifier -= 3;
}
else
++m_identifier;
outbound.header.identifier = m_identifier;
outbound.header.flag = flag;
outbound.header.ackSessionIdentifier = m_ackSessionIdentifier;
outbound.header.ackUniqueIdentifier = m_ackUniqueIdentifier;
outbound.header.ackDataSize = 0l;
outbound.applicationIdentifier = appId;
outbound.destination = m_recipient;
TQString contentType, cSeq, method;
switch(m_state)
{
case DataTransfer:
contentType = "application/x-msnmsgr-transreqbody";
if(m_type == File && m_direction == Incoming)
{
contentType = "application/x-msnmsgr-transrespbody";
}
break;
case Finished:
contentType = "application/x-msnmsgr-sessionclosebody";
break;
default:
contentType = "application/x-msnmsgr-sessionreqbody";
if(m_type == File && m_direction == Outgoing)
{
if(m_state == Negotiation){
contentType = "application/x-msnmsgr-transreqbody";
}
}
if(m_type == P2P::WebcamType && type==P2P::INVITE && m_state == Negotiation)
{
contentType = "application/x-msnmsgr-transreqbody";
}
break;
}
switch(type)
{
case BYE:
method = "BYE MSNMSGR:" + m_recipient + " MSNSLP/1.0";
cSeq = "0";
break;
case DECLINE:
method = "MSNSLP/1.0 603 DECLINE";
cSeq = "1";
break;
case ERROR:
contentType = "null";
method = "MSNSLP/1.0 500 Internal Error";
cSeq = "1";
break;
case INVITE:
method = "INVITE MSNMSGR:" + m_recipient + " MSNSLP/1.0";
cSeq = "0";
break;
case OK:
method = "MSNSLP/1.0 200 OK";
cSeq = "1";
break;
}
TQCString body = TQString(method + "\r\n"
"To: <msnmsgr:" + m_recipient + ">\r\n"
"From: <msnmsgr:" + m_sender + ">\r\n"
"Via: MSNSLP/1.0/TLP ;branch={" + m_branch.upper() + "}\r\n"
"CSeq: "+ cSeq +"\r\n"
"Call-ID: {" + m_callId.upper() + "}\r\n"
"Max-Forwards: 0\r\n"
"Content-Type: " + contentType + "\r\n"
"Content-Length: "+ TQString::number(content.length() + 1) + "\r\n"
"\r\n" +
content).utf8();
// NOTE The body must have a null character at the end.
// TQCString by chance automatically adds a \0 to the
// end of the string.
outbound.header.totalDataSize = body.size();
// Send the outbound message.
sendMessage(outbound, body);
}
void TransferContext::sendMessage(Message& outbound, const TQByteArray& body)
{
Q_INT64 offset = 0L, bytesLeft = outbound.header.totalDataSize;
Q_INT16 chunkLength = 1202;
// Split the outbound message if necessary.
while(bytesLeft > 0L)
{
if(bytesLeft < chunkLength)
{
// Copy the last chunk of the multipart message.
outbound.body.duplicate(body.data() + offset, bytesLeft);
outbound.header.dataSize = bytesLeft;
outbound.header.dataOffset = offset;
bytesLeft = 0L;
}
else
{
// Copy the next chunk of the multipart message in the sequence.
outbound.body.duplicate(body.data() + offset, chunkLength);
outbound.header.dataSize = chunkLength;
outbound.header.dataOffset = offset;
offset += chunkLength;
bytesLeft -= offset;
}
kdDebug(14140) << k_funcinfo <<
TQCString(outbound.body.data(), outbound.body.size())
<< endl;
TQByteArray stream;
// Write the outbound message to the stream.
m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
if(!m_socket)
{
// Send the outbound message.
m_dispatcher->callbackChannel()->send(stream);
}
else
{
// Send outbound message directly.
m_socket->writeBlock(stream.data(), stream.size());
}
}
}
void TransferContext::setType(TransferType type)
{
m_type = type;
}
void TransferContext::abort()
{
kdDebug(14140) << k_funcinfo << endl;
if(m_transfer)
{
if(m_transfer->error() == KIO::ERR_ABORTED)
{
switch(m_direction)
{
case P2P::Outgoing:
if(m_type == File)
{
// Do nothing.
}
break;
case P2P::Incoming:
if(m_type == File)
{
// Do nothing.
}
break;
}
}
else
{
m_state = Finished;
sendMessage(BYE, "\r\n");
}
}
}
void TransferContext::readyWrite()
{
if(m_direction == Outgoing && m_state == DataTransfer){
readyToSend();
}
}
void TransferContext::readyToSend()
{}
#include "p2p.moc"