Skip to content
Snippets Groups Projects
  • yanmorin's avatar
    59659d29
    · 59659d29
    yanmorin authored
    Remove some comment
    Correcting some sound issue
    Using fix FRAME_PER_BUFFER is better than 0
    59659d29
    History
    yanmorin authored
    Remove some comment
    Correcting some sound issue
    Using fix FRAME_PER_BUFFER is better than 0
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sipvoiplink.cpp 38.78 KiB
/**
 *  Copyright (C) 2004-2005 Savoir-Faire Linux inc.
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
 *  Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com>
 *                                                                              
 *  Portions Copyright (C) 2002,2003   Aymeric Moizard <jack@atosc.org>
 *
 *  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.
 *                                                                              
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *                                                                              
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <eXosip2/eXosip.h>
#include <osip2/osip.h>

#include "sipvoiplink.h"
#include "global.h"
#include "audio/codecDescriptor.h"
#include "error.h"
#include "manager.h"
#include "sipcall.h"
#include "user_cfg.h"
#include "eventthread.h"

#define DEFAULT_SIP_PORT	5060
#define RANDOM_SIP_PORT		rand() % 64000 + 1024
#define	DEFAULT_LOCAL_PORT	10500
#define	RANDOM_LOCAL_PORT	((rand() % 27250) + 5250)*2

#define VOICE_MSG			"Voice-Message"
#define LENGTH_VOICE_MSG	15

SipVoIPLink::SipVoIPLink()
{
  // default _audioRTP object initialization
  _evThread = new EventThread(this);
  _localPort = 0;
  _nMsgVoicemail = 0;
  _reg_id = -1;
  // defautlt _sipcallVector object initialization

  _registrationSend = false;
  _started = false;
}

SipVoIPLink::~SipVoIPLink(void) {
  endSipCalls();
  delete _evThread; _evThread = NULL;
  if (_started) {
    eXosip_quit();
  }
}

// for voIPLink interface
void
SipVoIPLink::terminate(void) 
{
}

bool 
SipVoIPLink::checkNetwork (void) 
{
  // Set IP address
  return getLocalIp();
}

int
SipVoIPLink::init (void)
{
  std::string tmp;
  int i;

  tmp = std::string(PROGNAME) + "/" + std::string(SFLPHONED_VERSION);
	
  i = eXosip_init ();
  _started = true;

  if (i != 0) {
    _debug("Could not initialize eXosip\n");
    exit (0);
  }
	
  srand (time(NULL));
  //should be NULL or INADDR_ANY for the second parameter?
  i = eXosip_listen_addr(IPPROTO_UDP, NULL, DEFAULT_SIP_PORT, AF_INET, 0);
  if (i != 0) {
    i = eXosip_listen_addr(IPPROTO_UDP, NULL, RANDOM_SIP_PORT, AF_INET, 0);
    if (i != 0) {
      _debug("Could not initialize transport layer\n");
      return -1;
    }
  }

  // If use STUN server, firewall address setup
  if (Manager::instance().useStun()) {
    eXosip_set_user_agent(tmp.data());
    if (behindNat() != 1) {
      return 0;
    }
    // This method is used to replace contact address with the public address of your NAT.
    eXosip_masquerade_contact((Manager::instance().getFirewallAddress()).data(), Manager::instance().getFirewallPort());
		
  } else {
    // Set user agent
    eXosip_set_user_agent(tmp.data());
  }

  _debug("Thread: start event thread\n");
  _evThread->start();
  return 1;
}

/**
 * Subscibe to message-summary notify
 * It allows eXosip to not send ' 481 Subcription Does Not Exist ' response
 */
void
SipVoIPLink::subscribeMessageSummary()
{
  osip_message_t *subscribe;
  const char *route= NULL;

  // from/to
  ManagerImpl& manager = Manager::instance();
  std::string from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), manager.getConfigString(SIGNALISATION, HOST_PART));

  // to
  std::string to;
  to = manager.getConfigString(SIGNALISATION, PROXY);
  if (!to.empty()) {
    to = toHeader(manager.getConfigString(SIGNALISATION, USER_PART)) + "@" + to;
  } else {
    to = from;
  }
  

  // like in http://www.faqs.org/rfcs/rfc3842.html
  const char *event="message-summary";
  int expires = 86400;

  // return 0 if no error
  // the first from is the to... but we send the same
  int error = eXosip_subscribe_build_initial_request(&subscribe, to.c_str(), from.c_str(), route, event, expires);

  if (error == 0) {
    // Accept: application/simple-message-summary
    osip_message_set_header (subscribe, "Accept", "application/simple-message-summary");

    _debug("Sending Message-summary subscription");
    eXosip_lock();
    // return 0 if ok
    error = eXosip_subscribe_send_initial_request (subscribe);
    _debug(" and return %d\n", error);
    eXosip_unlock();
  }
}

bool
SipVoIPLink::isInRtpmap (int index, int payload, CodecDescriptorVector* cdv) {
  for (int i = 0; i < index; i++) {
    if (cdv->at(i)->getPayload() == payload) {
      return true;
    }
  }
  return false;
}

int
SipVoIPLink::setRegister (void) 
{
  ManagerImpl& manager = Manager::instance();

  // all this will be inside the profil associate with the voip link
  std::string proxy = "sip:" + manager.getConfigString(SIGNALISATION, PROXY);
  std::string hostname = "sip:" + manager.getConfigString(SIGNALISATION, HOST_PART);
  std::string from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), manager.getConfigString(SIGNALISATION, HOST_PART));

  if (manager.getConfigString(SIGNALISATION, HOST_PART).empty()) {
    manager.displayConfigError("Fill host part field");
    return -1;
  }
  if (manager.getConfigString(SIGNALISATION, USER_PART).empty()) {
    manager.displayConfigError("Fill user part field");
    return -1;
  }

  if (setAuthentication() == -1) {
    _debug("No authentication\n");
    return -1;
  }

  _debug("REGISTER From: %s\n", from.data());
  osip_message_t *reg = NULL;
  eXosip_lock();
  if (!manager.getConfigString(SIGNALISATION, PROXY).empty()) {
    _reg_id = eXosip_register_build_initial_register ((char*)from.data(), 
						      (char*)proxy.data(), NULL, EXPIRES_VALUE, &reg);
  } else {
    _reg_id = eXosip_register_build_initial_register ((char*)from.data(), 
						      (char*)hostname.data(), NULL, EXPIRES_VALUE, &reg);
  }
  if (_reg_id < 0) {
    eXosip_unlock();
    return -1;
  }	

  osip_message_set_header (reg, "Event", "Registration");
  osip_message_set_header (reg, "Allow-Events", "presence");

  int i = eXosip_register_send_register (_reg_id, reg);
  if (i == -2) {
    _debug("cannot build registration, check the setup\n"); 
    eXosip_unlock();
    return -1;
  }
  if (i == -1) {
    _debug("Registration Failed\n");
    eXosip_unlock();
    return -1;
  }
  eXosip_unlock();

  manager.error()->setError(0);

  // subscribe to message one time?
  // subscribeMessageSummary();

  _registrationSend = true;
  return i;
}

/**
 * setUnregister 
 * unregister if we already send the first registration
 * @return -1 if there is an error
 */
int 
SipVoIPLink::setUnregister (void)
{
  if ( _registrationSend ) {
    int i = 0;
    osip_message_t *reg = NULL;

    eXosip_lock();

    if (_reg_id > 0) {
      _debug("UNREGISTER\n");
      i = eXosip_register_build_register (_reg_id, 0, &reg);
    }
    if (_reg_id < 0) {
      eXosip_unlock();
      return -1;
    }	

    i = eXosip_register_send_register (_reg_id, reg);
    if (i == -2) {
      _debug("(unregister) Cannot build registration, check the setup\n"); 
      eXosip_unlock();
      return -1;
    }
    if (i == -1) {
      _debug("(unregister) Registration Failed\n");
      eXosip_unlock();
      return -1;
    }
    eXosip_unlock();
    Manager::instance().error()->setError(0);
    return i;
  } else {
    // no registration send before
    return -1;
  }
}

int
SipVoIPLink::outgoingInvite (CALLID id, const std::string& to_url) 
{
  std::string from;
  std::string to;

  // TODO: should be inside account settings
  ManagerImpl& manager = Manager::instance();
  // Form the From header field basis on configuration panel
  std::string host = manager.getConfigString(SIGNALISATION, HOST_PART);
  if ( host.empty() ) {
    host = getLocalIpAddress();
  }
  from = fromHeader(manager.getConfigString(SIGNALISATION, USER_PART), host);
	
  to = toHeader(to_url);

  if (to.find("@") == std::string::npos and 
      manager.getConfigInt(SIGNALISATION, AUTO_REGISTER)) {
    to = to + "@" + manager.getConfigString(SIGNALISATION, HOST_PART);
  }
		
  _debug("From: %s\n", from.data());
  _debug("To: %s\n", to.data());

  if (manager.getConfigString(SIGNALISATION, PROXY).empty()) {
    // If no SIP proxy setting for direct call with only IP address
    if (checkNetwork()) {
      if (startCall(id, from, to, "", "") <= 0) {
    	 _debug("Warning SipVoIPLink: call not started\n");
	     return -1;
      }
    } else {
      manager.displayErrorText(id, "No network found\n");
      return -1;
    }
    return 0;
  } else {
    // If SIP proxy setting
    std::string route = "<sip:" + 
      manager.getConfigString(SIGNALISATION, PROXY) + ";lr>";
    if (checkNetwork()) {
      if (startCall(id, from, to, "", route) <= 0) {
	     _debug("Warning SipVoIPLink: call not started\n");
	     return -1;
      }
    } else {
      manager.displayErrorText(id, "No network found\n");
      return -1;
    }
    return 0;
  }
}

/**
 * @return 0 is good, -1 is bad
 */
int
SipVoIPLink::answer (CALLID id) 
{
  int i;
  int port;
  char tmpbuf[64];
  bzero (tmpbuf, 64);
  // Get  port   
  snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort());
  _debug("Answer call [id = %d, cid = %d, did = %d]\n", id, getSipCall(id)->getCid(), getSipCall(id)->getDid());
  port = getSipCall(id)->getLocalAudioPort();
  _debug("Local audio port: %d\n", port);


  osip_message_t *answerMessage = NULL;
  SipCall* ca = getSipCall(id);

  // Send 180 RINGING
  eXosip_lock ();
  eXosip_call_send_answer (ca->getTid(), RINGING, NULL);
  eXosip_unlock ();

  // Send 200 OK
  eXosip_lock();
  i = eXosip_call_build_answer (ca->getTid(), OK, &answerMessage);
  if (i != 0) {
    // Send 400 BAD_REQUEST
    eXosip_call_send_answer (ca->getTid(), BAD_REQ, NULL);
  } else {
    i = sdp_complete_200ok (ca->getDid(), answerMessage, port);
    if (i != 0) {
      osip_message_free (answerMessage);
      // Send 415 UNSUPPORTED_MEDIA_TYPE
      eXosip_call_send_answer (ca->getTid(), UNSUP_MEDIA_TYPE, NULL);
    } else {
      eXosip_call_send_answer (ca->getTid(), OK, answerMessage);
    }
  }
  eXosip_unlock();

  // Incoming call is answered, start the sound channel.
  if (_audiortp.createNewSession (getSipCall(id)) < 0) {
    _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__);
    i = -1;
  }
  return i;
}


/**
 * @return > 0 is good, -1 is bad
 */
int
SipVoIPLink::hangup (CALLID id) 
{
  int i = 0;
  SipCall* sipcall = getSipCall(id);
  if (sipcall == NULL) { return -1; }
  _debug("Hang up call [id = %d, cid = %d, did = %d]\n", 
    id, sipcall->getCid(), sipcall->getDid());	
  // Release SIP stack.
  eXosip_lock();
  i = eXosip_call_terminate (sipcall->getCid(), sipcall->getDid());
  eXosip_unlock();

  // Release RTP channels
  sipcall->enable_audio = false;
  _audiortp.closeRtpSession();

  deleteSipCall(id);
  return i;
}

int
SipVoIPLink::cancel (CALLID id) 
{
  int i = 0;
  SipCall* sipcall = getSipCall(id);
  _debug("Cancel call [id = %d, cid = %d]\n", id, sipcall->getCid());
  // Release SIP stack.
  eXosip_lock();
  i = eXosip_call_terminate (sipcall->getCid(), -1);
  eXosip_unlock();

  deleteSipCall(id);
  return i;
}

/*
 * @return -1 = sipcall not present
 */
int
SipVoIPLink::onhold (CALLID id) 
{
  osip_message_t *invite;
  int i;
  int did;

  sdp_message_t *local_sdp = NULL;

  SipCall *sipcall = getSipCall(id);
  if ( sipcall == NULL ) { return -1; }

  did = sipcall->getDid();

  eXosip_lock ();
  local_sdp = eXosip_get_local_sdp (did);
  eXosip_unlock ();

  if (local_sdp == NULL) {
    return -1;
  }

  // Build INVITE_METHOD for put call on-hold
  eXosip_lock ();
  i = eXosip_call_build_request (did, INVITE_METHOD, &invite);
  eXosip_unlock ();

  if (i != 0) {
    sdp_message_free(local_sdp);
    return -1;
  }

  /* add sdp body */
  {
    char *tmp = NULL;

    i = sdp_hold_call (local_sdp);
    if (i != 0) {
      sdp_message_free (local_sdp);
      osip_message_free (invite);
      return -1;
    }
    
    i = sdp_message_to_str (local_sdp, &tmp);
    sdp_message_free (local_sdp);
    if (i != 0) {
      osip_message_free (invite);
      osip_free (tmp);
      return -1;
    }
    osip_message_set_body (invite, tmp, strlen (tmp));
    osip_free (tmp);
    osip_message_set_content_type (invite, "application/sdp");
  }
  
  eXosip_lock ();
  // Send request
  _audiortp.closeRtpSession();

  i = eXosip_call_send_request (did, invite);
  eXosip_unlock ();
  
  // Disable audio
  sipcall->enable_audio = false;
  return i;
}

/**
 * @return 0 is good, -1 is bad
 */
int
SipVoIPLink::offhold (CALLID id) 
{
  osip_message_t *invite;
  int i;
  int did;

  sdp_message_t *local_sdp = NULL;

  did = getSipCall(id)->getDid();
  eXosip_lock ();
  local_sdp = eXosip_get_local_sdp (did);
  eXosip_unlock ();
  if (local_sdp == NULL) {
    return -1;
  }

  eXosip_lock ();
  // Build INVITE_METHOD for put call off-hold
  i = eXosip_call_build_request (did, INVITE_METHOD, &invite);
  eXosip_unlock ();

  if (i != 0) {
    sdp_message_free(local_sdp);
    return -1;
  }

  /* add sdp body */
  {
    char *tmp = NULL;
    
    i = sdp_off_hold_call (local_sdp);
    if (i != 0) {
      sdp_message_free (local_sdp);
      osip_message_free (invite);
      return -1;
    }
    
    i = sdp_message_to_str (local_sdp, &tmp);
    sdp_message_free (local_sdp);
    if (i != 0) {
      osip_message_free (invite);
      osip_free (tmp);
      return -1;
    }
    osip_message_set_body (invite, tmp, strlen (tmp));
    osip_free (tmp);
    osip_message_set_content_type (invite, "application/sdp");
  }

  eXosip_lock ();
  // Send request
  i = eXosip_call_send_request (did, invite);
  eXosip_unlock ();
  
  // Enable audio
  if (_audiortp.createNewSession (getSipCall(id)) < 0) {
    _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__);
    i = -1;
  }
  return i;
}

int
SipVoIPLink::transfer (CALLID id, const std::string& to)
{
  osip_message_t *refer;
  int i;
  std::string tmp_to;
  tmp_to = toHeader(to);
  if (tmp_to.find("@") == std::string::npos) {
    tmp_to = tmp_to + "@" + Manager::instance().getConfigString(SIGNALISATION,
HOST_PART);
  }

  eXosip_lock();
  // Build transfer request
  i = eXosip_call_build_refer (getSipCall(id)->getDid(), (char*)tmp_to.data(),
			       &refer);
  if (i == 0) {
    // Send transfer request
    i = eXosip_call_send_request (getSipCall(id)->getDid(), refer);
  }
  eXosip_unlock();
  return i;
}

int
SipVoIPLink::refuse (CALLID id)
{
  int i;
  char tmpbuf[64];
  bzero (tmpbuf, 64);
  // Get local port   
  snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort());
	
  osip_message_t *answerMessage = NULL;
  eXosip_lock();
  // not BUSY.. where decline the invitation!
  i = eXosip_call_build_answer (getSipCall(id)->getTid(), SIP_DECLINE, &answerMessage);
  if (i == 0) {
    i = eXosip_call_send_answer (getSipCall(id)->getTid(), SIP_DECLINE, answerMessage);
  }
  eXosip_unlock();
  return i;
}

int
SipVoIPLink::getEvent (void)
{
  // wait for 0 s, 50 ms
  eXosip_event_t* event = eXosip_event_wait (0, 50);
  eXosip_lock();
  eXosip_automatic_action();
  eXosip_unlock();

  if (event == NULL) {
    return -1;
  }

  SipCall* sipcall = NULL;
  CALLID id = 0;
  int returnValue = 0;

  _debug("Receive SipEvent #%d %s\n", event->type, event->textinfo);
  switch (event->type) {
    // IP-Phone user receives a new call
  case EXOSIP_CALL_INVITE: //
    checkNetwork();

    // Set local random port for incoming call
    if (!Manager::instance().useStun()) {
      setLocalPort(RANDOM_LOCAL_PORT);
    } else {
      // If there is a firewall
      if (behindNat() != 0) {
        setLocalPort(Manager::instance().getFirewallPort());
      } else {
        returnValue = -1;
        break;
      }
    }

    // Generate id
    id = Manager::instance().generateNewCallId();
    Manager::instance().pushBackNewCall(id, Incoming);
    _debug("New INVITE Event: call with id %d [cid = %d, did = %d]\n",id, event->cid, event->did);

    // Display the callerId-name
    osip_from_t *from;
    osip_from_init(&from);

    sipcall = getSipCall(id);
    if (event->request != NULL) {
      char *tmp = NULL;

      osip_from_to_str (event->request->from, &tmp);
      if (tmp != NULL) {
        snprintf (sipcall->getRemoteUri(), 256, "%s", tmp);
        osip_free (tmp);
      }
    }
    osip_from_parse(from, sipcall->getRemoteUri());
    {
      std::string name = osip_from_get_displayname(from);
      std::string urlUsername("");
      osip_uri_t* url = osip_from_get_url(from); 
      if ( url != NULL ) {
        urlUsername = url->username;
      }
      Manager::instance().callSetInfo(id, name, urlUsername);
      _debug("New INVITE Event: From: %s\n", name.c_str());
    }
    //Don't need this display text message now that we send the name
    //inside the Manager to the gui
    //Manager::instance().displayTextMessage(id, name);
    osip_from_free(from);

    // Associate an audio port with a call
    sipcall->setLocalAudioPort(_localPort);
    sipcall->setLocalIp(getLocalIpAddress());
    _debug("New INVITE Event: we set the local audio to: %s:%d\n", getLocalIpAddress().c_str(), _localPort);

    sipcall->newIncomingCall(event);
    if (Manager::instance().incomingCall(id) < 0) {
      Manager::instance().displayErrorText(id, "New INVITE Event: Incoming call failed");
    }
    break;

  case EXOSIP_CALL_REINVITE:
    _debug("!!! EXOSIP_CALL_REINVITE: Should reinvite? !!!\n");
    //eXosip_call_send_answer(event->tid, 403, NULL);
    //488 as http://www.atosc.org/pipermail/public/osip/2005-June/005385.html
    id = findCallId(event);
    if (id != 0) {
      sipcall = getSipCall(id);
      if (sipcall != NULL) {
        _debug("Call reinvite : [id = %d, cid = %d, did = %d], localport=%d\n", id, event->cid, event->did,sipcall->getLocalAudioPort());

        _audiortp.closeRtpSession();
        sipcall->newIncomingCall(event);
        _audiortp.createNewSession(sipcall);
      }
    } else {
      eXosip_call_send_answer(event->tid, 488, NULL);
    }
    break;

  case EXOSIP_CALL_PROCEEDING: // 8
    // proceeding call...
    break;

    // The peer-user answers
  case EXOSIP_CALL_ANSWERED: // 10
  {
    id = findCallIdInitial(event);
    if ( id != 0) {
      sipcall = getSipCall(id);
      if ( sipcall ) {
       _debug("Call is answered [id = %d, cid = %d, did = %d], localport=%d\n", 
      id, event->cid, event->did,sipcall->getLocalAudioPort());
      }

      // Answer
      if (Manager::instance().callCanBeAnswered(id)) {
        sipcall->setStandBy(false);
        if (sipcall->answeredCall(event) != -1) {
          sipcall->answeredCall_without_hold(event);
          Manager::instance().peerAnsweredCall(id);

          // Outgoing call is answered, start the sound channel.
          if (_audiortp.createNewSession (sipcall) < 0) {
            _debug("FATAL: Unable to start sound (%s:%d)\n", 
            __FILE__, __LINE__);
            returnValue = -1;
            break;
          }
        }
      } else {
        // Answer to on/off hold to send ACK
        sipcall->answeredCall(event);
      }
      break;
    } else {
      returnValue = -1;
    }
  }
  case EXOSIP_CALL_RINGING: //peer call is ringing
    id = findCallIdInitial(event);
    _debug("Call is ringing [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did);
    if (id != 0) {
      getSipCall(id)->ringingCall(event);
      Manager::instance().peerRingingCall(id);
    } else {
      returnValue = -1;
    }
    break;

  case EXOSIP_CALL_REDIRECTED:
    break;

  case EXOSIP_CALL_ACK:
    id = findCallId(event);
    _debug("ACK received [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did);
    if (id != 0) {
      getSipCall(id)->receivedAck(event);
    } else {
      returnValue = -1;
    }
    break;

    // The peer-user closed the phone call(we received BYE).
  case EXOSIP_CALL_CLOSED:
    id = findCallId(event);
    _debug("Call is closed [id = %d, cid = %d, did = %d]\n", id, event->cid, event->did);	
    if (id != 0) {
      if (Manager::instance().callCanBeClosed(id)) {
         sipcall = getSipCall(id);
         if ( sipcall != NULL ) { sipcall->enable_audio = false; }
         _audiortp.closeRtpSession();
      }
      Manager::instance().peerHungupCall(id);
      deleteSipCall(id);
    } else {
      returnValue = -1;
    }	
    break;
  case EXOSIP_CALL_RELEASED:
    //id = findCallId(event);
    //if (id!=0) {
    //Manager::instance().peerHungupCall(id);
    //deleteSipCall(id);
    //}
    break;
  case EXOSIP_CALL_REQUESTFAILURE:
    id = findCallId(event);

    // Handle 4XX errors
    switch (event->response->status_code) {
    case AUTH_REQUIRED:
      _debug("SIP Server ask required authentification: loging...\n");
      setAuthentication();
      eXosip_lock();
      eXosip_automatic_action();
      eXosip_unlock();
      break;
    case UNAUTHORIZED:
      _debug("Request is unauthorized. SIP Server ask authentification: loging...\n");
      setAuthentication();
      break;

    case BAD_REQ:
    case FORBIDDEN:
    case NOT_FOUND:
    case NOT_ALLOWED:
    case NOT_ACCEPTABLE:
    case REQ_TIMEOUT:
    case TEMP_UNAVAILABLE:
    case ADDR_INCOMPLETE:
      // Display error on the screen phone
      //Manager::instance().displayError(event->response->reason_phrase);
      Manager::instance().displayErrorText(id, event->response->reason_phrase);
      Manager::instance().callFailure(id);
    break;
    case BUSY_HERE:
      Manager::instance().displayErrorText(id, event->response->reason_phrase);
      Manager::instance().callBusy(id);
      break;
    case REQ_TERMINATED:
      break;
    default:
      break;
    }

    break; 

  case EXOSIP_CALL_SERVERFAILURE:
    // Handle 5XX errors
    switch (event->response->status_code) {
    case SERVICE_UNAVAILABLE:
      id = findCallId(event);
      Manager::instance().callFailure(id);
      break;
    default:
      break;
    }
    break;

  case EXOSIP_CALL_GLOBALFAILURE:
    // Handle 6XX errors
    switch (event->response->status_code) {
    case BUSY_EVERYWHERE:
    case DECLINE:
      id = findCallId(event);
      Manager::instance().callFailure(id);
      break;
    default:
      break;
    }
    break;

  case EXOSIP_CALL_MESSAGE_NEW: // 18
    // TODO:
    break;

  case EXOSIP_REGISTRATION_SUCCESS: // 1
    // Manager::instance().displayStatus(LOGGED_IN_STATUS);
    break;

  case EXOSIP_REGISTRATION_FAILURE: // 2
    //Manager::instance().displayError("getEvent : Registration Failure");
    break;

  case EXOSIP_MESSAGE_NEW:
    unsigned int k;
				
    if (event->request != NULL && MSG_IS_OPTIONS(event->request)) {
      for (k = 0; k < _sipcallVector.size(); k++) {
        if (_sipcallVector.at(k)->getCid() == event->cid) { 
          break;
        }
      }

      // TODO: Que faire si rien trouve??
      eXosip_lock();
      if (k == _sipcallVector.size()) {
        /* answer 200 ok */
        eXosip_options_send_answer (event->tid, OK, NULL);
      } else if (_sipcallVector.at(k)->getCid() == event->cid) {
        /* already answered! */
      } else {
        /* answer 486 ok */
      	eXosip_options_send_answer (event->tid, BUSY_HERE, NULL);
      }
      eXosip_unlock();
    } 

    // Voice message 
    else if (event->request != NULL && MSG_IS_NOTIFY(event->request)){
      int ii;
      unsigned int pos;
      unsigned int pos_slash;
      
      std::string nb_msg;
      osip_body_t *body = NULL;

      // Get the message body
      ii = osip_message_get_body(event->request, 0, &body);
      if (ii != 0) {
        _debug("Cannot get body in a new EXOSIP_MESSAGE_NEW event\n");
        returnValue = -1;
        break;
      }

      // Analyse message body
      if (!body || !body->body) {
        returnValue = -1;
        break;
      }
      std::string str(body->body);
      pos = str.find(VOICE_MSG);

      if (pos == std::string::npos) {
	     // If the string is not found
        returnValue = -1;
        break;
      } 

      pos_slash = str.find ("/");
      nb_msg = str.substr(pos + LENGTH_VOICE_MSG, 
      pos_slash - (pos + LENGTH_VOICE_MSG));

      // Set the number of voice-message
      setMsgVoicemail(atoi(nb_msg.data()));

      if (getMsgVoicemail() != 0) {
        // If there is at least one voice-message, start notification
        Manager::instance().startVoiceMessageNotification(nb_msg);
      } else {
        // Stop notification when there is 0 voice message
        Manager::instance().stopVoiceMessageNotification();
      }
    }
    break;

  case EXOSIP_SUBSCRIPTION_ANSWERED: // 38
    eXosip_lock();
    eXosip_automatic_action();
    eXosip_unlock();
    break;

  case EXOSIP_SUBSCRIPTION_REQUESTFAILURE: //40
    break;

  default:
    //Manager::instance().displayErrorText(event->type, "getEvent:default");
    returnValue = -1;
    break;
  }
  //_debug(" : end event : %d / %d\n", event->type, returnValue);
  eXosip_event_free(event);

  return returnValue;
}

int
SipVoIPLink::getLocalPort (void) 
{
  return _localPort;
}

void
SipVoIPLink::setLocalPort (int port) 
{
  _localPort = port;
}

void
SipVoIPLink::carryingDTMFdigits (CALLID id, char code) {
  int duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH);
  osip_message_t *info;
  const int body_len = 1000;
  int i;

  char *dtmf_body = new char[body_len];

  eXosip_lock();
  // Build info request
  i = eXosip_call_build_info (getSipCall(id)->getDid(), &info);
  if (i == 0) {
    snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n",
	     code, duration);
    osip_message_set_content_type (info, "application/dtmf-relay");
    osip_message_set_body (info, dtmf_body, strlen (dtmf_body));
    // Send info request
    i = eXosip_call_send_request (getSipCall(id)->getDid(), info);
  }
  eXosip_unlock();
	
  delete[] dtmf_body; dtmf_body = NULL;
}
 
void
SipVoIPLink::newOutgoingCall (CALLID id)
{
  SipCall* sipcall = new SipCall(id, Manager::instance().getCodecDescVector());
  if (sipcall != NULL) {
    _sipcallVector.push_back(sipcall);
    sipcall->setStandBy(true);
  }
}

void
SipVoIPLink::newIncomingCall (CALLID id)
{
  SipCall* sipcall = new SipCall(id, Manager::instance().getCodecDescVector());
  if (sipcall != NULL) {
    _sipcallVector.push_back(sipcall);
  }
}

void
SipVoIPLink::deleteSipCall (CALLID id)
{
  std::vector< SipCall* >::iterator iter = _sipcallVector.begin();

  while(iter != _sipcallVector.end()) {
    if (*iter && (*iter)->getId() == id) {
      delete *iter; *iter = NULL;
      _sipcallVector.erase(iter);
      return;
    }
    iter++;
  }
}

void
SipVoIPLink::endSipCalls()
{
  std::vector< SipCall* >::iterator iter = _sipcallVector.begin();
  while(iter != _sipcallVector.end()) {
    if ( *iter ) {

      // Release SIP stack.
      eXosip_lock();
      eXosip_call_terminate ((*iter)->getCid(), (*iter)->getDid());
      eXosip_unlock();
      // Release RTP channels
      _audiortp.closeRtpSession();
      delete *iter; *iter = NULL;
    }
    iter++;
  }
  _sipcallVector.clear();
}

SipCall*
SipVoIPLink::getSipCall (CALLID id)
{
  SipCall* sipcall = NULL;
  for (unsigned int i = 0; i < _sipcallVector.size(); i++) {
    sipcall = _sipcallVector.at(i);
    if (sipcall && sipcall->getId() == id) {
      return sipcall;
    } 
  }
  return NULL;
}

AudioCodec*
SipVoIPLink::getAudioCodec (CALLID id)
{
  SipCall* sipcall = getSipCall(id);
  if (sipcall != NULL) {
    return sipcall->getAudioCodec();
  } else {
    return NULL;
  }
}
///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
int
SipVoIPLink::sdp_hold_call (sdp_message_t * sdp)
{
  int pos;
  int pos_media = -1;
  char *rcvsnd;
  int recv_send = -1;

  pos = 0;
  rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  while (rcvsnd != NULL) {
    if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly")) {
      recv_send = 0;
    } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly")
				  || 0 == strcmp (rcvsnd, "sendrecv"))) {
      recv_send = 0;
      sprintf (rcvsnd, "sendonly");
    }
    pos++;
    rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  }

  pos_media = 0;
  while (!sdp_message_endof_media (sdp, pos_media)) {
    pos = 0;
    rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    while (rcvsnd != NULL) {
      if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly")) {
	recv_send = 0;
      } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly")
				    || 0 == strcmp (rcvsnd, "sendrecv"))) {
	recv_send = 0;
	sprintf (rcvsnd, "sendonly");
      }
      pos++;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    }
    pos_media++;
  }

  if (recv_send == -1) {
    /* we need to add a global attribute with a field set to "sendonly" */
    sdp_message_a_attribute_add (sdp, -1, osip_strdup ("sendonly"), NULL);
  }

  return 0;
}

int
SipVoIPLink::sdp_off_hold_call (sdp_message_t * sdp)
{
  int pos;
  int pos_media = -1;
  char *rcvsnd;

  pos = 0;
  rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  while (rcvsnd != NULL) {
    if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly")
			   || 0 == strcmp (rcvsnd, "recvonly"))) {
      sprintf (rcvsnd, "sendrecv");
    }
    pos++;
    rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  }

  pos_media = 0;
  while (!sdp_message_endof_media (sdp, pos_media)) {
    pos = 0;
    rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    while (rcvsnd != NULL) {
      if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly")
			     || 0 == strcmp (rcvsnd, "recvonly"))) {
	sprintf (rcvsnd, "sendrecv");
      }
      pos++;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    }
    pos_media++;
  }

  return 0;
}

int
SipVoIPLink::sdp_complete_200ok (int did, osip_message_t * answerMessage, int port)
{
  sdp_message_t *remote_sdp;
  sdp_media_t *remote_med;
  char *tmp = NULL;
  char buf[4096];
  char port_tmp[64];
  int pos;

  char localip[128];

  // Format port to a char*
  bzero(port_tmp, 64);
  snprintf(port_tmp, 63, "%d", port);

  remote_sdp = eXosip_get_remote_sdp (did);
  if (remote_sdp == NULL) {
    return -1;                /* no existing body? */
  }
  eXosip_guess_localip (AF_INET, localip, 128);
  snprintf (buf, 4096,
      "v=0\r\n"
      "o=user 0 0 IN IP4 %s\r\n"
      "s=session\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n", localip,localip);
 
  pos = 0;
  while (!osip_list_eol (remote_sdp->m_medias, pos)) {
    char payloads[128];
    int pos2;

    memset (payloads, '\0', sizeof (payloads));
    remote_med = (sdp_media_t *) osip_list_get (remote_sdp->m_medias, pos);

    if (0 == osip_strcasecmp (remote_med->m_media, "audio")) {
      pos2 = 0;
      while (!osip_list_eol (remote_med->m_payloads, pos2)) {
        tmp = (char *) osip_list_get (remote_med->m_payloads, pos2);
        if (tmp != NULL && (0 == osip_strcasecmp (tmp, "0")
                || 0 == osip_strcasecmp (tmp, "8")
                || 0 == osip_strcasecmp (tmp, "3"))) {
          strcat (payloads, tmp);
          strcat (payloads, " ");
        }
        pos2++;
      }
      strcat (buf, "m=");
      strcat (buf, remote_med->m_media);
      if (pos2 == 0 || payloads[0] == '\0') {
        strcat (buf, " 0 RTP/AVP \r\n");
        sdp_message_free (remote_sdp);
        return -1;        /* refuse anyway */
      } else {
        strcat (buf, " ");
        strcat (buf, port_tmp);
        strcat (buf, " RTP/AVP ");
        strcat (buf, payloads);
        strcat (buf, "\r\n");

        if (NULL != strstr (payloads, " 0 ")
            || (payloads[0] == '0' && payloads[1] == ' ')) {
          strcat (buf, "a=rtpmap:0 PCMU/8000\r\n");
        }
        if (NULL != strstr (payloads, " 8 ")
            || (payloads[0] == '8' && payloads[1] == ' ')) {
          strcat (buf, "a=rtpmap:8 PCMA/8000\r\n");
        }
        if (NULL != strstr (payloads, " 3")
            || (payloads[0] == '3' && payloads[1] == ' ')) {
          strcat (buf, "a=rtpmap:3 GSM/8000\r\n");
        }
      }
    } else {
      strcat (buf, "m=");
      strcat (buf, remote_med->m_media);
      strcat (buf, " 0 ");
      strcat (buf, remote_med->m_proto);
      strcat (buf, " \r\n");
    }
    pos++;
  }

  osip_message_set_body (answerMessage, buf, strlen (buf));
  osip_message_set_content_type (answerMessage, "application/sdp");
  sdp_message_free (remote_sdp);
  return 0;
}


int
SipVoIPLink::behindNat (void)
{
  StunAddress4 stunSvrAddr;
  stunSvrAddr.addr = 0;
	
  // Stun server
  std::string svr = Manager::instance().getConfigString(SIGNALISATION, STUN_SERVER);
	
  // Convert char* to StunAddress4 structure
  bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr);
  if (!ret) {
    _debug("SIP: Stun server address (%s) is not valid\n", svr.data());
    return 0;
  }
	
  // Firewall address
  //_debug("STUN server: %s\n", svr.data());
  Manager::instance().getStunInfo(stunSvrAddr);

  return 1;
}

/**
 * Get the local Ip by eXosip 
 * setLocalIpAdress
 * @return false if not found
 */
bool
SipVoIPLink::getLocalIp (void) 
{
  bool returnValue = true;
  char* myIPAddress = new char[65];
  if (eXosip_guess_localip (AF_INET, myIPAddress, 64) == -1) {
    returnValue = false;
  }
  setLocalIpAddress(std::string(myIPAddress));
  delete [] myIPAddress; myIPAddress = NULL;
  return returnValue;
}

int
SipVoIPLink::checkUrl (const std::string& url)
{
  int i;
	
  osip_from_t *to;
  i = osip_from_init(&to);
  if (i != 0) {
    _debug("Warning: Cannot initialize\n");
    return -1;
  }
  i = osip_from_parse(to, url.data());
  if (i != 0) {
    _debug("Warning: Cannot parse url\n");
    return -1;
  }

  // Free memory
  osip_from_free (to);
  return 0;
}

int
SipVoIPLink::setAuthentication (void) 
{
  ManagerImpl& manager = Manager::instance();
  std::string login, pass, realm;
  login = manager.getConfigString(SIGNALISATION, AUTH_USER_NAME);
  if (login.empty()) {
    login = manager.getConfigString(SIGNALISATION, USER_PART);
  }
  pass = manager.getConfigString(SIGNALISATION, PASSWORD);
  if (pass.empty()) {
    manager.displayConfigError("Fill password field");
    return -1;
  }
  int returnValue = 0;
  eXosip_lock();
  if (eXosip_add_authentication_info(login.data(), login.data(), 
				     pass.data(), NULL, NULL) != 0) {
    returnValue = -1;
  }
  eXosip_unlock();
  return returnValue;
}

std::string
SipVoIPLink::fromHeader (const std::string& user, const std::string& host) 
{
  std::string displayname = Manager::instance().getConfigString(SIGNALISATION,
FULL_NAME);
  return ("\"" + displayname + "\"" + " <sip:" + user + "@" + host + ">");
}


std::string
SipVoIPLink::toHeader(const std::string& to) 
{
  if (to.find("sip:") == std::string::npos) {
    return ("sip:" + to );
  } else {
    return to;
  }
}

int
SipVoIPLink::startCall (CALLID id, const std::string& from, const std::string& to, const std::string& subject, const std::string& route) 
{
  SipCall* sipcall = getSipCall(id);
  if ( sipcall == NULL) {
    return -1; // error, we can't find the sipcall
  }
  osip_message_t *invite;

  if (checkUrl(from) != 0) {
    Manager::instance().displayConfigError("Error for 'From' header");
    return -1;
  }
  if (checkUrl(to) != 0) {
    Manager::instance().error()->errorName(TO_ERROR);
    return -1;
  }
	
  if (!Manager::instance().useStun()) {
    // Set random port for outgoing call if no firewall
    setLocalPort(RANDOM_LOCAL_PORT);
    _debug("SipVoIPLink::startCall: Local audio port: %d\n",_localPort);
  } else {
    // If use Stun server
    if (behindNat() != 0) {
      _debug("SipVoIPLink::startCall: sip invite: firewall port = %d\n",Manager::instance().getFirewallPort());	
      setLocalPort(Manager::instance().getFirewallPort());
    } else {
      return -1;
    }
  }
	
  // Set local audio port for sipcall(id)
  sipcall->setLocalAudioPort(_localPort);
  sipcall->setLocalIp(getLocalIpAddress());

  int i = eXosip_call_build_initial_invite (&invite, (char*)to.data(),
                                        (char*)from.data(),
                                        (char*)route.data(),
                                        (char*)subject.data());
  if (i != 0) {
    return -1; // error when building the invite
  }

  int payload;
  char rtpmap[128];
  char rtpmap_attr[2048];
  char media[64];
  char media_audio[64];

  bzero(rtpmap, 128);
  bzero(rtpmap_attr, 2048);
  bzero(media, 64);
  bzero(media_audio, 64);

  // Set rtpmap according to the supported codec order
  CodecDescriptorVector* cdv = Manager::instance().getCodecDescVector();
  unsigned int nb = cdv->size();
  for (unsigned int iCodec = 0; iCodec < nb; iCodec++) {
    payload = cdv->at(iCodec)->getPayload();

    // Add payload to rtpmap if it is not already added
    if (!isInRtpmap(iCodec, payload, cdv)) {
      snprintf(media, 63, "%d ", payload);
      strcat (media_audio, media);

      snprintf(rtpmap, 127, "a=rtpmap: %d %s/%d\r\n", payload, 
	       cdv->at(iCodec)->rtpmapPayload(payload).data(), SAMPLING_RATE);
      strcat(rtpmap_attr, rtpmap);
    }
  }

  // http://www.antisip.com/documentation/eXosip2/group__howto1__initialize.html
  // tell sip if we support SIP extension like 100rel
  // osip_message_set_supported (invite, "100rel");

  /* add sdp body */
  {
    char tmp[4096];
    char localip[128];

    eXosip_guess_localip (AF_INET, localip, 128);
    char port[64];
    bzero (port, 64);
    snprintf (port, 63, "%d", getLocalPort());

    snprintf (tmp, 4096,
              "v=0\r\n"
              "o=SFLphone 0 0 IN IP4 %s\r\n"
              "s=call\r\n"
              "c=IN IP4 %s\r\n"
              "t=0 0\r\n"
              "m=audio %s RTP/AVP %s\r\n"
	            "%s",
              localip, localip, port, media_audio, rtpmap_attr);
    // media_audio should be one, two or three numbers?
    osip_message_set_body (invite, tmp, strlen (tmp));
    osip_message_set_content_type (invite, "application/sdp");
  }
  
  eXosip_lock();
	
  // this is the cid (call id from exosip)
  int cid = eXosip_call_send_initial_invite (invite);
  // Keep the cid in case of cancelling
  sipcall->setCid(cid);

  if (cid <= 0) {
    eXosip_unlock();
    return -1;
  } else {
    eXosip_call_set_reference (cid, NULL);
  }

  eXosip_unlock();

  return cid; // this is the Cid
}

CALLID
SipVoIPLink::findCallId (eXosip_event_t *e)
{
  for (unsigned int k = 0; k < _sipcallVector.size(); k++) {
    SipCall* sipcall = _sipcallVector.at(k);
    if (sipcall && sipcall->getCid() == e->cid &&
        sipcall->getDid() == e->did) {
      return sipcall->getId();
    }
  }
  return 0;
}

/**
 * This function is used when findCallId failed (return 0)
 * ie: the dialog id change
 *     can be use when anwsering a new call or 
 *         when cancelling a call
 */
CALLID
SipVoIPLink::findCallIdInitial (eXosip_event_t *e)
{
  for (unsigned int k = 0; k < _sipcallVector.size(); k++) {
    SipCall* sipcall = _sipcallVector.at(k);
    // the dialog id is not set when you do a new call
    // so you can't check it when you want to retreive it
    // for the first call anwser
    if (sipcall && sipcall->getCid() == e->cid) {
      return sipcall->getId();
    }
  }
  return 0;
}