Skip to content
Snippets Groups Projects
Select Git revision
  • efb6f574209f0e22dec6b0e91fd55a830dda4501
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

voiplink.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    sipvoiplink.cpp 46.18 KiB
    /*
     *  Copyright (C) 2004-2006 Savoir-Faire Linux inc.
     *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
     *  Author : Laurielle Lea <laurielle.lea@savoirfairelinux.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.
     *
     *  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 "sipvoiplink.h"
    #include "eventthread.h"
    #include "sipcall.h"
    #include <sstream> // for ostringstream
    
    #include "manager.h"
    #include "user_cfg.h" // SIGNALISATION / PULSE #define
    
    // for listener
    #define DEFAULT_SIP_PORT  5060
    #define RANDOM_SIP_PORT   rand() % 64000 + 1024
    #define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2
    
    #define EXOSIP_ERROR_NO   0
    #define EXOSIP_ERROR_STD -1
    #define EXOSIP_ERROR_BUILDING -2
    
    // for registration
    #define EXPIRES_VALUE 180
    
    // 1XX responses
    #define DIALOG_ESTABLISHED 101
    // see: osip_const.h
    
    // FOR VOICE Message handling
    #define VOICE_MSG     "Voice-Message"
    #define LENGTH_VOICE_MSG  15
    
    // need for hold/unhold
    #define INVITE_METHOD "INVITE"
    
    
    SIPVoIPLink::SIPVoIPLink(const AccountID& accountID)
     : VoIPLink(accountID), _localExternAddress("")
    {
      _evThread = new EventThread(this);
    
      _nMsgVoicemail = 0;
      _eXosipRegID = EXOSIP_ERROR_STD;
      _eXosipStarted = false;
    
      _nbTryListenAddr = 2; // number of times to try to start SIP listener
      _localExternPort = 0;
    
      // to get random number for RANDOM_PORT
      srand (time(NULL));
    }
    
    SIPVoIPLink::~SIPVoIPLink()
    {
      delete _evThread; _evThread = 0;
      terminate();
    }
    
    bool 
    SIPVoIPLink::init()
    {
      if (!_eXosipStarted) {
        if (0 != eXosip_init()) {
          _debug("Could not initialize eXosip\n");
          return false;
        }
        _eXosipStarted = true;
      
        // check networking capabilities
        if ( !checkNetwork() ) {
          _debug("SIP FAILURE: Unable to determine network capabilities\n");
          return false;
        }
      
        // if we useStun and we failed to receive something on port 5060, we try a random port
        // If use STUN server, firewall address setup
        int errExosip = 0;
        int port = DEFAULT_SIP_PORT;
      
        int iTry = 1;  // try number..
      
        do {
          if (_useStun && !Manager::instance().behindNat(port)) { 
            port = RANDOM_SIP_PORT; 
            if (!Manager::instance().behindNat(port)) {
             _debug("SIP FAILURE: Unable to check NAT setting\n");
              return false; // hoho we can't use the random sip port too...
            }
          }
      
          // second parameter, NULL is "::" for ipv6 and "0.0.0.0" for ipv4, we can put INADDR_ANY
          errExosip = eXosip_listen_addr(IPPROTO_UDP, INADDR_ANY, port, AF_INET, 0);
          if (errExosip != 0) {
            _debug("SIP ERROR: [%d/%d] could not initialize SIP listener on port %d\n", iTry, _nbTryListenAddr, port);
            port = RANDOM_SIP_PORT;
          }
        } while ( errExosip != 0 && iTry < _nbTryListenAddr );
      
        if ( errExosip != 0 ) { // we didn't succeeded
          _debug("SIP FAILURE: SIP failed to listen on port %d\n", port);
          return false;
        }
        _localPort = port;
        _debug("SIP Init: listening on port %d\n", port);
        
        if (_useStun) {
          // This method is used to replace contact address with the public address of your NAT
          // it should be call after eXosip_listen_addr
          // set by last behindNat() call (ish)...
          _localExternAddress  = Manager::instance().getFirewallAddress();
          _localExternPort     = Manager::instance().getFirewallPort();
          eXosip_masquerade_contact(_localExternAddress.data(), _localExternPort);
        } else {
          _localExternAddress = _localIPAddress;
          _localExternPort    = _localPort;
        }
        
        // Set user agent
        std::string tmp = std::string(PROGNAME_GLOBAL) + "/" + std::string(SFLPHONED_VERSION);
        eXosip_set_user_agent(tmp.data());
      
        _debug("SIP Init: starting loop thread (SIP events)\n");
        _evThread->start();
      }
      return _eXosipStarted;
    }
    
    
    
    void 
    SIPVoIPLink::terminate()
    {
      terminateSIPCall(); 
      if (_eXosipStarted) {
        eXosip_quit();
        _eXosipStarted = false;
      }
    }
    
    void
    SIPVoIPLink::terminateSIPCall()
    {
      ost::MutexLock m(_callMapMutex);
      CallMap::iterator iter = _callMap.begin();
      SIPCall *call;
      while( iter != _callMap.end() ) {
        call = dynamic_cast<SIPCall*>(iter->second);
        if (call) {
          // Release SIP stack.
          eXosip_lock();
          eXosip_call_terminate(call->getCid(), call->getDid() );
          eXosip_unlock();
          delete call; call = 0;
        }
        iter++;
      }
      _callMap.clear();
    }
    
    bool
    SIPVoIPLink::checkNetwork()
    {
      // Set IP address
      return loadSIPLocalIP();
    }
    
    bool
    SIPVoIPLink::loadSIPLocalIP() 
    {
      bool returnValue = true;
      if (_localIPAddress == "127.0.0.1") {
        char* myIPAddress = new char[65];
        if (eXosip_guess_localip(AF_INET, myIPAddress, 64) == EXOSIP_ERROR_STD) {
          returnValue = false;
        } else {
          _localIPAddress = std::string(myIPAddress);
          _debug("Checking network, setting local IP address to: %s\n", myIPAddress);
        }
        delete [] myIPAddress; myIPAddress = NULL;
      }
      return returnValue;
    }
    
    void
    SIPVoIPLink::getEvent()
    {
      eXosip_event_t* event = eXosip_event_wait(0, 50);
      eXosip_lock();
      eXosip_automatic_action();
      eXosip_unlock();
    
      if (event == NULL) {
        return;
      }
    
      _debug("SIP Event: [cdt=%4d:%4d:%4d] type=#%03d %s \n", event->cid, event->did, event->tid, event->type, event->textinfo);
      switch (event->type) {
         /* REGISTER related events */
         case EXOSIP_REGISTRATION_NEW:         /** 00 < announce new registration.       */
           _debug("EXOSIP_REGISTRATION_NEW event is not implemented\n");
           break;
         case EXOSIP_REGISTRATION_SUCCESS:     /** 01 < user is successfully registred.  */
           Manager::instance().registrationSucceed(getAccountID());
           break;
         case EXOSIP_REGISTRATION_FAILURE:     /** 02 < user is not registred.           */
           Manager::instance().registrationFailed(getAccountID());
           break;
         case EXOSIP_REGISTRATION_REFRESHED:   /** 03 < registration has been refreshed. */
           _debug("EXOSIP_REGISTRATION_REFRESHED event is not implemented\n");
           break;
         case EXOSIP_REGISTRATION_TERMINATED:  /** 04 < UA is not registred any more.    */
           _debug("EXOSIP_REGISTRATION_TERMINATED event is not implmeneted\n");
           break;
          
          /* INVITE related events within calls */
         case EXOSIP_CALL_INVITE:          /** 05 < announce a new call                   */
           SIPCallInvite(event);
           break;
         case EXOSIP_CALL_REINVITE:        /** 06 < announce a new INVITE within call     */
           SIPCallReinvite(event);
           break;
    
         case EXOSIP_CALL_NOANSWER:        /** 07 < announce no answer within the timeout */
           _debug("EXOSIP_CALL_NOANSWER event is not implemented\n");
           break;
         case EXOSIP_CALL_PROCEEDING:      /** 08 < announce processing by a remote app   */
           _debug("EXOSIP_CALL_NOANSWER event is not implemented\n");
           break;
         case EXOSIP_CALL_RINGING:         /** 09 < announce ringback                     */
           SIPCallRinging(event);
           break;
         case EXOSIP_CALL_ANSWERED:        /** 10 < announce start of call                */
           SIPCallAnswered(event);
           break;
         case EXOSIP_CALL_REDIRECTED:      /** 11 < announce a redirection                */
           _debug("EXOSIP_CALL_REDIRECTED event is not implemented\n");
           break;
         case EXOSIP_CALL_REQUESTFAILURE:  /** 12 < announce a request failure            */
           SIPCallRequestFailure(event);
           break;
         case EXOSIP_CALL_SERVERFAILURE:   /** 13 < announce a server failure             */
           SIPCallServerFailure(event);
           break;
         case EXOSIP_CALL_GLOBALFAILURE:   /** 14 < announce a global failure             */
           SIPCallServerFailure(event);
           break;
         case EXOSIP_CALL_ACK:             /** 15 < ACK received for 200ok to INVITE      */
           SIPCallAck(event);
           break;
          
         case EXOSIP_CALL_CANCELLED:       /** 16 < announce that call has been cancelled */
         case EXOSIP_CALL_TIMEOUT:         /** 17 < announce that call has failed         */
           Manager::instance().displayError("EXOSIP Call Error not implemented yet");
           break;
    
          /* request related events within calls (except INVITE) */
         case EXOSIP_CALL_MESSAGE_NEW:            /** 18 < announce new incoming MESSAGE. */
          SIPCallMessageNew(event);
          break;
         case EXOSIP_CALL_MESSAGE_PROCEEDING:     /** 19 < announce a 1xx for MESSAGE. */
         case EXOSIP_CALL_MESSAGE_ANSWERED:       /** 20 < announce a 200ok  */
           // 200 OK
         case EXOSIP_CALL_MESSAGE_REDIRECTED:     /** 21 < announce a failure. */
         case EXOSIP_CALL_MESSAGE_REQUESTFAILURE: /** 22 < announce a failure. */
         case EXOSIP_CALL_MESSAGE_SERVERFAILURE:  /** 23 < announce a failure. */
         case EXOSIP_CALL_MESSAGE_GLOBALFAILURE:  /** 24 < announce a failure. */
           Manager::instance().displayError("EXOSIP Call Message not implemented yet");
           break;
    
         case EXOSIP_CALL_CLOSED:          /** 25 < a BYE was received for this call      */
           SIPCallClosed(event);
           break;
    
          /* for both UAS & UAC events */
         case EXOSIP_CALL_RELEASED:           /** 26 < call context is cleared.            */
           SIPCallReleased(event);
           break;
         
          /* response received for request outside calls */
         case EXOSIP_MESSAGE_NEW:            /** 27 < announce new incoming MESSAGE. */
           if (event->request == NULL) { break; }
           SIPMessageNew(event);
           break;
         case EXOSIP_MESSAGE_PROCEEDING:     /** 28 < announce a 1xx for MESSAGE. */
         case EXOSIP_MESSAGE_ANSWERED:       /** 29 < announce a 200ok  */
         case EXOSIP_MESSAGE_REDIRECTED:     /** 30 < announce a failure. */
           Manager::instance().displayError("EXOSIP Message not implemented yet");
         break;
    
         case EXOSIP_MESSAGE_REQUESTFAILURE: /** 31 < announce a failure. */
           if (event->response !=0 && event->response->status_code == SIP_METHOD_NOT_ALLOWED) {
             Manager::instance().incomingMessage(getAccountID(), "Message are not allowed");
           } else {
             Manager::instance().displayError("EXOSIP_MESSAGE_REQUESTFAILURE not implemented yet");
           }
         break;
         case EXOSIP_MESSAGE_SERVERFAILURE:  /** 32 < announce a failure. */
         case EXOSIP_MESSAGE_GLOBALFAILURE:  /** 33 < announce a failure. */
           Manager::instance().displayError("EXOSIP Message not implemented yet");
           break;
          
          /* Presence and Instant Messaging */
         case EXOSIP_SUBSCRIPTION_UPDATE:       /** 34 < announce incoming SUBSCRIBE.      */
         case EXOSIP_SUBSCRIPTION_CLOSED:       /** 35 < announce end of subscription.     */
           Manager::instance().displayError("EXOSIP Subscription not implemented yet");
           break;
          
         case EXOSIP_SUBSCRIPTION_NOANSWER:        /** 37 < announce no answer              */
         case EXOSIP_SUBSCRIPTION_PROCEEDING:      /** 38 < announce a 1xx                  */
           Manager::instance().displayError("EXOSIP Subscription resposne not implemented yet");
           break;
         case EXOSIP_SUBSCRIPTION_ANSWERED:        /** 39 < announce a 200ok                */
           eXosip_lock();
           eXosip_automatic_action();
           eXosip_unlock();
         break;
    
         case EXOSIP_SUBSCRIPTION_REDIRECTED:      /** 40 < announce a redirection          */
         case EXOSIP_SUBSCRIPTION_REQUESTFAILURE:  /** 41 < announce a request failure      */
         case EXOSIP_SUBSCRIPTION_SERVERFAILURE:   /** 42 < announce a server failure       */
         case EXOSIP_SUBSCRIPTION_GLOBALFAILURE:   /** 43 < announce a global failure       */
         case EXOSIP_SUBSCRIPTION_NOTIFY:          /** 44 < announce new NOTIFY request     */
         case EXOSIP_SUBSCRIPTION_RELEASED:        /** 45 < call context is cleared.        */
           Manager::instance().displayError("EXOSIP Subscription resposne not implemented yet");
           break;
          
         case EXOSIP_IN_SUBSCRIPTION_NEW:          /** 46 < announce new incoming SUBSCRIBE.*/
         case EXOSIP_IN_SUBSCRIPTION_RELEASED:     /** 47 < announce end of subscription.   */
           Manager::instance().displayError("EXOSIP Subscription not implemented yet");
           break;
          
         case EXOSIP_EVENT_COUNT:               /** 48 < MAX number of events  */
          break;
      }  
      eXosip_event_free(event);
    }
    
    bool
    SIPVoIPLink::setRegister()
    {
      if (_eXosipRegID != EXOSIP_ERROR_STD) {
        Manager::instance().displayError("SIP Error: Registration already sent. Try to unregister");
        return false;
      }
    
      std::string hostname = getHostName();
      if (hostname.empty()) {
        Manager::instance().displayConfigError("Fill host part field");
        return false;
      }
    
      if (_userpart.empty()) {
        Manager::instance().displayConfigError("Fill user part field");
        return false;
      }
    
    
      std::string proxy = "sip:" + _proxy;
      hostname = "sip:" + hostname;
      std::string from = SIPFromHeader(_userpart, getHostName());
      
      osip_message_t *reg = NULL;
      eXosip_lock();
      if (!_proxy.empty()) {
        _debug("SIP Register: From: %s to %s\n", from.data(), proxy.data());
        _eXosipRegID = eXosip_register_build_initial_register(from.data(), 
                      proxy.data(), NULL, EXPIRES_VALUE, &reg);
      } else {
        _debug("SIP Register: From: %s to %s\n", from.data(), hostname.data());
        _eXosipRegID = eXosip_register_build_initial_register(from.data(), 
                      hostname.data(), NULL, EXPIRES_VALUE, &reg);
      }
      eXosip_unlock();
      if (_eXosipRegID < EXOSIP_ERROR_NO ) {
        return false;
      }
    
      if (!sendSIPAuthentification()) {
        _debug("SIP Register: No authentication\n");
        return false;
      }
    
      osip_message_set_header (reg, "Event", "Registration");
      osip_message_set_header (reg, "Allow-Events", "presence");
    
      eXosip_lock();
      int eXosipErr = eXosip_register_send_register(_eXosipRegID, reg);
      if (eXosipErr == EXOSIP_ERROR_BUILDING) {
        _debug("SIP Failure: Cannot build registration, check the setup\n"); 
        eXosip_unlock();
        return false;
      }
      if (eXosipErr == EXOSIP_ERROR_STD) {
        _debug("SIP Failure: Registration sending failed\n");
        eXosip_unlock();
        return false;
      }
      eXosip_unlock();
    
      return true;
    }
    
    std::string
    SIPVoIPLink::SIPFromHeader(const std::string& userpart, const std::string& hostpart) 
    {
      return ("\"" + getFullName() + "\"" + " <sip:" + userpart + "@" + hostpart + ">");
    }
    
    bool
    SIPVoIPLink::sendSIPAuthentification() 
    {
      std::string login = _authname;
      if (login.empty()) {
        login = _userpart;
      }
      if (login.empty()) {
        Manager::instance().displayConfigError("Fill authentification name");
        return false;
      }
      if (_password.empty()) {
        Manager::instance().displayConfigError("Fill password field");
        return false;
      }
      eXosip_lock();
      int returnValue = eXosip_add_authentication_info(login.data(), login.data(), _password.data(), NULL, NULL);
      eXosip_unlock();
    
      return (returnValue != EXOSIP_ERROR_STD ? true : false);
    }
    
    bool
    SIPVoIPLink::setUnregister()
    {
      if ( _eXosipRegID == EXOSIP_ERROR_STD) return false;
      int eXosipErr = EXOSIP_ERROR_NO;
      osip_message_t *reg = NULL;
    
      eXosip_lock();
      eXosipErr = eXosip_register_build_register (_eXosipRegID, 0, &reg);
      eXosip_unlock();
    
      if (eXosipErr != EXOSIP_ERROR_NO) {
        _debug("SIP Failure: Unable to build registration for setUnregister");
        return false;
      }
    
      eXosip_lock();
      _debug("< Sending REGISTER (expire=0)\n");
      eXosipErr = eXosip_register_send_register (_eXosipRegID, reg);
      if (eXosipErr == EXOSIP_ERROR_BUILDING) {
        _debug("SIP Failure: Cannot build registration (unregister), check the setup\n"); 
        eXosip_unlock();
        return false;
      }
      if (eXosipErr == EXOSIP_ERROR_STD) {
        _debug("SIP Failure: Unable to send registration (unregister)\n");
      }
      eXosip_unlock();
      _eXosipRegID = EXOSIP_ERROR_STD;
    
      return true;
    }
    
    Call* 
    SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl)
    {
      SIPCall* call = new SIPCall(id, Call::Outgoing);
      if (call) {
        call->setPeerNumber(toUrl);
        // we have to add the codec before using it in SIPOutgoingInvite...
        call->setCodecMap(Manager::instance().getCodecDescriptorMap());
        if ( SIPOutgoingInvite(call) ) {
          call->setConnectionState(Call::Progressing);
          call->setState(Call::Active);
          addCall(call);
        } else {
          delete call; call = 0;
        }
      }
      return call;
    }
    
    bool
    SIPVoIPLink::answer(const CallID& id)
    {
      _debug("SIP Action: start answering\n");
    
      SIPCall* call = getSIPCall(id);
      if (call==0) {
        _debug("SIP Failure: SIPCall doesn't exists\n");
        return false;
      }
    
      // Send 180 RINGING
      _debug("< Send 180 Ringing\n");
      eXosip_lock ();
      eXosip_call_send_answer(call->getTid(), SIP_RINGING, NULL);
      eXosip_unlock ();
      call->setConnectionState(Call::Ringing);
    
      // Send 200 OK
      osip_message_t *answerMessage = NULL;
      eXosip_lock();
      _debug("< Building 200 OK\n");
      int i = eXosip_call_build_answer(call->getTid(), SIP_OK, &answerMessage);
      if (i != 0) {
       _debug("< Send 400 Bad Request\n");
        eXosip_call_send_answer (call->getTid(), SIP_BAD_REQUEST, NULL);
      } else {
        // use exosip, bug locked
        i = 0;
        sdp_message_t *remote_sdp = eXosip_get_remote_sdp(call->getDid());
        if (remote_sdp!=NULL) {
          i = call->sdp_complete_message(remote_sdp, answerMessage);
          if (i!=0) {
            osip_message_free(answerMessage);
          }
          sdp_message_free(remote_sdp);
        }
        if (i != 0) {
          _debug("< Send 415 Unsupported Media Type\n");
          eXosip_call_send_answer (call->getTid(), SIP_UNSUPPORTED_MEDIA_TYPE, NULL);
        } else {
          _debug("< Send 200 OK\n");
          eXosip_call_send_answer (call->getTid(), SIP_OK, answerMessage);
        }
      }
      eXosip_unlock();
    
      if(i==0) {
        // Incoming call is answered, start the sound channel.
        _debug("          Starting AudioRTP\n");
        if (_audiortp.createNewSession(call) >= 0) {
          call->setAudioStart(true);
          call->setConnectionState(Call::Connected);
          call->setState(Call::Active);
          return true;
        } else {
          _debug("FATAL: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__);
        }
      }
      removeCall(call->getCallId());
      return false;
    
    /*  int i;
      int port;
      char tmpbuf[64];
      bzero (tmpbuf, 64);
      // Get  port   
      snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort());
    
      _debug("%10d: Answer call [cid = %d, did = %d]\n", id, getSipCall(id)->getCid(), getSipCall(id)->getDid());
      port = getSipCall(id)->getLocalAudioPort();
      _debug("            Local audio port: %d\n", port);
    */
    }
    
    bool
    SIPVoIPLink::hangup(const CallID& id)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      _debug("Hang up call %s [cd: %3d %3d]\n", id.data(), call->getCid(), call->getDid()); 
      // Release SIP stack.
      eXosip_lock();
      eXosip_call_terminate(call->getCid(), call->getDid());
      eXosip_unlock();
    
      // Release RTP channels
      if (Manager::instance().isCurrentCall(id)) {
        _audiortp.closeRtpSession();
      }
      removeCall(id);
      return true;
    }
    
    bool
    SIPVoIPLink::cancel(const CallID& id)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      _debug("Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); 
      // Release SIP stack.
      eXosip_lock();
      eXosip_call_terminate(call->getCid(), -1);
      eXosip_unlock();
    
      removeCall(id);
      return true;
    }
    
    bool
    SIPVoIPLink::onhold(const CallID& id)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      int did = call->getDid();
    
      eXosip_lock ();
      sdp_message_t *local_sdp = eXosip_get_local_sdp(did);
      eXosip_unlock ();
    
      if (local_sdp == NULL) {
        _debug("SIP Failure: unable to find local_sdp\n");
        return false;
      }
    
      // Build INVITE_METHOD for put call on-hold
      osip_message_t *invite = NULL;
      eXosip_lock ();
      int exosipErr = eXosip_call_build_request (did, INVITE_METHOD, &invite);
      eXosip_unlock ();
    
      if (exosipErr != 0) {
        sdp_message_free(local_sdp);
        _debug("SIP Failure: unable to build invite method to hold call\n");
        return false;
      }
    
      /* add sdp body */
      {
        char *tmp = NULL;
    
        int i = sdp_hold_call(local_sdp);
        if (i != 0) {
          sdp_message_free (local_sdp);
          osip_message_free (invite);
          _debug("SIP Failure: Unable to hold call in SDP\n");
          return false;
        }
        
        i = sdp_message_to_str(local_sdp, &tmp);
        sdp_message_free(local_sdp);
        if (i != 0) {
          osip_message_free (invite);
          osip_free (tmp);
          _debug("SIP Failure: Unable to translate sdp message to string\n");
          return false;
        }
        osip_message_set_body (invite, tmp, strlen (tmp));
        osip_free (tmp);
        osip_message_set_content_type (invite, "application/sdp");
      }
      
      // Stop sound
      call->setAudioStart(false);
      _audiortp.closeRtpSession();
    
      // send request
      _debug("< Send on hold request\n");
      eXosip_lock ();
      exosipErr = eXosip_call_send_request (did, invite);
      eXosip_unlock ();
      
      return true;
    }
    
    bool 
    SIPVoIPLink::offhold(const CallID& id)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      int did = call->getDid();
    
      eXosip_lock ();
      sdp_message_t *local_sdp = eXosip_get_local_sdp(did);
      eXosip_unlock ();
    
      if (local_sdp == NULL) {
        _debug("SIP Failure: unable to find local_sdp\n");
        return false;
      }
    
      // Build INVITE_METHOD for put call off-hold
      osip_message_t *invite;
      eXosip_lock ();
      int exosipErr = eXosip_call_build_request (did, INVITE_METHOD, &invite);
      eXosip_unlock ();
    
      if (exosipErr != 0) {
        sdp_message_free(local_sdp);
        return EXOSIP_ERROR_STD;
      }
    
      /* add sdp body */
      {
        char *tmp = NULL;
        
        int i = sdp_off_hold_call (local_sdp);
        if (i != 0) {
          sdp_message_free (local_sdp);
          osip_message_free (invite);
          return false;
        }
        
        i = sdp_message_to_str (local_sdp, &tmp);
        sdp_message_free (local_sdp);
        if (i != 0) {
          osip_message_free (invite);
          osip_free (tmp);
          return false;
        }
        osip_message_set_body (invite, tmp, strlen (tmp));
        osip_free (tmp);
        osip_message_set_content_type (invite, "application/sdp");
      }
    
      // Send request
      _debug("< Send off hold request\n");
      eXosip_lock ();
      exosipErr = eXosip_call_send_request (did, invite);
      eXosip_unlock ();
    
      // Enable audio
      _debug("          Starting AudioRTP\n");
      // it's sure that this is the current call id...
      if (_audiortp.createNewSession(call) < 0) {
        _debug("SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__);
        return false;
      }
      return true;
    }
    
    bool 
    SIPVoIPLink::transfer(const CallID& id, const std::string& to)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      std::string tmp_to = SIPToHeader(to);
      if (tmp_to.find("@") == std::string::npos) {
        tmp_to = tmp_to + "@" + getHostName();
      }
    
      osip_message_t *refer;
      eXosip_lock();
      // Build transfer request
      int exosipErr = eXosip_call_build_refer(call->getDid(), (char*)tmp_to.data(), &refer);
      if (exosipErr == 0) {
        // Send transfer request
        exosipErr = eXosip_call_send_request(call->getDid(), refer);
      }
      eXosip_unlock();
    
      // shall we delete the call?
      removeCall(id);
      return true;
    }
    
    bool
    SIPVoIPLink::refuse (const CallID& id)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      osip_message_t *answerMessage = NULL;
      eXosip_lock();
      // not BUSY.. where decline the invitation!
      int exosipErr = eXosip_call_build_answer(call->getTid(), SIP_DECLINE, &answerMessage);
      if (exosipErr == 0) {
        exosipErr = eXosip_call_send_answer(call->getTid(), SIP_DECLINE, answerMessage);
      }
      eXosip_unlock();
      return true;
    }
    
    bool 
    SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code)
    {
      SIPCall* call = getSIPCall(id);
      if (call==0) { _debug("Call doesn't exist\n"); return false; }  
    
      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(call->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(call->getDid(), info);
      }
      eXosip_unlock();
      
      delete[] dtmf_body; dtmf_body = NULL;
      return true;
    }
    
    bool
    SIPVoIPLink::sendMessage(const std::string& to, const std::string& body)
    {
      bool returnValue = false;
    
      // fast return
      if (body.empty()) {return returnValue; }
    
      osip_message_t* message = 0;
      const char* method = "MESSAGE";
    
      std::string sipFrom = getSipFrom();
      std::string sipTo   = getSipTo(to);
      std::string sipRoute = getSipRoute();
    
      if (!SIPCheckUrl(sipFrom)) {
        Manager::instance().displayConfigError("Error in source address");
        return returnValue;
      }
      if (!SIPCheckUrl(sipTo)) {
        Manager::instance().displayError("Error in destination address");
        return returnValue;
      }
    
      int eXosipError = EXOSIP_ERROR_STD;
      eXosip_lock();
      if ( sipRoute.empty() ) {
        eXosipError = eXosip_message_build_request(&message, method, sipTo.c_str(), sipFrom.c_str(), NULL);
      } else {
        eXosipError = eXosip_message_build_request(&message, method, sipTo.c_str(), sipFrom.c_str(), sipRoute.c_str());
      }
    
      if (eXosipError == EXOSIP_ERROR_NO) {
        // add body
        // add message
        // src: http://www.atosc.org/pipermail/public/osip/2005-October/006007.html
        osip_message_set_expires(message, "120");
        osip_message_set_body(message, body.c_str(), body.length());
        osip_message_set_content_type(message, "text/plain");
    
        eXosipError = eXosip_message_send_request(message);
        if (eXosipError == EXOSIP_ERROR_NO) {
          // correctly send the message
          returnValue = true;
        }
      }
      eXosip_unlock();
      return returnValue;
    }
    
    bool
    SIPVoIPLink::SIPOutgoingInvite(SIPCall* call) 
    {
      // If no SIP proxy setting for direct call with only IP address
      if (!SIPStartCall(call, "")) {
        _debug("SIP Failure: call not started\n");
        return false;
      }
      return true;
    }
    
    bool
    SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject) 
    {
      if (!call) return false;
    
      std::string to    = getSipTo(call->getPeerNumber());
      std::string from  = getSipFrom();
      std::string route = getSipRoute();
      _debug("            From: %s\n", from.data());
      _debug("            Route: %s\n", route.data());
    
      if (!SIPCheckUrl(from)) {
        _debug("SIP Error: Source address is invalid %s\n", from.data());
        Manager::instance().displayConfigError("Error in source address");
        return false;
      }
      if (!SIPCheckUrl(to)) {
        Manager::instance().displayErrorText(call->getCallId(), "Error in destination address");
        return false;
      }
    
      osip_message_t *invite;
      eXosip_lock();
      int eXosipError = eXosip_call_build_initial_invite (&invite, (char*)to.data(),
                                            (char*)from.data(),
                                            (char*)route.data(),
                                            (char*)subject.data());
      
      if (eXosipError != 0) {
        eXosip_unlock();
        return false; // error when building the invite
      }
    
      setCallAudioLocal(call);
    
      std::ostringstream media_audio;
      std::ostringstream rtpmap_attr;
      int payload;
      int nbChannel;
    
      // Set rtpmap according to the supported codec order
      CodecMap map = call->getCodecMap().getMap();
      CodecMap::iterator iter = map.begin();
    
      while(iter != map.end()) {
        if (iter->second!=0 && iter->second->isActive()) {
          payload = iter->first;
          // add each payload in the list of payload
          media_audio << payload << " ";
    
          rtpmap_attr << "a=rtpmap: " << payload << " " << 
          iter->second->getCodecName().data() << "/" << iter->second->getClockRate();
    
          nbChannel = iter->second->getChannel();
          if (nbChannel!=1) {
            rtpmap_attr << "/" << nbChannel;
          }
          rtpmap_attr << "\r\n";
        }
        // go to next codec
        iter++;
      }
    
      // 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];
        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 %d RTP/AVP %s\r\n"
                  "%s",
                  _localExternAddress.c_str(), _localExternAddress.c_str(), call->getLocalExternAudioPort(), media_audio.str().c_str(), rtpmap_attr.str().c_str());
        // 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");
        _debug("SDP send: %s", tmp);
      }
      
      _debug("> INVITE To <%s>\n", to.data());
      int cid = eXosip_call_send_initial_invite(invite);
    
      // Keep the cid in case of cancelling
      call->setCid(cid);
    
      if (cid <= 0) {
        eXosip_unlock();
        return false ;
      } else {
        _debug("SIP Info: Outgoing callID is %s, cid=%d\n", call->getCallId().data(), cid);
        eXosip_call_set_reference (cid, NULL);
      }
      eXosip_unlock();
    
      return true;
    }
    
    /**
     * Get the Sip FROM url (add sip:, add @host, etc...)
     */ 
    std::string
    SIPVoIPLink::getSipFrom() {
    
      // Form the From header field basis on configuration panel
      std::string host = getHostName();
      if ( host.empty() ) {
        host = _localIPAddress;
      }
      return SIPFromHeader(_userpart, host);
    }
    
    /**
     * Get the Sip TO url (add sip:, add @host, etc...)
     */
    std::string
    SIPVoIPLink::getSipTo(const std::string& to_url) {
      // Form the From header field basis on configuration panel
      bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true;
    
      // add a @host if we are registered and there is no one inside the url
      if (to_url.find("@") == std::string::npos && isRegistered) {
        std::string host = getHostName();
        if(!host.empty()) {
          return SIPToHeader(to_url + "@" + host);
        }
      }
      return SIPToHeader(to_url);
    }
    
    /**
     * Get the sip proxy (add sip: if there is one) 
     * @return empty string or <sip:proxy;lr> url
     */
    std::string
    SIPVoIPLink::getSipRoute() {
      std::string proxy = _proxy;
      if ( !proxy.empty() ) {
        proxy = "<sip:" + proxy + ";lr>";
      }
      return proxy; // return empty
    }
    
    std::string
    SIPVoIPLink::SIPToHeader(const std::string& to) 
    {
      if (to.find("sip:") == std::string::npos) {
        return ("sip:" + to );
      } else {
        return to;
      }
    }
    
    bool
    SIPVoIPLink::SIPCheckUrl(const std::string& url)
    {
      int i;
    
      osip_from_t *to;
      i = osip_from_init(&to);
      if (i != 0) {
        _debug("SIP Warning: Cannot initialize osip parser\n");
        return false;
      }
      i = osip_from_parse(to, url.data());
      if (i != 0) {
        _debug("SIP Warning: Cannot parse url %s\n", url.data());
        return false;
      }
    
      // Free memory
      osip_from_free (to);
      return true;
    }
    
    bool
    SIPVoIPLink::setCallAudioLocal(SIPCall* call) 
    {
      // Setting Audio
      unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT;
      unsigned int callLocalExternAudioPort = callLocalAudioPort;
      if (_useStun) {
        // If use Stun server
        if (Manager::instance().behindNat(callLocalAudioPort)) {
          callLocalExternAudioPort = Manager::instance().getFirewallPort();
        }
      }
      _debug("            Setting local audio port to: %d\n", callLocalAudioPort);
      _debug("            Setting local audio port (external) to: %d\n", callLocalExternAudioPort);
      
      // Set local audio port for SIPCall(id)
      call->setLocalIp(_localIPAddress);
      call->setLocalAudioPort(callLocalAudioPort);
      call->setLocalExternAudioPort(callLocalExternAudioPort);
    
      return true;
    }
    
    void
    SIPVoIPLink::SIPCallInvite(eXosip_event_t *event)
    {
      _debug("> INVITE (receive)\n");
    
      CallID id = Manager::instance().getNewCallID();
    
      SIPCall* call = new SIPCall(id, Call::Incoming);
      if (!call) {
        _debug("SIP Failure: unable to create an incoming call");
        return;
      }
      setCallAudioLocal(call);
      call->setConnectionState(Call::Progressing);
      if (call->SIPCallInvite(event)) {
        if (Manager::instance().incomingCall(call, getAccountID())) {
          addCall(call);
        } else {
          delete call; call = 0;
        }
      } else {
        delete call; call = 0;
      }
    }
    
    void
    SIPVoIPLink::SIPCallReinvite(eXosip_event_t *event)
    {
      _debug("> REINVITE (receive)\n");
      SIPCall* call = findSIPCallWithCidDid(event->cid, event->did);
      if (call == 0) {
        _debug("SIP Failure: unknown call\n");
        _debug("< Send 488 Not Acceptable Here");
        eXosip_lock();
        eXosip_call_send_answer(event->tid, 488, NULL);
        eXosip_unlock();
        return;
      }
      if ( call->getCallId() == Manager::instance().getCurrentCallId()) {
        // STOP tone
        Manager::instance().stopTone();
        // STOP old rtp session
        _audiortp.closeRtpSession();
        call->setAudioStart(false);
      }
      call->SIPCallReinvite(event);
    }
    
    void
    SIPVoIPLink::SIPCallRinging(eXosip_event_t *event)
    {
      SIPCall* call = findSIPCallWithCidDid(event->cid, event->did);
      if (!call) {
        _debug("SIP Failure: unknown call\n");
        return;
      }
      // we could set the cid/did/tid and get the FROM here...
      // but we found the call with the cid/did already, why setting it again?
      // call->ringingCall(event);
      call->setConnectionState(Call::Ringing);
      Manager::instance().peerRingingCall(call->getCallId());
    }
    
    void
    SIPVoIPLink::SIPCallAnswered(eXosip_event_t *event)
    {
      SIPCall* call = findSIPCallWithCid(event->cid);
      if (!call) {
        _debug("SIP Failure: unknown call\n");
        return;
      }
      call->setDid(event->did);
    
      if (call->getConnectionState() != Call::Connected) {
        call->SIPCallAnswered(event);
        call->SIPCallAnsweredWithoutHold(event);
    
        call->setConnectionState(Call::Connected);
        call->setState(Call::Active);
    
        Manager::instance().peerAnsweredCall(call->getCallId());
        if (Manager::instance().isCurrentCall(call->getCallId())) {
          _debug("SIP: Starting AudioRTP\n");
          if ( _audiortp.createNewSession(call) < 0) {
            _debug("RTP Failure: unable to create new session\n");
          } else {
            call->setAudioStart(true);
          }
        }
      } else {
        _debug("Answering call (on/off hold to send ACK)\n");
         call->SIPCallAnswered(event);
      }
    }
    
    void
    SIPVoIPLink::SIPCallRequestFailure(eXosip_event_t *event)
    {
      if (!event->response) { return; }
      // 404 error
      _debug("SIP Event: Request Failure, receive code %d\n", event->response->status_code);
      // Handle 4XX errors
      switch (event->response->status_code) {
      case SIP_PROXY_AUTHENTICATION_REQUIRED: 
        _debug("SIP Server ask required authentification: logging...\n");
        sendSIPAuthentification();
        eXosip_lock();
        eXosip_automatic_action();
        eXosip_unlock();
        break;
      case SIP_UNAUTHORIZED:
        _debug("Request is unauthorized. SIP Server ask authentification: logging...\n");
        sendSIPAuthentification();
        break;
    
      case SIP_BUSY_HERE:  // 486
        {
          SIPCall* call = findSIPCallWithCid(event->cid);
          if (call!=0) {
            CallID& id = call->getCallId();
            call->setConnectionState(Call::Connected);
            call->setState(Call::Busy);
            Manager::instance().displayErrorText(id, event->response->reason_phrase);
            Manager::instance().callBusy(id);
            removeCall(id);
          }
        }
        break;
      case SIP_REQUEST_TERMINATED: // 487
        break;
    
      default:
      /*case SIP_BAD_REQUEST:
      case SIP_FORBIDDEN:
      case SIP_NOT_FOUND:
      case SIP_METHOD_NOT_ALLOWED:
      case SIP_406_NOT_ACCEPTABLE:
      case SIP_REQ_TIME_OUT:
      case SIP_TEMPORARILY_UNAVAILABLE:
      case SIP_ADDRESS_INCOMPLETE:
      case SIP_NOT_ACCEPTABLE_HERE: // 488 */
        // Display error on the screen phone
        {
          SIPCall* call = findSIPCallWithCid(event->cid);
          if (call!=0) {
            CallID& id = call->getCallId();
            call->setConnectionState(Call::Connected);
            call->setState(Call::Error);
            Manager::instance().displayErrorText(id, event->response->reason_phrase);
            Manager::instance().callFailure(id);
            removeCall(id);
          }
        }
      }
    }
    
    void
    SIPVoIPLink::SIPCallServerFailure(eXosip_event_t *event) 
    {
      if (!event->response) { return; }
      switch(event->response->status_code) {
      case SIP_SERVICE_UNAVAILABLE: // 500
      case SIP_BUSY_EVRYWHERE:     // 600
      case SIP_DECLINE:             // 603
        SIPCall* call = findSIPCallWithCid(event->cid);
        if (call != 0) {
          CallID id = call->getCallId();
          Manager::instance().callFailure(id);
          removeCall(id);
        }
      break;
      }
    }
    
    void
    SIPVoIPLink::SIPCallAck(eXosip_event_t *event) 
    {
      SIPCall* call = findSIPCallWithCidDid(event->cid, event->did);
      if (!call) { return; }
      if (!call->isAudioStarted()) {
        if (Manager::instance().isCurrentCall(call->getCallId())) {
          _debug("            Starting AudioRTP\n");
          if ( _audiortp.createNewSession(call) ) {
            call->setAudioStart(true);
          }
        }
      }
    }
    
    void
    SIPVoIPLink::SIPCallMessageNew(eXosip_event_t *event) 
    {
      if (0 == event->request) return;
    
      _debug("Receive a call message\n");
    
      if (MSG_IS_INFO(event->request)) {
        _debug("It's a Request Info\n");
        osip_content_type_t* c_t = event->request->content_type;
        if (c_t != 0 && c_t->type != 0 && c_t->subtype != 0 ) {
          _debug("  Content Type of the message: %s/%s\n", c_t->type, c_t->subtype);
          // application/dtmf-relay
          if (strcmp(c_t->type, "application") == 0 && strcmp(c_t->subtype, "dtmf-relay") == 0) {
             handleDtmfRelay(event);
          }
        }
      }
    
      osip_message_t *answerOKNewMessage;
      eXosip_lock();
      if ( 0 == eXosip_call_build_answer(event->tid, SIP_OK, &answerOKNewMessage)) {
        _debug("< Sending 200 OK\n");
        eXosip_call_send_answer(event->tid, SIP_OK, answerOKNewMessage);
      } else {
        _debug("SIP Failure: Could not sent an OK message\n");
      }
      eXosip_unlock();
    
    }
    
    void
    SIPVoIPLink::SIPCallClosed(eXosip_event_t *event) 
    {
      // it was without did before
      SIPCall* call = findSIPCallWithCid(event->cid);
      if (!call) { return; }
    
      CallID id = call->getCallId();
      call->setDid(event->did);
      if (Manager::instance().isCurrentCall(id)) {
        call->setAudioStart(false);
        _audiortp.closeRtpSession();
      }  
      Manager::instance().peerHungupCall(id);
      removeCall(id);
    }
    
    void
    SIPVoIPLink::SIPCallReleased(eXosip_event_t *event)
    {
      // do cleanup if exists
      // only cid because did is always 0 in these case..
      SIPCall* call = findSIPCallWithCid(event->cid);
      if (!call) { return; }
    
      // if we are here.. something when wrong before...
      CallID id = call->getCallId();
      Manager::instance().callFailure(id);
      removeCall(id);
    }
    
    void
    SIPVoIPLink::SIPMessageNew(eXosip_event_t *event)
    {
      if (MSG_IS_OPTIONS(event->request)) {
        // old handling was
        // - send 200 OK if call id is not found
        // - send nothing if call id is found
        eXosip_lock();
        eXosip_options_send_answer (event->tid, SIP_OK, NULL);
        eXosip_unlock();
      }
      // Voice message 
      else if (MSG_IS_NOTIFY(event->request)){
        _debug("> NOTIFY Voice message\n");
        int ii;
        unsigned int pos;
        unsigned int pos_slash;
    
        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");
          return;
        }
    
        // Analyse message body
        if (!body || !body->body) {
           return;
        }
        std::string str(body->body);
        pos = str.find(VOICE_MSG);
    
        if (pos == std::string::npos) {
          // If the string is not found
          return;
        } 
    
        pos_slash = str.find ("/");
        std::string nb_msg = str.substr(pos + LENGTH_VOICE_MSG, 
        pos_slash - (pos + LENGTH_VOICE_MSG));
    
        // Set the number of voice-message
        int msgVoicemail = atoi(nb_msg.data());
    
        if (msgVoicemail != 0) {
          // If there is at least one voice-message, start notification
          Manager::instance().startVoiceMessageNotification(getAccountID(), nb_msg);
        } else {
          // Stop notification when there is 0 voice message
          Manager::instance().stopVoiceMessageNotification(getAccountID());
        }
    
      // http://www.jdrosen.net/papers/draft-ietf-simple-im-session-00.txt
      } else if (MSG_IS_MESSAGE(event->request)) {
        _debug("> MESSAGE received\n");
        // osip_content_type_t* osip_message::content_type
        osip_content_type_t* c_t = event->request->content_type;
        if (c_t != 0 &&  c_t->type != 0 && c_t->subtype != 0 ) {
          _debug("  Content Type of the message: %s/%s\n", c_t->type, c_t->subtype);
    
          osip_body_t *body = NULL;
          // Get the message body
          if (0 == osip_message_get_body(event->request, 0, &body)) {
            _debug("  Body length: %d\n", body->length);
            if (body->body!=0 && 
                strcmp(c_t->type,"text") == 0 && 
                strcmp(c_t->subtype,"plain") == 0
              ) {
              _debug("  Text body: %s\n", body->body);
              Manager::instance().incomingMessage(getAccountID(), body->body);
            }
          }
        }
        osip_message_t *answerOK;
        eXosip_lock();
        if ( 0 == eXosip_message_build_answer(event->tid, SIP_OK, &answerOK)) {
            _debug("< Sending 200 OK\n");
            eXosip_message_send_answer(event->tid, SIP_OK, answerOK);
        }
        eXosip_unlock();
      }
    
    }
    
    SIPCall* 
    SIPVoIPLink::findSIPCallWithCid(int cid) 
    {
      if (cid < 1) {
        _debug("Not enough information for this event\n");
        return 0;
      }
      ost::MutexLock m(_callMapMutex);
      SIPCall* call = 0;
      CallMap::iterator iter = _callMap.begin();
      while(iter != _callMap.end()) {
        call = dynamic_cast<SIPCall*>(iter->second);
        if (call && call->getCid() == cid) {
          return call;
        }
        iter++;
      }
      return 0;
    }
    
    SIPCall* 
    SIPVoIPLink::findSIPCallWithCidDid(int cid, int did) 
    {
      if (cid < 1 && did < -1) {
        _debug("Not enough information for this event\n");
        return 0;
      }
      ost::MutexLock m(_callMapMutex);
      SIPCall* call = 0;
      CallMap::iterator iter = _callMap.begin();
      while(iter != _callMap.end()) {
        call = dynamic_cast<SIPCall*>(iter->second);
        if (call && call->getCid() == cid && call->getDid() == did) {
          return call;
        }
        iter++;
      }
      return 0;
    }
    
    SIPCall*
    SIPVoIPLink::getSIPCall(const CallID& id) 
    {
      Call* call = getCall(id);
      if (call) {
        return dynamic_cast<SIPCall*>(call);
      }
      return 0;
    }
    
    /**
     * Handle an INFO with application/dtmf-relay content-type
     * @param event eXosip Event
     */
    bool
    SIPVoIPLink::handleDtmfRelay(eXosip_event_t* event) {
    
      SIPCall* call = findSIPCallWithCidDid(event->cid, event->did);
      if (call==0) { return false; }
    
    
      bool returnValue = false;
      osip_body_t *body = NULL;
      // Get the message body
      if (0 == osip_message_get_body(event->request, 0, &body) && body->body != 0 )   {
        _debug("  Text body: %s\n", body->body);
        std::string dtmfBody(body->body);
        unsigned int posStart = 0;
        unsigned int posEnd = 0;
        std::string signal;
        std::string duration;
        // search for signal=and duration=
        posStart = dtmfBody.find("Signal=");
        if (posStart != std::string::npos) {
          posStart += strlen("Signal=");
          posEnd = dtmfBody.find("\n", posStart);
          if (posEnd == std::string::npos) {
            posEnd = dtmfBody.length();
          }
          signal = dtmfBody.substr(posStart, posEnd-posStart+1);
          _debug("Signal value: %s\n", signal.c_str());
          
          if (!signal.empty()) {
            if (Manager::instance().isCurrentCall(call->getCallId())) {
              Manager::instance().playDtmf(signal[0]);
              returnValue = true;
            }
          }
    /*
     // we receive the duration, but we use our configuration...
    
          posStart = dtmfBody.find("Duration=");
          if (posStart != std::string::npos) {
            posStart += strlen("Duration=");
            posEnd = dtmfBody.find("\n", posStart);
            if (posEnd == std::string::npos) {
                posEnd = dtmfBody.length();
            }
            duration = dtmfBody.substr(posStart, posEnd-posStart+1);
            _debug("Duration value: %s\n", duration.c_str());
            returnValue = true;
          }
    */
        }
      }
      return returnValue;
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    // 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;
    }