Logo Search packages:      
Sourcecode: kmess version File versions

void P2PApplicationBase::gotMessage ( const P2PMessage p2pMessage  )  [inherited]

Main entry function for incoming incoming messages.

This method receives the incoming P2P messages, analyses it's flags and delivers it to various handling methods:

Other P2P messages are rejected with an error message.

Parameters:
p2pMessage A reference to a P2PMessage object.

Definition at line 650 of file p2papplicationbase.cpp.

References P2PApplicationBase::UnAckedMessage::ackSessionID, Application::APP_MODE_DATACAST, Application::APP_MODE_ERROR_HANDLER, P2PApplicationBase::UnAckedMessage::dataSize, Application::endApplication(), P2PApplicationBase::endApplicationLater(), P2PApplicationBase::UnAckedMessage::footerCode, P2PMessage::getAckSessionID(), Application::getContactHandle(), P2PMessage::getData(), P2PMessage::getDataSize(), P2PMessage::getFlags(), P2PFragmentTracker::getMessageID(), P2PMessage::getMessageID(), Application::getMode(), P2PMessage::getSessionID(), P2PMessage::getTotalSize(), P2PApplicationBase::getUnAckedMessage(), P2PApplicationBase::gotDataFragment(), P2PApplicationBase::gotErrorAck(), P2PApplicationBase::gotNegotiationFragment(), P2PApplicationBase::gotTimeoutAck(), P2PApplicationBase::gotTransferAbortedAck(), P2PMessage::isAbortedReceivingAck(), P2PMessage::isAbortedSendingAck(), P2PMessage::isAck(), P2PMessage::isConnectionHandshake(), P2PMessage::isData(), P2PMessage::isDataPreparation(), P2PMessage::isError(), P2PMessage::isFragment(), P2PMessage::isLastFragment(), P2PMessage::isNegativeAck(), P2PApplicationBase::isP2PAckSent(), P2PMessage::isSlpData(), Application::isUserStartedApp(), P2PMessage::isWaitingForAck(), P2PMessage::isWaitingForReply(), P2PApplicationBase::UnAckedMessage::messageID, P2PApplicationBase::UnAckedMessage::messageType, P2PApplicationBase::P2P_MSG_DATA, P2PApplicationBase::P2P_MSG_DATA_PREPARATION, P2PApplicationBase::P2P_MSG_UNKNOWN, P2PApplicationBase::P2P_MSG_WEBCAM_SETUP, P2PApplicationBase::P2P_WAIT_END_APPLICATION, P2PApplicationBase::P2P_WAIT_FOR_FILE_DATA, P2PApplicationBase::sendP2PAbort(), P2PApplicationBase::UnAckedMessage::sentTime, P2PApplicationBase::UnAckedMessage::sessionID, Application::setClosing(), Application::showEventMessage(), and P2PApplicationBase::UnAckedMessage::totalSize.

Referenced by ApplicationList::gotMessage(), and ApplicationList::slotGotDirectConnectionMessage().

{
  // Reset session variables.
  waitingTimer_->stop();   // stop timer

#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
  if( getMode() == APP_MODE_DATACAST )
  {
    // Special case, abuse of P2P system
    kDebug() << "P2P message from " << getContactHandle() << " is handled by a datacast session.";
  }
  else if( nextMessageID_ == 0 )
  {
    // No messages sent, new application
    kDebug() << "P2P message from " << getContactHandle() << " is handled by a newly created session.";
  }
  else
  {
    // s/found/received/ since ApplicationList::gotMessage() finds it, but this makes it easier to filter the logs.
    kDebug() << "New P2P message from " << getContactHandle() << " is handled by session " << getSessionID() << ".";
  }
#endif

  // Cancel termination if a new message is received
  // while the application was already scheduelled to be closed.
  // Ignore typical messages which are sent while a transfer is aborting.
  if( waitingState_ == P2P_WAIT_END_APPLICATION
  &&  ! p2pMessage.isError()
  &&  ! p2pMessage.isAbortedReceivingAck()
  &&  ! p2pMessage.isAbortedSendingAck() )
  {
    kWarning() << "received another message while awaiting termination "
                  "(flags=0x" << QString::number( p2pMessage.getFlags(), 16 ) <<
                  " size="    << p2pMessage.getDataSize() <<
                  " contact=" << getContactHandle() <<
                  " session=" << getSessionID() <<
                  " class="   << metaObject()->className() <<
                  " action=unsetclosing).";

    // Don't reset the waiting state. should happen by next method which responds to the message.
    // It could be a SLP BYE for example or "481 No Such Call".
    userAborted_ = false;
    setClosing( false );
  }


  // If this object was initialized to handle a Bad packet.
  if( getMode() == APP_MODE_ERROR_HANDLER )
  {
    // Make an exception for SLP packets
    bool isSlpMessage = ( p2pMessage.isSlpData() &&
                          p2pMessage.getDataSize() > 20  );   // for cvs produced data-preparation messages without a session-id
    if(! isSlpMessage)
    {
      // We received an unknown control message, this object was initialized to send the correct response.
      // Inform the user
      showEventMessage( i18n("The invitation was cancelled. The contact sent bad data, or KMess does not support it."), ChatMessage::CONTENT_APP_CANCELED, ! isUserStartedApp() );

      // Abort
      sendP2PAbort();
      endApplication();
      return;
    }
  }


  // Handle ACKs before everything else.
  if( p2pMessage.getDataSize() == 0 )  // HACK: for Bot2K3 4.1: no testing for flags
  {
    UnAckedMessage *unAcked         = getUnAckedMessage(p2pMessage);
    P2PMessageType ackedMessageType = P2P_MSG_UNKNOWN;
    if( unAcked == 0 )
    {
      if( p2pMessage.isAck() )  // other messages can't relate have the control flags set.
      {
        kWarning() << "Unable to handle ACK message, "
                      "original message not found "
                      "(flags=0x" << QString::number(p2pMessage.getFlags(), 16) <<
                      " size=0 state=" << waitingState_ <<
                      " contact=" << getContactHandle() <<
                      " session=" << getSessionID() <<
                      " class="   << metaObject()->className() <<
                      " action=return).";
        return;
      }
    }
    else
    {
      // Remove ack record
      // Also avoids that hasUnAckedMessage() returns the message we just received.
      ackedMessageType = unAcked->messageType;
      outgoingMessages_.removeAll( unAcked );

#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
      kDebug() << "Removed ack slot for message "
               << "(sid="     << unAcked->sessionID
               << " mid="     << unAcked->messageID
               << " ackSid="  << unAcked->ackSessionID
               << " size="    << unAcked->dataSize
               << " type="    << unAcked->messageType
               << ")";
#endif

      delete unAcked;
    }


    if( p2pMessage.isAck() )  // 0x02
    {
      gotAck( p2pMessage, ackedMessageType );
    }
    else if( p2pMessage.isConnectionHandshake() ) // 0x100
    {
      gotDirectConnectionHandshake( p2pMessage );
    }
    else if( p2pMessage.isNegativeAck() )  // 0x01
    {
      gotErrorAck( p2pMessage );
    }
    else if( p2pMessage.isWaitingForReply() )  // 0x04
    {
      gotTimeoutAck( p2pMessage );
    }
    else if( p2pMessage.isWaitingForAck() )  // 0x06
    {
      gotTimeoutAck( p2pMessage );
    }
    else if( p2pMessage.isError() )  // 0x08
    {
      gotErrorAck( p2pMessage );
    }
    else if( p2pMessage.isAbortedSendingAck() ) // 0x40
    {
      gotTransferAbortedAck( p2pMessage );
    }
    else if( p2pMessage.isAbortedReceivingAck() ) // 0x80
    {
      gotTransferAbortedAck( p2pMessage );
    }
    else
    {
      kWarning() << "Unable to handle ACK message, "
                    "unknown flag value encountered "
                    "(flags=0x" << QString::number(p2pMessage.getFlags(), 16) <<
                    " size=0 state=" << waitingState_ <<
                    " contact=" << getContactHandle() <<
                    " session=" << getSessionID() <<
                    " class="   << metaObject()->className() <<
                    " action=assumerror).";

      // Make sure the application does abort somehow.
      gotErrorAck( p2pMessage );
    }

    return;
  }


  // Send the ack if needed (also applies to INVITE messages)
  shouldSendAck_ = p2pMessage.isLastFragment();

  // Store some message parameters for Acknowledgement later, or to send an error message.
  // This should not be inserted in a if(shouldSendAck_) line, this info is used to resolve fragmented messages.
  lastIncomingMessage_.dataSize     = p2pMessage.getDataSize();
  lastIncomingMessage_.messageID    = p2pMessage.getMessageID();
  lastIncomingMessage_.messageType  = P2P_MSG_UNKNOWN;
  lastIncomingMessage_.sentTime     = 0;
  lastIncomingMessage_.sessionID    = p2pMessage.getSessionID();
  lastIncomingMessage_.totalSize    = p2pMessage.getTotalSize();
  lastIncomingMessage_.ackSessionID = p2pMessage.getAckSessionID();
  lastIncomingMessage_.footerCode   = 0;


  // Special cases (abuse of the P2P system)
  if( getMode() == APP_MODE_DATACAST )
  {
    // Ink data, etc..
    gotDataFragment(p2pMessage);
    return;
  }


  // Session ID is 0? -> the clients negotiate for session.
  if(p2pMessage.getSessionID() == 0)
  {
    if( p2pMessage.isSlpData() )
    {
      // Parse the fragment
      gotNegotiationFragment(p2pMessage);
    }
    else
    {
      kWarning() << "P2PApplicationBase:gotMessage() - P2P message can't be handled, "
                    "unknown negotiation message type "
                    "(flags=0x" << QString::number(p2pMessage.getFlags(), 16) <<
                    " size="    << p2pMessage.getDataSize() <<
                    " contact=" << getContactHandle() <<
                    " session=" << getSessionID() <<
                    " class="   << metaObject()->className() <<
                    " action=send0x08).";

      // Don't know what to do with a negotiation message with flags set.
      sendP2PAbort();

      // Abort the application
      showEventMessage( i18n("The transfer failed. The contact sent bad data, or KMess does not support it."), ChatMessage::CONTENT_APP_CANCELED, true );
      endApplicationLater();
      return;
    }
  }
  // Session ID not zero? -> clients exchange data in the session
  else
  {
    // Check for data preparation messages
    if( p2pMessage.isDataPreparation() )
    {
      shouldSendAck_ = true;
      lastIncomingMessage_.messageType = P2P_MSG_DATA_PREPARATION;
      gotDataPreparation(p2pMessage);
    }
    else if( waitingState_ != P2P_WAIT_FOR_FILE_DATA
         &&  p2pMessage.isData()
         &&  p2pMessage.getDataSize()  == 4
         &&  p2pMessage.getTotalSize() == 4 )
    {
      // HACK: added for Encarta Instant Answers (has data flag set with preparation message).
      kWarning() << "Expecting data-preparation message, "
                    "got message of 4 bytes with incorrect flags "
                    "(assuming data preparation,"
                    " contact=" << getContactHandle() <<
                    " session=" << getSessionID() <<
                    " class="   << metaObject()->className() << ")!";
      shouldSendAck_ = true;
      lastIncomingMessage_.messageType = P2P_MSG_DATA_PREPARATION;
      gotDataPreparation(p2pMessage);
    }
    // Check for normal data messages
    else if( p2pMessage.isData() )
    {
      lastIncomingMessage_.messageType = P2P_MSG_DATA;
      gotDataFragment(p2pMessage);
    }
    else if( waitingState_ == P2P_WAIT_FOR_FILE_DATA
         &&  p2pMessage.getFlags() == 0
         &&  p2pMessage.isFragment())
    {
      // HACK: added for Kopete 0.9.2 code (has no flag set with data messages).
      kWarning() << "Expecting data message, "
                    "got fragmented message with no flags set "
                    "(assuming file data,"
                    " contact=" << getContactHandle() <<
                    " session=" << getSessionID() <<
                    " class="   << metaObject()->className() << ")!";
      lastIncomingMessage_.messageType = P2P_MSG_DATA;
      gotDataFragment(p2pMessage);
    }
    else if( p2pMessage.getFlags() == 0
         &&  p2pMessage.getDataSize() >= 18
         &&  ( p2pMessage.getData()[0] == '\x80'  // start of each webcam data message
               || fragmentTracker_.getMessageID() == p2pMessage.getMessageID() )  // continuation of setup message (<receiver>))    TODO: add native support for "data streams" in this base class.
           )
    {
      // Webcam setup. Even WLM sends this without flags.
      // At the switchboard the "footercode" is set to 4.
      // Pass it to the WebcamTransferP2P class to deal with it.
      lastIncomingMessage_.messageType = P2P_MSG_WEBCAM_SETUP;
      lastIncomingMessage_.footerCode  = 4;   // FIXME: direct assumption.
      gotDataFragment( p2pMessage );
    }
    else
    {
      // Unknown p2p message
      kWarning() << "P2P message can't be handled, "
                    "unknown data message type "
                    "(flags=0x" << QString::number(p2pMessage.getFlags(), 16) <<
                    " size="    << p2pMessage.getDataSize() <<
                    " contact=" << getContactHandle() <<
                    " session=" << getSessionID() <<
                    " class="   << metaObject()->className() <<
                    " action=send0x08).";

      // Got an P2P binary message we can't handle. (some new kind of flag..?)
      sendP2PAbort();

      // Abort the application
      showEventMessage( i18n("The transfer failed. The contact sent bad data, or KMess does not support it."), ChatMessage::CONTENT_APP_CANCELED, true );
      endApplicationLater();
      return;
    }
  }

#ifdef KMESSTEST
  // At the end of this method, we should have sent the ACK..!
  KMESS_ASSERT( isP2PAckSent() );
#endif
}


Generated by  Doxygen 1.6.0   Back to index