Logo Search packages:      
Sourcecode: kmess version File versions

msndirectconnection.cpp

/***************************************************************************
                          msndirectconnection.cpp -  description
                             -------------------
    begin                : Tue 12 27 2005
    copyright            : (C) 2005 by Diederik van der Boor
    email                : "vdboor" --at-- "codingdomain.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 "msndirectconnection.h"

#include "../../kmessdebug.h"
#include "../p2pmessage.h"

#include <qcstring.h>

#ifdef KMESSDEBUG_DIRECTCONNECTION
  #define KMESSDEBUG_DIRECTCONNECTION_GENERAL
  // note that enabling the KMESSDEBUG_DIRECTCONNECTION_RECEIVING makes file transfer slow and inresponsive.
  #define KMESSDEBUG_DIRECTCONNECTION_RECEIVING
#endif



MsnDirectConnection::MsnDirectConnection(const QString &contactHandle)
 : DirectConnectionBase(0, "MsnDirectConnection")
 , bufferOffset_(0)
 , contactHandle_(contactHandle)
 , firstMessage_(true)
 , remainingBlockBytes_(0)
{
}


MsnDirectConnection::~MsnDirectConnection()
{
}



// Return the contact handle
const QString& MsnDirectConnection::getContactHandle() const
{
  return contactHandle_;
}



// Send the handshake packets
bool MsnDirectConnection::initialize()
{
  // When this is the server, nothing needs to be sent.
  if(isServer())
  {
    return isConnected();
  }

#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "MsnDirectConnection: sending preamble message (0x666f6f00)." << endl;
#endif
#ifdef KMESS_NETWORK_WINDOW
  KMESS_NET_INIT(this, "DC " + getRemoteIp());
#endif
  firstMessage_ = false;

  // Send the preamble packet
  // This consists of 8 bytes:
  // the length field followed by "foo\0"
  QByteArray preambleMessage(8);
  P2PMessage::insertBytes( preambleMessage, 4, 0 );
  preambleMessage[4] = 0x66;  // f
  preambleMessage[5] = 0x6f;  // 0
  preambleMessage[6] = 0x6f;  // 0
  preambleMessage[7] = 0x00;  // \0
  return writeBlock( preambleMessage.data(), preambleMessage.size() );
}



// Send the message to the contact
bool MsnDirectConnection::sendMessage(const QByteArray &message)
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "MsnDirectConnection: sending message block (size=" << message.size() << ")" << endl;
#endif
#ifdef KMESSTEST
  ASSERT( message.size() <= 1352 );  // max message size for official client
#endif

  // KMESS_NET_SENT is called by the base class already.

  // Send the message size in advance
  QByteArray lengthField(4);
  P2PMessage::insertBytes( lengthField, message.size(), 0 );
  writeBlock( lengthField );

  // Send the block
  return writeBlock( message );
}



// This is called when data is received from the socket.
void MsnDirectConnection::slotDataReceived()
{
  // Make sure we read all available bytes from the socket before returning
  do   // while(getBytesAvailable() > 0);
  {

    // If no block was started
    if( remainingBlockBytes_ <= 0 )
    {
      // Start with a new data block
      // First 4 bytes indicate the message/block length.
      QByteArray preamble(4);
      int noBytesRead = readBlock(preamble, 4);
      if( noBytesRead != 4 )
      {
        // read error
        kdWarning() << "MsnDirectConnection::slotDataReceived: read error on socket." << endl;
        closeConnection();
        return;
      }

      // Add the message to the network window.
#ifdef KMESS_NETWORK_WINDOW
      if( firstMessage_ )
        KMESS_NET_INIT(this, "DC " + getRemoteIp());
      KMESS_NET_RECEIVED( this, preamble );
#endif
      firstMessage_ = false;

      // Convert the received bytes to a uint
      remainingBlockBytes_ = P2PMessage::extractBytes( preamble, 0 );
#ifdef KMESSDEBUG_DIRECTCONNECTION_RECEIVING
      kdDebug() << "MsnDirectConnection: 4 bytes read, next block size is " << remainingBlockBytes_ << "." << endl;
#endif

      // Prevent other clients from DOS-ing us by forcing a large allocation
      // Official client only allows up to 1352 bytes.
      if( remainingBlockBytes_ > 4000 )
      {
        kdWarning() << "MsnDirectConnection: received unexpected large block length"
                    << " of " << remainingBlockBytes_ << " bytes, aborting transfer!" << endl;
        closeConnection();
        return;
      }

      // Allocate the buffer.
      buffer_.resize(remainingBlockBytes_);
      buffer_.fill('\0');
      bufferOffset_ = 0;
    }


    // If there is a block waiting
    // Also check whether there are still bytes to read.
    // The length field is sent in a separate packet.
    if(remainingBlockBytes_ > 0 && getBytesAvailable() > 0)
    {
      // Fill the buffer with the bytes received. Reads as many bytes as possible.
      int noBytesRead = readBlock( buffer_, remainingBlockBytes_, bufferOffset_ );

      // Handle read errors
      if(noBytesRead <= 0)
      {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
        kdWarning() << "MsnDirectConnection: read error (returned " << noBytesRead << " block size=" << buffer_.size() << ")" << endl;
#endif
        // Socket error, exit
        closeConnection();
        return;
      }

#ifdef KMESS_NETWORK_WINDOW
      KMESS_NET_RECEIVED(this, buffer_);
#endif

      // Update offsets
      bufferOffset_        += noBytesRead;
      remainingBlockBytes_ -= noBytesRead;

#ifdef KMESSDEBUG_DIRECTCONNECTION_RECEIVING
      kdDebug() << "MsnDirectConnection::slotDataReceived: " << noBytesRead << " bytes read, "
                                                             << remainingBlockBytes_ << " bytes remaining." << endl;
#endif
#ifdef KMESSTEST
      ASSERT( (bufferOffset_ + remainingBlockBytes_) == buffer_.size() );
#endif

      // Signal when we've received the while block
      if( remainingBlockBytes_ <= 0 )
      {
        // Find out whether it's a preamble packet or normal message.
        if( buffer_.size() == 4 )
        {
          // This should be a preamble packet from the other client.
          if( QCString(buffer_.data(), 4) == "foo" )
          {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
            kdDebug() << "MsnDirectConnection::slotDataReceived: received preamble packet, ignoring." << endl;
#endif
          }
          else
          {
            kdWarning() << "MsnDirectConnection::slotDataReceived: received unexpected preamble packet"
                        << " (data=" << buffer_.data() << "), ignoring." << endl;
          }
        }
        else
        {
          // A normal message was received
          emit messageReceived( buffer_ );
        }
      }
    }
  }
  while(getBytesAvailable() > 0);
}



#include "msndirectconnection.moc"

Generated by  Doxygen 1.6.0   Back to index