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

voiceconversation.cpp

/***************************************************************************
                          voiceconversion.cpp  -  description
                             -------------------
    begin                : 1230 2003
    copyright            : (C) 2003 by Mike K. Bennett (C)2004 by Steve gigijoe
    email                : mkb137b@hotmail.com, stevegigijoe@hotmail.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 "voiceconversation.h"
#include "sipconnection.h"

//#include <qsettings.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmdcodec.h>
/*
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
*/
#include "../../kmessdebug.h"
#include "../../currentaccount.h"
#include "../mimemessage.h"

unsigned SIP_LISTEN_PORT = 5060;
unsigned RTP_LISTEN_PORT = 10000;

VoiceConversation::VoiceConversation(const QString &contactHandle)
 : MimeApplication(contactHandle),
   userAgent_(0)
{
  setApplicationType( ChatMessage::TYPE_APPLICATION_AUDIO );
}



// The destructor
VoiceConversation::~VoiceConversation()
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "DESTROYED VoiceConversation" << endl;
#endif

  if(userAgent_ != 0)
  {
    userAgent_->releaseCall();
    delete userAgent_;
  }
}



// Step one of a contact-started chat: the contact invites the user
void VoiceConversation::contactStarted1_ContactInvitesUser(const MimeMessage& message)
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - contactStarted1_ContactInvitesUser" << endl;
#endif

  remotePublicIp_  = message.getValue("Public-IP");
  remotePrivateIp_ = message.getValue("Private-IP");
  remoteUPnP_      = message.getValue("UPnP");

#ifdef HAS_KPHONE

  // Send the message to the chat window
  offerAcceptOrReject( i18n("You are invited to start a voice conversation.") );

#else

  // Not compiled with KPhone support, reject invitation.
  showSystemMessage( i18n("You are invited to start a voice conversation but support for this was not installed"),
                     ChatMessage::CONTENT_SYSTEM_NOTICE, true );
  sendCancelMessage(CANCEL_NOT_INSTALLED);
  return;

#endif
}



// Step two of a contact-started chat: the user accepts
void VoiceConversation::contactStarted2_UserAccepts()
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - contactStarted2_UserAccepts" << endl;
#endif
  MimeMessage message;

  message.addField( "Invitation-Command", "ACCEPT");
  message.addField( "Context-Data",       "Requested:SIP_A,;");
  message.addField( "Invitation-Cookie",  getCookie());
  message.addField( "Session-ID",         getSessionId());
  message.addField( "Session-Protocol",   "SM1");

  if(getExternalIp() == getLocalIp())
  {
    // Our local IP is our external IP.
    connectionType_ = "Direct-Connect";
  }
  else if(getExternalIp() == remotePublicIp_)
  {
    // Same real IP address, different port
    connectionType_ = "Port-Restrict-NAT";
  }
  else
  {
    // This machine is behine NAT
    connectionType_ = "Symmetric-NAT";
  }

  message.addField( "Conn-Type",          connectionType_   );
  message.addField( "Sip-Capability",     "1"               );
  message.addField( "Private IP",         getLocalIp()      );
  message.addField( "Public-IP",          getExternalIp()   );
  message.addField( "UPnP",               remoteUPnP_       );
  message.addField( "Launch-Application", "TRUE"            );
  message.addField( "Request-Data",       "IP-Address:"     );
  message.addField( "IP-Address",         getExternalIp().latin1() );

  sendMessage(message);
}



// Step three of a contact-started chat: the contact confirms the accept
void VoiceConversation::contactStarted3_ContactConfirmsAccept(const MimeMessage& message)
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - contactStarted3_ContactConfirmsAccept" << endl;
#endif
  // Get the contact's information from the message
  // QString ipPort = message.getValue("IP-Address");
  // remoteUPnP_ = message.getValue("UPnP");

  QString base64IP = message.getValue("IP-Address-Enc64"); // SIP contact IP address
  QString ipPort   = KCodecs::base64Decode( base64IP.data() );

  int pos;

  if((pos = ipPort.find(":")) != -1)
  {
    remotePort_ = ipPort.right(ipPort.length() - (pos + 1));
    remoteIp_   = ipPort.left(pos);
  }

  // Start the application
  startConversation(true);
}



// Return the application's GUID
QString VoiceConversation::getAppId()
{
  return "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}";
}



// Return a cancel message to display
QString VoiceConversation::getUserAbortMessage() const
{
  return i18n("You have cancelled the voice conversation.");
}



// Start the voice
void VoiceConversation::startConversation( bool connectToRemote )
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - Start the voice conversation." << endl;
#endif

  QString localIP         = getLocalIp();
  bool    isDirectConnect = (getExternalIp() == remotePublicIp_);

  if(foundUPnPDevice_ && ! isDirectConnect)
  {
    // Use external IP address
    localIP = getExternalIp();
  }
  else if(! foundUPnPDevice_ && ! isDirectConnect)
  {
    // else in the same local network
    //QSettings settings;
    //settings.writeEntry("/kphone/STUN/UseStun", "Yes"); // STUN not test yet
  }
  else //if(isDirectConnect)
  {
    // Use internal IP address
    remoteIp_ = remotePrivateIp_;
  }

  userAgent_ = new UserAgent(localIP, SIP_LISTEN_PORT, RTP_LISTEN_PORT);

  // Abort if the audio device can't be opened.
  if(! userAgent_->audioCheck())
  {
    showEventMessage( i18n("The invitation was cancelled. The audio device couldn't be opened."), ChatMessage::CONTENT_APP_CANCELED, ! isUserStartedApp() );
    sendCancelMessage(CANCEL_FAILED);
    return;
  }


  if(connectToRemote)
  {
    // Make outgoing call
    // Doesn't have username@hostname format, will it work ?
    userAgent_->outgoingCall( "sip:" + remoteIp_ + ":" + remotePort_ );
    showEventMessage( i18n("Start voice conversation. Connecting to %1.").arg(remoteIp_), ChatMessage::CONTENT_APP_INFO, false );
  }
  else
  {
    // Wait for an incoming call
    userAgent_->incomingCall();
    showEventMessage( i18n("Start voice conversation. Listening on %1.").arg(localIP), ChatMessage::CONTENT_APP_INFO, true );
  }
}



// User stop voice conversation
void VoiceConversation::userAborted()
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - userAborted" << endl;
#endif

  // Stop the KPhone application now. (not in destructor)
  if(userAgent_)
  {
    userAgent_->releaseCall();
    delete userAgent_;
    userAgent_ = 0;
  }

  MimeApplication::userAborted();
}



// Step one of a user-started chat: the user invites the contact
void VoiceConversation::userStarted1_UserInvitesContact()
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversion - userStarted1_UserInvitesContact" << endl;
#endif
  QString     html;
  MimeMessage message;

  QString contextData    = "Requested:SIP_A,;Capabilities:SIP_A,;";
  QString connectionType = "Symmetric-NAT"; // Use Symmetric-NAT as default

  /*
  if(foundUPnPDevice_)
    connectionType = "Symmetric-NAT";
  else
  connectionType =  "Port-Restrict-NAT";

//  message.addField("Conn-Type", "Symmetric-NAT");
//  connectionType = "Unknown-NAT";
  */

  // Create the invitation message
  message.addField( "Application-Name",   "an audio conversation" );
  message.addField( "Application-GUID",   getAppId()              );
  message.addField( "Session-Protocol",   "SM1"                   );
  message.addField( "Context-Data",       contextData             );
  message.addField( "Invitation-Command", "INVITE"                );
  message.addField( "Invitation-Cookie",  getCookie()             );
  message.addField( "Session-ID",         getSessionId()          );
  message.addField( "Conn-Type",          connectionType          );
  message.addField( "Sip-Capability",     "1");
  message.addField( "Private-IP",         getLocalIp()            );
  message.addField( "Public-IP",          getExternalIp()         );
  message.addField( "UPnP",              "FALSE"                  ); // Check UPnP support later

  sendMessage(message);

  // Give the user the option of cancelling the transfer
  offerCancel( i18n("Inviting the contact to a voice conversation.") );
}



// Step two of a user-started chat: the contact accepts
void VoiceConversation::userStarted2_ContactAccepts(const MimeMessage& message)
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - userStarted2_ContactAccepts" << endl;
#endif

  // Store the data from the message
  remotePublicIp_  = message.getValue("Public-IP");
  remotePrivateIp_ = message.getValue("Private-IP");
  remoteIp_        = message.getValue("IP-Address");
  remoteUPnP_      = message.getValue("UPnP");
  connectionType_  = message.getValue("Conn-Type");


  // Confirm the accept-message
  MimeMessage response;

  QString UPnPString = (foundUPnPDevice_ ? "TRUE" : "FALSE");

  QString sipContactAddress;
  bool isDirectConnect = (getExternalIp() == remotePublicIp_);

  if(foundUPnPDevice_ && ! isDirectConnect)
  {
    // UPnP NAT Traversal support
    sipContactAddress.sprintf("%s:%d", getExternalIp().latin1(), SIP_LISTEN_PORT);
  }
  else
  {
    sipContactAddress.sprintf("%s:%d", getLocalIp().latin1(), SIP_LISTEN_PORT);
  }

  QString sipContactEncoded = QString::fromUtf8(KCodecs::base64Encode( sipContactAddress.utf8() ));

  response.addField( "Invitation-Command", "ACCEPT"            );
  response.addField( "Context-Data",       "Requested:SIP_A,;" );
  response.addField( "Invitation-Cookie",  getCookie()         );
  response.addField( "Session-ID",         getSessionId()      );
  response.addField( "Conn-Type",          connectionType_     ); // TODO: Test this
//response.addField( "Session-Protocol",   "SM1"               );
  response.addField( "Sip-Capability",     "1"                 );
  response.addField( "Private-IP",         getLocalIp()        );
  response.addField( "Public-IP",          getExternalIp()     );
  response.addField( "UPnP",               UPnPString          ); // UPnP NAT Traversal support
  response.addField( "Launch-Application", "TRUE"              );
//response.addField( "Request-Data",       "IP-Address:"       );
  response.addField( "IP-Address",         sipContactAddress   );
  response.addField( "IP-Address-Enc64",   sipContactEncoded   );

  sendMessage(response);
}



// Step three of a user-started chat: the user confirms the accept
void VoiceConversation::userStarted3_UserPrepares()
{
#ifdef KMESSDEBUG_VOICECONVERSATION
  kdDebug() << "VoiceConversation - userStarted3_UserPrepares" << endl;
#endif

  // Start the application
  startConversation(false);
}

#include "voiceconversation.moc"

Generated by  Doxygen 1.6.0   Back to index