Skip to content
Snippets Groups Projects
Select Git revision
  • 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
30 results

sipvoiplink.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    sipvoiplink.cpp 29.89 KiB
    /**
     *  Copyright (C) 2004-2005 Savoir-Faire Linux inc.
     *  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 <sys/time.h>
    
    #include <eXosip2/eXosip.h>  
    #include <osip2/osip.h>
    #include <osipparser2/osip_const.h>
    #include <osipparser2/osip_headers.h>
    #include <osipparser2/osip_body.h>
    
    #include <cc++/thread.h>
    #include <stdexcept>
    #include <iostream>
    #include <string>
    #include <vector>
    
    #include "global.h"
    #include "sipvoiplink.h"
    #include "audio/audiortp.h"
    #include "audio/codecDescriptor.h"
    #include "call.h"
    #include "error.h"
    #include "eventthread.h"
    #include "manager.h"
    #include "sipcall.h"
    #include "user_cfg.h"
    #include "voIPLink.h"
     
    using namespace ost;
    using namespace std;
    
    #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
    
    
    SipVoIPLink::SipVoIPLink (short id) : VoIPLink (id)
    {
    	setId(id);
    	_localPort = 0;
    	_cid = 0;
    	_evThread = new EventThread (this);
    	_sipcallVector = new SipCallVector();
    	_audiortp = new AudioRtp();
    }
    
    SipVoIPLink::~SipVoIPLink (void)
    {
    	if (_evThread != NULL) {
    		delete _evThread;
    		_evThread = NULL;
    	}
    	delete _sipcallVector;
    	delete _audiortp;
    }
    
    bool 
    SipVoIPLink::checkNetwork (void) 
    {
    	// Set IP address
    	if (getLocalIp() == -1) {
    		// If no network
    		return false;
    	} else {
    		return true;
    	}
    }
    
    int
    SipVoIPLink::init (void)
    {
    	string tmp;
    	int i;
    
    	tmp = string(PROGNAME) + "/" + string(VERSION);
    	
    	i = eXosip_init ();
      	if (i != 0) {
    		_debug("Could not initialize eXosip\n");
          	exit (0);
        }
    	
    	srand (time(NULL));
    	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;
    		}
    
    		eXosip_masquerade_contact((Manager::instance().getFirewallAddress()).data(),
    				Manager::instance().getFirewallPort());
    		
    	} 
    	
    	eXosip_set_user_agent(tmp.data());
    	_evThread->start();
    	return 1;
    }
    
    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;
    }
    
    void
    SipVoIPLink::quit(void) 
    {
    	eXosip_quit();	
    }
    
    int
    SipVoIPLink::setRegister (void) 
    {
    	int i;
    	int reg_id = -1;
    	osip_message_t *reg = NULL;
    
    	string proxy = "sip:" + get_config_fields_str(SIGNALISATION, PROXY);
    
    	string hostname = "sip:" + get_config_fields_str(SIGNALISATION, HOST_PART);
    	
    	string from = fromHeader(get_config_fields_str(SIGNALISATION, USER_PART), 
    							get_config_fields_str(SIGNALISATION, HOST_PART));
    
    	if (get_config_fields_str(SIGNALISATION, HOST_PART).empty()) {
    		Manager::instance().error()->errorName(HOST_PART_FIELD_EMPTY);
    		return -1;
    	}
    	if (get_config_fields_str(SIGNALISATION, USER_PART).empty()) {
    		Manager::instance().error()->errorName(USER_PART_FIELD_EMPTY);
    		return -1;
    	}
    
    	eXosip_lock();
    	if (setAuthentication() == -1) {
    		_debug("No authentication\n");
    		eXosip_unlock();
    		return -1;
    	}
    	
    	_debug("REGISTER From: %s\n", from.data());
    	if (!get_config_fields_str(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;
    	}	
    
      	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::instance().error()->setError(0);
    	return i;
    }
    int
    SipVoIPLink::outgoingInvite (short id, const string& to_url) 
    {
    	string from;
    	string to;
    
    	// Form the From header field basis on configuration panel
    	from = fromHeader(get_config_fields_str(SIGNALISATION, USER_PART),
    							get_config_fields_str(SIGNALISATION, HOST_PART));
    	
    	to = toHeader(to_url);
    
    	if (to.find("@") == string::npos and 
    			get_config_fields_int(SIGNALISATION, AUTO_REGISTER) == YES) {
    		to = to + "@" + get_config_fields_str(SIGNALISATION, HOST_PART);
    	}
    		
    	_debug("From: %s\n", from.data());
    	_debug("To: %s\n", to.data());
    
    	if (get_config_fields_str(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::instance().displayErrorText(id, "No network found\n");
                return -1;
            }
    		return 0;
    	} else {
    	// If SIP proxy setting
    		string route = "<sip:" + 
    			get_config_fields_str(SIGNALISATION, PROXY) + ";lr>";
    		if (checkNetwork()) {
    			if (startCall(id, from, to, "", route) <= 0) {
    				_debug("Warning SipVoIPLink: call not started\n");
    				return -1;
    			}
    		} else {
    			Manager::instance().displayErrorText(id, "No network found\n");
                return -1;
            }
    		return 0;
    	}
    }
    
    int
    SipVoIPLink::answer (short 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 *answer = 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, &answer);
    	if (i != 0) {
    		// Send 400 BAD_REQUEST
         	eXosip_call_send_answer (ca->getTid(), BAD_REQ, NULL);
      	} else {
         	i = sdp_complete_200ok (ca->getDid(), answer, port);
         	if (i != 0) {
            	osip_message_free (answer);
    			// Send 415 UNSUPPORTED_MEDIA_TYPE
            	eXosip_call_send_answer (ca->getTid(), UNSUP_MEDIA_TYPE, NULL);
         	} else {
            	eXosip_call_send_answer (ca->getTid(), OK, answer);
    		}
      	}
    	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__);
    		exit(1);
    	}
    	return i;
    }
    
    int
    SipVoIPLink::hangup (short id) 
    {
    	int i = 0;
    	if (!Manager::instance().getbCongestion()) {
    		_debug("Hang up call [id = %d, cid = %d, did = %d]\n", 
    				id, getSipCall(id)->getCid(), getSipCall(id)->getDid());	
    		// Release SIP stack.
    		eXosip_lock();
    		i = eXosip_call_terminate (getSipCall(id)->getCid(), 
    									getSipCall(id)->getDid());
    		eXosip_unlock();
    
    		// Release RTP channels
    		_audiortp->closeRtpSession(getSipCall(id));
    	}
    				
    	deleteSipCall(id);
    	return i;
    }
    
    int
    SipVoIPLink::cancel (short id) 
    {
    	int i = 0;
    	if (!Manager::instance().getbCongestion()) {
    		_debug("Cancel call [id = %d, cid = %d]\n", id, getCid());
    		// Release SIP stack.
    		eXosip_lock();
    		i = eXosip_call_terminate (getCid(), -1);
    		eXosip_unlock();
    	}
    	deleteSipCall(id);
    	return i;
    }
    
    int
    SipVoIPLink::onhold (short 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 on-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_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);
    		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 ();
      
    	// Disable audio
    	_audiortp->closeRtpSession(getSipCall(id));
    	return i;
    }
    
    int
    SipVoIPLink::offhold (short 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);
    		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__);
    		exit(1);
    	}
    	return i;
    }
    
    int
    SipVoIPLink::transfer (short id, const string& to)
    {
    	osip_message_t *refer;
    	int i;
    	string tmp_to;
    	tmp_to = toHeader(to);
    	if (tmp_to.find("@") == string::npos) {
    		tmp_to = tmp_to + "@" + get_config_fields_str(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 (short id)
    {
    	int i;
    	char tmpbuf[64];
    	bzero (tmpbuf, 64);
        // Get local port   
        snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort());
    	
    	osip_message_t *answer = NULL;
    	eXosip_lock();
    	i = eXosip_call_build_answer (getSipCall(id)->getTid(), BUSY_HERE, &answer);
    	if (i == 0) {
    	  i = eXosip_call_send_answer (getSipCall(id)->getTid(), BUSY_HERE, answer);
    	}
    	eXosip_unlock();
    	return i;
    }
    
    int
    SipVoIPLink::getEvent (void)
    {
    	eXosip_event_t *event;
    	short id;
    	char *name;
    
    	event = eXosip_event_wait (0, 50);
    	eXosip_lock();
    	eXosip_automatic_action();
    	eXosip_unlock();
    
    	if (event == NULL) {
    		return -1;
    	}	
    	switch (event->type) {
    		// IP-Phone user receives a new call
    		case EXOSIP_CALL_INVITE: //
    			// 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 {
    					return -1;
    				}	
    			}
    			
    			// Generate id
    			id = Manager::instance().generateNewCallId();
    			Manager::instance().pushBackNewCall(id, Incoming);
    			_debug("Incoming Call with identifiant %d [cid = %d, did = %d]\n",
    				   id, event->cid, event->did);
    			_debug("Local audio port: %d\n", _localPort);
    
    			// Display the callerId-name
    			osip_from_t *from;
      			osip_from_init(&from);
    
    			if (event->request != NULL) {
          			char *tmp = NULL;
    
          			osip_from_to_str (event->request->from, &tmp);
          			if (tmp != NULL) {
              			snprintf (getSipCall(id)->getRemoteUri(), 256, "%s", tmp);
              			osip_free (tmp);
            		}
        		}
      			osip_from_parse(from, getSipCall(id)->getRemoteUri());
    			name = osip_from_get_displayname(from);
    			Manager::instance().displayTextMessage(id, name);
    			if (Manager::instance().getCall(id) != NULL) {
    				Manager::instance().getCall(id)->setCallerIdName(name);
    			} else {
    				return -1;
    			}
    			_debug("From: %s\n", name);
    			osip_from_free(from);
    			
    			// Associate an audio port with a call
    			getSipCall(id)->setLocalAudioPort(_localPort);
    
    			
    			getSipCall(id)->newIncomingCall(event);
    			if (Manager::instance().incomingCall(id) < 0) {
    				Manager::instance().displayErrorText(id, "Incoming call failed");
    				return -1;
    			}
    	
    			break;
    
    		// The peer-user answers
    		case EXOSIP_CALL_ANSWERED: 
    			id = findCallId(event);
    			if (id == 0) {
    				id = findCallIdWhenRinging();
    			}
    			_debug("Call is answered [id = %d, cid = %d, did = %d], localport=%d\n", 
    					id, event->cid, event->did,getSipCall(id)->getLocalAudioPort());
     
    			// Answer
    			if (id > 0 and !Manager::instance().getCall(id)->isOnHold()
    					   and !Manager::instance().getCall(id)->isOffHold()) {
    				getSipCall(id)->setStandBy(false);
    				if (getSipCall(id)->answeredCall(event) != -1) {
    					getSipCall(id)->answeredCall_without_hold(event);
    					Manager::instance().peerAnsweredCall(id);
    
    					// Outgoing call is answered, start the sound channel.
    					if (_audiortp->createNewSession (getSipCall(id)) < 0) {
    						_debug("FATAL: Unable to start sound (%s:%d)\n", 
    								__FILE__, __LINE__);
    						exit(1);
    					}
    				}
    			} else {
    			// Answer to on/off hold to send ACK
    				if (id > 0) {
    					getSipCall(id)->answeredCall(event);
    					_debug("-----------------------\n");
    				}
    			}
    			break;
    			
    		case EXOSIP_CALL_RINGING: //peer call is ringing
    			id = findCallIdWhenRinging();
    			
    			_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 {
    				return -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 {
    				return -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().getCall(id)->isProgressing()) {
    					_audiortp->closeRtpSession(getSipCall(id));
    				}
    				Manager::instance().peerHungupCall(id);
    				deleteSipCall(id);
    			} else {
    				return -1;
    			}	
    			break;
    
    		case EXOSIP_CALL_REQUESTFAILURE:
    			id = findCallId(event);
    
    			// Handle 4XX errors
    			switch (event->response->status_code) {
    				case AUTH_REQUIRED:
    					eXosip_lock();
    					eXosip_automatic_action();
    					eXosip_unlock();
    					break;
    				case BAD_REQ:
    				case UNAUTHORIZED:
    				case FORBIDDEN:
    				case NOT_FOUND:
    				case NOT_ALLOWED:
    				case NOT_ACCEPTABLE:
    				case REQ_TIMEOUT:
    				case TEMP_UNAVAILABLE:
    				case ADDR_INCOMPLETE:
    				case BUSY_HERE:
    					// Display error on the screen phone
    					Manager::instance().displayError(event->response->reason_phrase);
    					Manager::instance().congestion(true);
    					break;
    				case REQ_TERMINATED:
    					break;
    				default:
    					break;
    			}
    			break; 
    
    		case EXOSIP_CALL_SERVERFAILURE:
    			// Handle 5XX errors
    			switch (event->response->status_code) {
    				case SERVICE_UNAVAILABLE:
    					Manager::instance().ringback(false);
    					Manager::instance().congestion(true);					
    					break;
    				default:
    					break;
    			}
    			break;
    
    		case EXOSIP_CALL_GLOBALFAILURE:
    			// Handle 6XX errors
    			switch (event->response->status_code) {
    				case BUSY_EVERYWHERE:
    				case DECLINE:
    					Manager::instance().ringback(false);
    					Manager::instance().congestion(true);					
    					break;
    				default:
    					break;
    			}
    			break;
    
    		case EXOSIP_REGISTRATION_SUCCESS:
    			Manager::instance().displayStatus(LOGGED_IN_STATUS);
    			break;
    
    		case EXOSIP_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();
    			}
    			break;
    
    		default:
    			return -1;
    			break;
    	}
    	eXosip_event_free(event);
     	
        return 0;
    }
    
    int
    SipVoIPLink::getLocalPort (void) 
    {
    	return _localPort;
    }
    
    void
    SipVoIPLink::setLocalPort (int port) 
    {
    	_localPort = port;
    }
    
    void
    SipVoIPLink::carryingDTMFdigits (short id, char code) {
    	  int duration = get_config_fields_int(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;
    }
     
    void
    SipVoIPLink::newOutgoingCall (short callid)
    {
    	_sipcallVector->push_back(new SipCall(callid, 
    										  Manager::instance().getCodecDescVector()));
    	if (getSipCall(callid) != NULL) {
    		getSipCall(callid)->setStandBy(true);
    	}
    }
    
    void
    SipVoIPLink::newIncomingCall (short callid)
    {
    	SipCall* sipcall = new SipCall(callid, Manager::instance().getCodecDescVector());
    	_sipcallVector->push_back(sipcall);
    }
    
    void
    SipVoIPLink::deleteSipCall (short callid)
    {
    	unsigned int i = 0;
    	while (i < _sipcallVector->size()) {
    		if (_sipcallVector->at(i)->getId() == callid) {
    			_sipcallVector->erase(_sipcallVector->begin()+i);
    			return;
    		} else {
    			i++;
    		}
    	}
    }
    
    SipCall*
    SipVoIPLink::getSipCall (short callid)
    {
    	for (unsigned int i = 0; i < _sipcallVector->size(); i++) {
    		if (_sipcallVector->at(i)->getId() == callid) {
    			return _sipcallVector->at(i);
    		} 
    	}
    	return NULL;
    }
    
    AudioCodec*
    SipVoIPLink::getAudioCodec (short callid)
    {
    	if (getSipCall(callid)) {
    		return getSipCall(callid)->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 * answer, 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 (answer, buf, strlen (buf));
    	osip_message_set_content_type (answer, "application/sdp");
    	sdp_message_free (remote_sdp);
    	return 0;
    }
    
    
    int
    SipVoIPLink::behindNat (void)
    {
    	StunAddress4 stunSvrAddr;
    	stunSvrAddr.addr = 0;
    	
    	// Stun server
    	string svr = get_config_fields_str(SIGNALISATION, STUN_SERVER);
    	
    	// Convert char* to StunAddress4 structure
    	bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr);
    	if (!ret) {
    		_debug("SIP: Stun server address not valid\n");
    		return 0;
    	}
    	
    	// Firewall address
    	_debug("STUN server: %s\n", svr.data());
    	Manager::instance().getStunInfo(stunSvrAddr);
    
    	return 1;
    }
    
    int 
    SipVoIPLink::getLocalIp (void) 
    {
    	int ret = 0;
    	char* myIPAddress = new char[65];
    	ret = eXosip_guess_localip (2, myIPAddress, 64);
    	setLocalIpAddress(string(myIPAddress));
    
    	return ret;
    }
    
    int
    SipVoIPLink::checkUrl (const 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) 
    {
    	string login, pass, realm;
    	login = get_config_fields_str(SIGNALISATION, AUTH_USER_NAME);
    	if (login.empty()) {
    		login = get_config_fields_str(SIGNALISATION, USER_PART);
    	}
    	pass = get_config_fields_str(SIGNALISATION, PASSWORD);
    	if (pass.empty()) {
    		Manager::instance().error()->errorName(PASSWD_FIELD_EMPTY);				
    		return -1;
    	}
    
    	if (eXosip_add_authentication_info(login.data(), login.data(), 
    		pass.data(), NULL, NULL) != 0) {
    		return -1;
    	}
    	return 0;
    }
    
    string
    SipVoIPLink::fromHeader (const string& user, const string& host) 
    {
    	string displayname = get_config_fields_str(SIGNALISATION, FULL_NAME);
    	return ("\"" + displayname + "\"" + " <sip:" + user + "@" + host + ">");
    }
    
    
    string
    SipVoIPLink::toHeader(const string& to) 
    {
    	if (to.find("sip:") == string::npos) {
    		return ("sip:" + to );
    	} else {
    		return to;
    	}
    }
    
    int
    SipVoIPLink::startCall (short id, const string& from, const string& to, 
    							const string& subject,  const string& route) 
    {
      	osip_message_t *invite;
      	int i;
    
      	if (checkUrl(from) != 0) {
    		Manager::instance().error()->errorName(FROM_ERROR);
        	return -1;
      	}
      	if (checkUrl(to) != 0) {
    		Manager::instance().error()->errorName(TO_ERROR);
        	return -1;
      	}
    	
    	char port[64];
    	if (!Manager::instance().useStun()) {
    		// Set random port for outgoing call if no firewall
    		setLocalPort(RANDOM_LOCAL_PORT);
    		_debug("Local audio port: %d\n",_localPort);
    	} else {
    		// If use Stun server
    		if (behindNat() != 0) {
    			_debug("sip invite: firewall port = %d\n",Manager::instance().getFirewallPort());	
    			setLocalPort(Manager::instance().getFirewallPort());
    		} else {
    			return -1;
    		}
    	}
    	
    	// Set local audio port for sipcall(id)
    	if (getSipCall(id) != NULL) {
    		getSipCall(id)->setLocalAudioPort(_localPort);
    	} else {
    		return -1;
    	}
    	
    	bzero (port, 64);
    	snprintf (port, 63, "%d", getLocalPort());
      	
    	i = eXosip_call_build_initial_invite (&invite, (char*)to.data(),
                                            (char*)from.data(),
                                            (char*)route.data(),
                                            (char*)subject.data());
      	if (i != 0) {
    		return -1;
        }
      	
    
    	int payload;
    	unsigned int nb;
    	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
    	nb = Manager::instance().getNumberOfCodecs();
    	for (unsigned int i = 0; i < nb; i++) {
    		payload = Manager::instance().getCodecDescVector()->at(i)->getPayload();
    
    		// Add payload to rtpmap if it is not already added
    		if (!isInRtpmap(i, payload, Manager::instance().getCodecDescVector())) {
    			snprintf(media, 63, "%d ", payload);
    			strcat (media_audio, media);
    			
    			snprintf(rtpmap, 127, "a=rtpmap: %d %s/%d\r\n", payload, 
    				Manager::instance().getCodecDescVector()->at(i)->rtpmapPayload(payload).data(), SAMPLING_RATE);
    			strcat(rtpmap_attr, rtpmap); 
    		}
    	}
    
      /* add sdp body */
      {
        char tmp[4096];
        char localip[128];
    
        eXosip_guess_localip (AF_INET, localip, 128);
        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);
    	
        osip_message_set_body (invite, tmp, strlen (tmp));
        osip_message_set_content_type (invite, "application/sdp");
      }
      
    	eXosip_lock();
    	
    	i = eXosip_call_send_initial_invite (invite);
    
    	if (i <= 0) {
    		eXosip_unlock();
    		return -1;
    	} else {
    		eXosip_call_set_reference (i, NULL);
    	}
    
    	eXosip_unlock();
    
    	// Keep the cid in case of cancelling
    	setCid(i);
    
      	return i;	
    }
    
    short
    SipVoIPLink::findCallId (eXosip_event_t *e)
    {
    	unsigned int k;
    	
    	for (k = 0; k < _sipcallVector->size(); k++) {
    		if (_sipcallVector->at(k)->getCid() == e->cid and
    				_sipcallVector->at(k)->getDid()	== e->did) {
    			return _sipcallVector->at(k)->getId();
    		}
        }
    	return 0;
    }
    
    short
    SipVoIPLink::findCallIdWhenRinging (void)
    {
    	unsigned int k;
    	int i = Manager::instance().selectedCall();
    	
    	if (i != -1) {
    		return i;
    	} else {
    		for (k = 0; k < _sipcallVector->size(); k++) {
    			if (_sipcallVector->at(k)->getStandBy()) {
    				return _sipcallVector->at(k)->getId();
    			}
    		}
    	}
    	return 0;
    }