/* p2p.cpp - msn p2p protocol Copyright (c) 2003-2005 by Olivier Goffart Copyright (c) 2005 by Gregg Edghill ************************************************************************* * * * 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 // Kde includes #include #include // TQt includes #include // Kopete includes #include 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, TQ_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, TQ_INT32 flag, TQ_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: \r\n" "From: \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) { TQ_INT64 offset = 0L, bytesLeft = outbound.header.totalDataSize; TQ_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() == TDEIO::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"