diff options
Diffstat (limited to 'kopete/protocols/msn/p2p.cpp')
-rw-r--r-- | kopete/protocols/msn/p2p.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/kopete/protocols/msn/p2p.cpp b/kopete/protocols/msn/p2p.cpp new file mode 100644 index 00000000..219fd935 --- /dev/null +++ b/kopete/protocols/msn/p2p.cpp @@ -0,0 +1,412 @@ +/* + 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 <qfile.h> + +// Kopete includes +#include <kopetetransfermanager.h> + +QString P2P::Uid::createUid() +{ + return (QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + QString::number(rand()%0xAAFF+0x1111, 16) + "-" + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)).upper(); +} + +TransferContext::TransferContext(const QString &contact, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId) + : QObject(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; + + QByteArray 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 QByteArray& 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; + + QByteArray 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; + QByteArray bytes(4); + bytes.fill('\0'); + outbound.body = bytes; + outbound.applicationIdentifier = 1; + outbound.destination = m_recipient; + + QByteArray 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 QString& 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; + + QString 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; + } + + QCString body = QString(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: "+ QString::number(content.length() + 1) + "\r\n" + "\r\n" + + content).utf8(); + + // NOTE The body must have a null character at the end. + // QCString 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 QByteArray& 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 << + QCString(outbound.body.data(), outbound.body.size()) + << endl; + + QByteArray 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" |