Logo Search packages:      
Sourcecode: kmess version File versions  Download package

p2pmessage.cpp

/***************************************************************************
 *                                                                         *
 *   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 "p2pmessage.h"

#include "../kmessdebug.h"

#include <qcstring.h>


/*

// Constructor to create an empty P2P message
P2PMessage::P2PMessage()
  : sessionID_(0)
  , messageID_(0)
  , dataOffset_(0)
  , dataSize_(0)
  , flags_(0)
  , totalSize_(0)
  , ackSessionID_(0)
  , ackUniqueID_(0)
  , ackDataSize_(0)
{

}

*/


// Build a P2P message based on the received binary data.
00038 P2PMessage::P2PMessage(const char *data, uint size)
{
  // Copy the binary data to our own managed copy
  message_.duplicate(data, size);

#ifdef KMESSTEST
  ASSERT(message_.size() == size);
#endif

  parseP2PHeader();
}



// Build a P2P message based on the received binary data.
P2PMessage::P2PMessage(const QByteArray &message)
  : message_(message)
{
  parseP2PHeader();
}



// Retreives the current session ID.
unsigned long P2PMessage::getSessionID() const
{
  return sessionID_;
}

// Retreives the current message ID. */
unsigned long P2PMessage::getMessageID() const
{
  return messageID_;
}

// Retreives the current message offset.
unsigned long P2PMessage::getDataOffset() const
{
  return dataOffset_;
}

// Retreives the current message size.
unsigned long P2PMessage::getDataSize() const
{
  return dataSize_;
}

// Retreives the total message size.
unsigned long P2PMessage::getTotalSize() const
{
  return totalSize_;
}

// Retreives the current message size.
unsigned long P2PMessage::getFlags() const
{
  return flags_;
}

// Retrieves the ack session ID field.
unsigned long P2PMessage::getAckSessionID() const
{
  return ackSessionID_;
}

// Retreives the ack unique ID field.
unsigned long P2PMessage::getAckUniqueID() const
{
  return ackUniqueID_;
}

// Retreives the ack data size field.
unsigned long P2PMessage::getAckDataSize() const
{
  return ackDataSize_;
}

// Retreives the nonce field (for direct connection handshake.
QString P2PMessage::getNonce() const
{
#ifdef KMESSTEST
  ASSERT( isConnectionHandshake() );
#endif
  return extractNonce(message_.data());
}



// Return a pointer to the data.
const char *P2PMessage::getData() const
{
  return (message_.data() + 48);
}



// Negative ack message (0x01)
bool P2PMessage::isNegativeAck() const
{
  return (flags_ & MSN_FLAG_NEGATIVE_ACK) == MSN_FLAG_NEGATIVE_ACK;
}


// The message is an ACK (0x02)
bool P2PMessage::isAck() const
{
  return (flags_ & MSN_FLAG_ACK) == MSN_FLAG_ACK;
}



// Waiting for ack (0x06)
bool P2PMessage::isWaitingForAck() const
{
  return (flags_ & MSN_FLAG_WAITING_FOR_ACK) == MSN_FLAG_WAITING_FOR_ACK;
}



// Waiting for reply (0x04)
bool P2PMessage::isWaitingForReply() const
{
  return (flags_ & MSN_FLAG_WAITING) == MSN_FLAG_WAITING;
}



// Return true if the error flag was set (0x80)
bool P2PMessage::isError() const
{
  return (flags_ & MSN_FLAG_ERROR) == MSN_FLAG_ERROR;
}


// Return true if this is MsnObject data (e.g. pictures) (0x20)
bool P2PMessage::isMsnObjectData() const
{
  // Used the MSN_FLAG_FILE_DATA mask here on purpose,
  // I want to be sure only the object data flag is set here.
  return (flags_ & MSN_FLAG_FILE_DATA) == MSN_FLAG_OBJECT_DATA;
}



// Returns true if the sender aborted it's own data transfer (0x40)
bool P2PMessage::isAbortedSendingAck() const
{
  return (flags_ & MSN_FLAG_ABORTED_SENDING) == MSN_FLAG_ABORTED_SENDING;
}



// Returns true if the receiver aborted the data transfer (0x80)
bool P2PMessage::isAbortedReceivingAck() const
{
  return (flags_ & MSN_FLAG_ABORTED_RECEIVING) == MSN_FLAG_ABORTED_RECEIVING;
}



// Returns true if the message is a direct-connection handshake message (0x100)
bool P2PMessage::isConnectionHandshake() const
{
  return (flags_ & MSN_FLAG_DC_HANDSHAKE) == MSN_FLAG_DC_HANDSHAKE;
}



// Set if the message contains file data (0x1000030)
bool P2PMessage::isFileData() const
{
  return (flags_ & MSN_FLAG_FILE_DATA) == MSN_FLAG_FILE_DATA;
}



// Return true if this is an fragment (splitted message packet).
bool P2PMessage::isFragment() const
{
  return (dataSize_ < totalSize_);
}



// Returns true if the message size exceeds the total size
bool P2PMessage::isLastFragment() const
{
  // offset + this message size >= the total size.
  return (dataOffset_ + dataSize_) >= totalSize_;
}



// Parse the binary P2P header bytes
void P2PMessage::parseP2PHeader()
{
  char *messageData = message_.data();

  if(! messageData || message_.size() < 48)
  {
    // bodyData may be NULL
    kdWarning() << "P2PMessage: no data found to parse!" << endl;
    return;
  }

  // Header:
  //
  // 0    4    8        16        24   28   32   36   40        48
  // |....|....|....|....|....|....|....|....|....|....|....|....|
  // |sid |mid |offset   |totalsize|size|flag|asid|auid|a-datasz |
  //
  // More info can be found at http://siebe.bot2k3.net/docs/

  sessionID_       = extractBytes     ( messageData,  0);
  messageID_       = extractBytes     ( messageData,  4);
  dataOffset_      = extractLongBytes ( messageData,  8);
  totalSize_       = extractLongBytes ( messageData, 16);
  dataSize_        = extractBytes     ( messageData, 24);
  flags_           = extractBytes     ( messageData, 28);
  ackSessionID_    = extractBytes     ( messageData, 32);
  ackUniqueID_     = extractBytes     ( messageData, 36);
  ackDataSize_     = extractLongBytes ( messageData, 40);
}





// ------------------------------------------------------
// A utility to fill the byte data block

void P2PMessage::insertBytes(QByteArray &buffer, const unsigned int value, const int offset)
{
  // Also based on Kopete code. I started with a nice struct,
  // but not all c++ compilers use the same data size for the fields:
  /*
  * "The only restrictions that the language spec places are that
  *  (1) a char is one byte,
  *  (2) char <= short int <= int <= long int <= long long int.
  *  The exact size is completely compiler dependent."
  *
   * gcc on i686-pc-linux-gnu gives these results:
  * - sizeof(int)           = 4
  * - sizeof(long int)      = 4
  * - sizeof(long long int) = 8
  * this beats me.
  *
  * In other words, I'll use bitwise shifts to set bytes manually.
  */

  buffer[offset + 0] = (char) ( value        & 0xFF);
  buffer[offset + 1] = (char) ((value >>  8) & 0xFF);
  buffer[offset + 2] = (char) ((value >> 16) & 0xFF);
  buffer[offset + 3] = (char) ((value >> 24) & 0xFF);
  // I think it's obvious we're copying every byte individually here..
  // However, note the bytes are placed in network order. (big endian)
}

void P2PMessage::insertShortBytes(QByteArray &buffer, const unsigned short value, const int offset)
{
  buffer[offset + 0] = (char) ( value        & 0xFF);
  buffer[offset + 1] = (char) ((value >>  8) & 0xFF);
}

void P2PMessage::insertNonce(QByteArray &buffer, const QString &nonce, const int offset)
{
  // Remove the separators
  // The QString(..) call makes a copy because QString::remove isn't const.
  QString fixedNonce = QString(nonce).remove('-').remove('{').remove('}');

#ifdef KMESSTEST
  ASSERT( fixedNonce.length() == 32      );  // 32 hex codes will be saved as 16 bytes.
  ASSERT( buffer.size() >= (uint) (offset + 16) );  // buffer needs to have 16 bytes of space left.
#endif

  // fields 7-9 (ack-msgid/uniqueid/ack-size) will be overwritten with the nonce.
  // When the nonce was {4299E384-8248-4AF6-90E4-5B26A040AC34} as string
  // it will be sent as bytes like: 84E39942-4882-F64A-90E4-5B26A040AC34
  // The order of the first 3 fields is reversed.
  const int noncePos[] = { 3,2,1,0            // Field 1 reversed
                         , 5,4                // Field 2 reversed
                         , 7,6                // Field 3 reversed
                         , 8,9                // field 4
                         , 10,11,12,13,14,15  // field 5
                         };
  for(int i = 0; i < 16; i++)
  {
    buffer[offset + i] = fixedNonce.mid(noncePos[i] * 2, 2).toUInt(0, 16);
  }
}

unsigned int P2PMessage::extractBytes(const char *data, const int offset)
{
  // Convert the bytes from network order to a normal int.
  return (((unsigned char) data[offset + 0]      )
        | ((unsigned char) data[offset + 1] <<  8)
        | ((unsigned char) data[offset + 2] << 16)
        | ((unsigned char) data[offset + 3] << 24));
}

unsigned long P2PMessage::extractLongBytes(const char *data, const int offset)
{
  // Convert the bytes from network order to a long int.
  return (((unsigned char) data[offset + 0]      )
        | ((unsigned char) data[offset + 1] <<  8)
        | ((unsigned char) data[offset + 2] << 16)
        | ((unsigned char) data[offset + 3] << 24));

        // FIXME: gcc complains that the shift width is larger then the actual data type supports..
        //        as long as the msn servers don't send huge messages, we don't have much to worry..
        //        otherwise cast the data to Q_INT64 first.
//        | ((unsigned char) data[offset + 4] << 32)
//        | ((unsigned char) data[offset + 5] << 40)
//        | ((unsigned char) data[offset + 6] << 48)
//        | ((unsigned char) data[offset + 7] << 56));
}


QString P2PMessage::extractNonce(const char *data, const int offset)
{
  const int noncePos[] = { 3,2,1,0            // Field 1 reversed
                         , 5,4                // Field 2 reversed
                         , 7,6                // Field 3 reversed
                         , 8,9                // field 4
                         , 10,11,12,13,14,15  // field 5
                         };

  const char hexMap[] = "0123456789ABCDEF";
  QString hex;
  for(int i = 0; i < 16; i++)
  {
    if( i == 4 || i == 6 || i == 8 || i == 10 )
    {
      hex += "-";
    }
    int upper = (data[offset + noncePos[i]] & 0xf0) >> 4;
    int lower = (data[offset + noncePos[i]] & 0x0f);
    hex += hexMap[upper];
    hex += hexMap[lower];
  }

  return "{" + hex + "}";
}


Generated by  Doxygen 1.6.0   Back to index