sipvoiplink.cpp 94.7 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Savoir-Faire Linux Inc.
3
4
 *
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
5
 *  Author: Yun Liu <yun.liu@savoirfairelinux.com>
6
 *  Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
7
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
8
 *
jpbl's avatar
jpbl committed
9
10
 *  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
11
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
12
13
14
15
16
17
18
19
20
21
 *  (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.
22
23
24
25
26
27
28
29
30
31
32
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
jpbl's avatar
jpbl committed
33
 */
Emmanuel Milou's avatar
Emmanuel Milou committed
34

35
36
37
38
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

jpbl's avatar
jpbl committed
39
#include "sipvoiplink.h"
40
41
42

#include "manager.h"

43
#include "sip/sdp.h"
yanmorin's avatar
   
yanmorin committed
44
#include "sipcall.h"
Emmanuel Milou's avatar
nothing    
Emmanuel Milou committed
45
#include "sipaccount.h"
46
#include "eventthread.h"
47
#include "SdesNegotiator.h"
48
49
50

#include "dbus/dbusmanager.h"
#include "dbus/callmanager.h"
51

52
#include "hooks/urlhook.h"
53
54
#include "im/InstantMessaging.h"

55
#include "audio/audiolayer.h"
56
#include "audio/audiortp/AudioRtpFactory.h"
57

58
#include "pjsip/sip_endpoint.h"
59
#include "pjsip/sip_transport_tls.h"
60
61
62
#include "pjsip/sip_transport_tls.h"
#include "pjsip/sip_uri.h"
#include <pjnath.h>
63

Emmanuel Milou's avatar
Emmanuel Milou committed
64
65
66
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
67
#include <istream>
68
#include <utility> // for std::pair
Emmanuel Milou's avatar
Emmanuel Milou committed
69

70
71
72
73
74
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>

75
76
#include <map>

77
78
using namespace sfl;

79
80
namespace {
static const char * const invitationStateMap[] = {
81
82
83
84
85
86
87
    "PJSIP_INV_STATE_NULL",
    "PJSIP_INV_STATE_CALLING",
    "PJSIP_INV_STATE_INCOMING",
    "PJSIP_INV_STATE_EARLY",
    "PJSIP_INV_STATE_CONNECTING",
    "PJSIP_INV_STATE_CONFIRMED",
    "PJSIP_INV_STATE_DISCONNECTED"
pierre-luc's avatar
pierre-luc committed
88
};
Emmanuel Milou's avatar
Emmanuel Milou committed
89

90
static const char * const transactionStateMap[] = {
91
92
93
94
95
96
97
98
99
    "PJSIP_TSX_STATE_NULL" ,
    "PJSIP_TSX_STATE_CALLING",
    "PJSIP_TSX_STATE_TRYING",
    "PJSIP_TSX_STATE_PROCEEDING",
    "PJSIP_TSX_STATE_COMPLETED",
    "PJSIP_TSX_STATE_CONFIRMED",
    "PJSIP_TSX_STATE_TERMINATED",
    "PJSIP_TSX_STATE_DESTROYED",
    "PJSIP_TSX_STATE_MAX"
pierre-luc's avatar
pierre-luc committed
100
101
};

102
/** The default transport (5060) */
103
pjsip_transport *_localUDPTransport = NULL;
104
105

/** The local tls listener */
106
pjsip_tpfactory *_localTlsListener = NULL;
107

108
109
/** A map to retreive SFLphone internal call id
 *  Given a SIP call ID (usefull for transaction sucha as transfer)*/
110
std::map<std::string, std::string> transferCallID;
111

112
const pj_str_t STR_USER_AGENT = { (char*) "User-Agent", 10 };
113

Emmanuel Milou's avatar
Emmanuel Milou committed
114
/**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/
jpbl's avatar
jpbl committed
115

116
117
118
119
120
121
/*
 * Retrieve the SDP of the peer contained in the offer
 *
 * @param rdata The request data
 * @param r_sdp The pjmedia_sdp_media to stock the remote SDP
 */
122
123
int getModId();

124
/**
125
126
127
128
 * Set audio (SDP) configuration for a call
 * localport, localip, localexternalport
 * @param call a SIPCall valid pointer
 */
Rafaël Carré's avatar
Rafaël Carré committed
129
void setCallMediaLocal (SIPCall* call, const std::string &localIP);
130

131
132
133
134
/**
 * Helper function to parser header from incoming sip messages
 */
std::string fetchHeaderValue (pjsip_msg *msg, std::string field);
135

Emmanuel Milou's avatar
Emmanuel Milou committed
136
137
138
/*
 *  The global pool factory
 */
139
pj_caching_pool pool_cache, *_cp = &pool_cache;
Emmanuel Milou's avatar
Emmanuel Milou committed
140
141
142
143

/*
 * The pool to allocate memory
 */
144
pj_pool_t *_pool;
Emmanuel Milou's avatar
Emmanuel Milou committed
145
146
147
148

/*
 *	The SIP endpoint
 */
149
pjsip_endpoint *_endpt;
Emmanuel Milou's avatar
Emmanuel Milou committed
150
151
152
153

/*
 *	The SIP module
 */
154
pjsip_module _mod_ua;
Emmanuel Milou's avatar
Emmanuel Milou committed
155
156
157
158

/*
 * Thread related
 */
159
160
161
pj_thread_t *thread;
pj_thread_desc desc;

Emmanuel Milou's avatar
Emmanuel Milou committed
162
163
/*
 * Session callback
164
 * Called after SDP offer/answer session has completed.
Emmanuel Milou's avatar
Emmanuel Milou committed
165
166
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
167
 * @param	status	A pj_status_t structure
Emmanuel Milou's avatar
Emmanuel Milou committed
168
 */
169
170
171
172
173
174
void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status UNUSED);


void sdp_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer);

void sdp_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer);
Emmanuel Milou's avatar
Emmanuel Milou committed
175
176
177

/*
 * Session callback
178
 * Called when the invite session state has changed.
Emmanuel Milou's avatar
Emmanuel Milou committed
179
180
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
181
 * @param	e	A pointer on a pjsip_event structure
Emmanuel Milou's avatar
Emmanuel Milou committed
182
 */
183
void invite_session_state_changed_cb (pjsip_inv_session *inv, pjsip_event *e);
Emmanuel Milou's avatar
Emmanuel Milou committed
184
185

/*
186
 * Called when the invite usage module has created a new dialog and invite
Emmanuel Milou's avatar
Emmanuel Milou committed
187
188
189
190
191
 * because of forked outgoing request.
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
 * @param	e	A pointer on a pjsip_event structure
 */
192
void outgoing_request_forked_cb (pjsip_inv_session *inv, pjsip_event *e);
Emmanuel Milou's avatar
Emmanuel Milou committed
193
194
195
196
197
198
199
200
201
202

/*
 * Session callback
 * Called whenever any transactions within the session has changed their state.
 * Useful to monitor the progress of an outgoing request.
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
 * @param	tsx	A pointer on a pjsip_transaction structure
 * @param	e	A pointer on a pjsip_event structure
 */
203
void transaction_state_changed_cb (pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
Emmanuel Milou's avatar
Emmanuel Milou committed
204

Alexandre Savard's avatar
Alexandre Savard committed
205

Emmanuel Milou's avatar
Emmanuel Milou committed
206
207
208
/*
 * Registration callback
 */
209
void registration_cb (struct pjsip_regc_cbparam *param);
Emmanuel Milou's avatar
Emmanuel Milou committed
210
211
212
213
214
215

/*
 * Called to handle incoming requests outside dialogs
 * @param   rdata
 * @return  pj_bool_t
 */
216
pj_bool_t transaction_request_cb (pjsip_rx_data *rdata);
Emmanuel Milou's avatar
Emmanuel Milou committed
217
218
219
220
221
222

/*
 * Called to handle incoming response
 * @param	rdata
 * @return	pj_bool_t
 */
223
pj_bool_t transaction_response_cb (pjsip_rx_data *rdata UNUSED) ;
Emmanuel Milou's avatar
Emmanuel Milou committed
224

225
226
227
228
229
230
231
/**
 * Send a reINVITE inside an active dialog to modify its state
 * Local SDP session should be modified before calling this method
 * @param sip call
 */
int SIPSessionReinvite(SIPCall *);

Emmanuel Milou's avatar
Emmanuel Milou committed
232
233
234
/*
 * Transfer callbacks
 */
235
void transfer_client_cb (pjsip_evsub *sub, pjsip_event *event);
236
237
238
239
240
void transfer_server_cb (pjsip_evsub *sub, pjsip_event *event);

/**
 * Helper function to process refer function on call transfer
 */
241
void onCallTransfered (pjsip_inv_session *inv, pjsip_rx_data *rdata);
Emmanuel Milou's avatar
Emmanuel Milou committed
242

243
244
245
246
247
248
249
std::string loadSIPLocalIP()
{
    pj_sockaddr ip_addr;
    if (pj_gethostip (pj_AF_INET(), &ip_addr) == PJ_SUCCESS)
        return pj_inet_ntoa (ip_addr.ipv4.sin_addr);
    return "";
}
250
251
} // end anonymous namespace

Emmanuel Milou's avatar
Emmanuel Milou committed
252
253
/*************************************************************************************************/

254
SIPVoIPLink* SIPVoIPLink::instance_ = NULL;
Emmanuel Milou's avatar
Emmanuel Milou committed
255

256

257
258
SIPVoIPLink::SIPVoIPLink() :
    evThread_(new EventThread(this))
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
259
{
260
    srand (time (NULL));    // to get random number for RANDOM_PORT
Alexandre Savard's avatar
Alexandre Savard committed
261

262
263
    /* Initialize the pjsip library */
    pjsipInit();
yanmorin's avatar
   
yanmorin committed
264
}
jpbl's avatar
jpbl committed
265

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
266
267
SIPVoIPLink::~SIPVoIPLink()
{
268
	delete evThread_;
269
	pjsipShutdown();
270
271
}

272
SIPVoIPLink* SIPVoIPLink::instance ()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
273
{
274
275
    if (!instance_)
        instance_ = new SIPVoIPLink;
Emmanuel Milou's avatar
Emmanuel Milou committed
276

277
    return instance_;
yanmorin's avatar
   
yanmorin committed
278
279
}

280
void SIPVoIPLink::init() {}
Emmanuel Milou's avatar
Emmanuel Milou committed
281

282
void SIPVoIPLink::terminate() {}
jpbl's avatar
jpbl committed
283

284
void
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
285
286
SIPVoIPLink::getEvent()
{
287
    // We have to register the external thread so it could access the pjsip frameworks
288
289
    if (!pj_thread_is_registered())
        pj_thread_register (NULL, desc, &thread);
yanmorin's avatar
   
yanmorin committed
290

291
292
293
294
    // PJSIP polling
    pj_time_val timeout = {0, 10};

    pjsip_endpt_handle_events (_endpt, &timeout);
295

296
}
297

298
void SIPVoIPLink::sendRegister (Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
299
{
300
301
    pjsip_tx_data *tdata;
    pjsip_hdr hdr_list;
302

303
    SIPAccount *account = static_cast<SIPAccount*>(a);
Emmanuel Milou's avatar
Emmanuel Milou committed
304

305
    // Create SIP transport or get existent SIP transport from internal map
306
    // according to account settings, if the transport could not be created but
307
308
    // one is already set in account, use this one (most likely this is the
    // transport we tried to create)
309
    acquireTransport (account);
Emmanuel Milou's avatar
Emmanuel Milou committed
310

311
    if (account->getAccountTransport()) {
312
313
314
315
        _debug ("Acquire transport in account registration: %s %s (refcnt=%d)",
                account->getAccountTransport()->obj_name,
                account->getAccountTransport()->info,
                (int) pj_atomic_get (account->getAccountTransport()->ref_cnt));
316
    }
317

318
    pjsip_regc *regc;
Rafaël Carré's avatar
Rafaël Carré committed
319
	ost::MutexLock m(mutexSIP_);
Emmanuel Milou's avatar
Emmanuel Milou committed
320

Rafaël Carré's avatar
Rafaël Carré committed
321
322
323
	// Get the client registration information for this particular account
	regc = account->getRegistrationInfo();
	account->setRegister (true);
324

Rafaël Carré's avatar
Rafaël Carré committed
325
326
327
328
329
330
	// Set the expire value of the message from the config file
	std::istringstream stream (account->getRegistrationExpire());
	int expire_value;
	stream >> expire_value;
	if (!expire_value)
		expire_value = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
331

Rafaël Carré's avatar
Rafaël Carré committed
332
333
	// Update the state of the voip link
	account->setRegistrationState (Trying);
334

Rafaël Carré's avatar
Rafaël Carré committed
335
336
337
	// Create the registration according to the account ID
	if (pjsip_regc_create (_endpt, (void *) account, &registration_cb, &regc) != PJ_SUCCESS)
		throw VoipLinkException("UserAgent: Unable to create regc structure.");
338

Rafaël Carré's avatar
Rafaël Carré committed
339
340
341
	// Creates URI
	std::string fromUri(account->getFromUri());
	std::string srvUri(account->getServerUri());
342

Rafaël Carré's avatar
Rafaël Carré committed
343
344
	std::string address(findLocalAddressFromUri(srvUri, account->getAccountTransport()));
	int port = findLocalPortFromUri(srvUri, account->getAccountTransport());
345

Rafaël Carré's avatar
Rafaël Carré committed
346
347
348
349
	std::stringstream ss;
	std::string portStr;
	ss << port;
	ss >> portStr;
350

Rafaël Carré's avatar
Rafaël Carré committed
351
	std::string contactUri(account->getContactHeader(address, portStr));
352

Rafaël Carré's avatar
Rafaël Carré committed
353
354
355
356
	_debug ("UserAgent: sendRegister: fromUri: %s serverUri: %s contactUri: %s",
			fromUri.c_str(),
			srvUri.c_str(),
			contactUri.c_str());
357

Rafaël Carré's avatar
Rafaël Carré committed
358
359
	pj_str_t pjFrom;
	pj_cstr (&pjFrom, fromUri.c_str());
360

Rafaël Carré's avatar
Rafaël Carré committed
361
362
	pj_str_t pjContact;
	pj_cstr (&pjContact, contactUri.c_str());
363

Rafaël Carré's avatar
Rafaël Carré committed
364
365
	pj_str_t pjSrv;
	pj_cstr (&pjSrv, srvUri.c_str());
366

Rafaël Carré's avatar
Rafaël Carré committed
367
368
369
	// Initializes registration
	if (pjsip_regc_init (regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, expire_value) != PJ_SUCCESS)
		throw VoipLinkException("Unable to initialize account registration structure");
370

Rafaël Carré's avatar
Rafaël Carré committed
371
372
373
374
375
	// Fill route set
	if (!account->getServiceRoute().empty()) {
		pjsip_route_hdr *route_set = createRouteSet(account, _pool);
		pjsip_regc_set_route_set (regc, route_set);
	}
376

Rafaël Carré's avatar
Rafaël Carré committed
377
378
379
	unsigned count = account->getCredentialCount();
	pjsip_cred_info *info = account->getCredInfo();
	pjsip_regc_set_credentials (regc, count, info);
jpbl's avatar
jpbl committed
380

Rafaël Carré's avatar
Rafaël Carré committed
381
382
	// Add User-Agent Header
	pj_list_init (&hdr_list);
383

Rafaël Carré's avatar
Rafaël Carré committed
384
385
386
	const std::string agent(account->getUserAgentName());
	pj_str_t useragent = pj_str ((char*)agent.c_str());
	pjsip_generic_string_hdr *h = pjsip_generic_string_hdr_create (_pool, &STR_USER_AGENT, &useragent);
387

Rafaël Carré's avatar
Rafaël Carré committed
388
389
	pj_list_push_back (&hdr_list, (pjsip_hdr*) h);
	pjsip_regc_add_headers (regc, &hdr_list);
jpbl's avatar
jpbl committed
390

Rafaël Carré's avatar
Rafaël Carré committed
391
392
	if (pjsip_regc_register (regc, PJ_TRUE, &tdata) != PJ_SUCCESS)
		throw VoipLinkException("Unable to initialize transaction data for account registration");
Emmanuel Milou's avatar
Emmanuel Milou committed
393

Rafaël Carré's avatar
Rafaël Carré committed
394
	pjsip_tpselector *tp = initTransportSelector (account->getAccountTransport(), _pool);
395

Rafaël Carré's avatar
Rafaël Carré committed
396
397
	// pjsip_regc_set_transport increments transport ref count by one
	pj_status_t status = pjsip_regc_set_transport (regc, tp);
Emmanuel Milou's avatar
Emmanuel Milou committed
398

Rafaël Carré's avatar
Rafaël Carré committed
399
400
401
402
	if (account->getAccountTransport()) {
		// decrease transport's ref count, counter icrementation is
		// managed when acquiring transport
		pjsip_transport_dec_ref (account->getAccountTransport ());
403

Rafaël Carré's avatar
Rafaël Carré committed
404
405
406
407
408
		_debug ("UserAgent: After setting the transport in account registration using transport: %s %s (refcnt=%d)",
				account->getAccountTransport()->obj_name,
				account->getAccountTransport()->info,
				(int) pj_atomic_get (account->getAccountTransport()->ref_cnt));
	}
409

Rafaël Carré's avatar
Rafaël Carré committed
410
411
	if (status != PJ_SUCCESS)
		throw VoipLinkException("Unable to set transport");
412

Rafaël Carré's avatar
Rafaël Carré committed
413
414
415
	// Send registration request
	// pjsip_regc_send increment the transport ref count by one,
	status = pjsip_regc_send (regc, tdata);
416

Rafaël Carré's avatar
Rafaël Carré committed
417
418
419
420
421
422
	if (account->getAccountTransport()) {
		// Decrease transport's ref count, since coresponding reference counter decrementation
		// is performed in pjsip_regc_destroy. This function is never called in SFLphone as the
		// regc data structure is permanently associated to the account at first registration.
		pjsip_transport_dec_ref (account->getAccountTransport ());
	}
423

Rafaël Carré's avatar
Rafaël Carré committed
424
425
	if (status != PJ_SUCCESS)
		throw VoipLinkException("Unable to send account registration request");
426

427
428
    account->setRegistrationInfo (regc);

429
430
    pjsip_transport *transport = account->getAccountTransport();
    if (transport)
431
        _debug ("Sent account registration using transport: %s %s (refcnt=%d)",
432
                transport->obj_name, transport->info, (int) pj_atomic_get(transport->ref_cnt));
433
434
}

435
void SIPVoIPLink::sendUnregister (Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
436
{
437
    SIPAccount *account = dynamic_cast<SIPAccount *>(a);
438

439
    // If an transport is attached to this account, detach it and decrease reference counter
440
441
442
443
444
    if (account->getAccountTransport()) {
        _debug ("Sent account unregistration using transport: %s %s (refcnt=%d)",
                account->getAccountTransport()->obj_name,
                account->getAccountTransport()->info,
                (int) pj_atomic_get (account->getAccountTransport()->ref_cnt));
445
446
447
448

    }

    // This may occurs if account failed to register and is in state INVALID
449
450
    if (!account->isRegister()) {
        account->setRegistrationState (Unregistered);
451
        return;
452
453
    }

454
    pjsip_regc *regc = account->getRegistrationInfo();
455
    if (!regc)
456
        throw VoipLinkException("Registration structure is NULL");
457

458
    pjsip_tx_data *tdata = NULL;
459
    if (pjsip_regc_unregister (regc, &tdata) != PJ_SUCCESS)
460
        throw VoipLinkException("Unable to unregister sip account");
461

462
    if (pjsip_regc_send (regc, tdata) != PJ_SUCCESS)
463
        throw VoipLinkException("Unable to send request to unregister sip account");
464

465
    account->setRegister (false);
jpbl's avatar
jpbl committed
466
467
}

468
Call *SIPVoIPLink::newOutgoingCall (const std::string& id, const std::string& toUrl)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
469
{
470
    // Create a new SIP call
471
    SIPCall* call = new SIPCall (id, Call::Outgoing, _cp);
Yun Liu's avatar
Yun Liu committed
472

473
    // Find the account associated to this call
474
    SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (Manager::instance().getAccountFromCall (id)));
475
476
477
478
479
    if (account == NULL) {
    	_error ("UserAgent: Error: Could not retrieving account to make call with");
    	call->setConnectionState (Call::Disconnected);
    	call->setState (Call::Error);
    	delete call;
480
481
    	// TODO: We should investigate how we could get rid of this error and create a IP2IP call instead
    	throw VoipLinkException("Could not get account for this call");
482
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
483

484
485
    // If toUri is not a well formated sip URI, use account information to process it
    std::string toUri;
486
    if((toUrl.find("sip:") != std::string::npos) or
487
    		toUrl.find("sips:") != std::string::npos)
488
    	toUri = toUrl;
489
    else
490
        toUri = account->getToUri (toUrl);
491

492
    call->setPeerNumber (toUri);
493
    _debug ("UserAgent: New outgoing call %s to %s", id.c_str(), toUri.c_str());
494

495
    std::string localAddr(getInterfaceAddrFromName (account->getLocalInterface ()));
496
    _debug ("UserAgent: Local address for thi call: %s", localAddr.c_str());
497

498
    if (localAddr == "0.0.0.0")
499
    	localAddr = loadSIPLocalIP();
500

501
    setCallMediaLocal (call, localAddr);
Emmanuel Milou's avatar
Emmanuel Milou committed
502

503
    // May use the published address as well
504
505
    std::string addrSdp = account->isStunEnabled() ?
        account->getPublishedAddress() :
506
        getInterfaceAddrFromName(account->getLocalInterface());
507

508
    if (addrSdp == "0.0.0.0")
509
		addrSdp = loadSIPLocalIP();
Emmanuel Milou's avatar
Emmanuel Milou committed
510

511
512
513
    // Initialize the session using ULAW as default codec in case of early media
    // The session should be ready to receive media once the first INVITE is sent, before
    // the session initialization is completed
514
    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (PAYLOAD_CODEC_ULAW);
515
516
    if (audiocodec == NULL) {
    	_error ("UserAgent: Could not instantiate codec");
517
518
    	delete call;
    	throw VoipLinkException ("Could not instantiate codec for early media");
519
    }
520

521
522
	try {
		_info ("UserAgent: Creating new rtp session");
523
524
525
		call->getAudioRtp()->initAudioRtpConfig ();
		call->getAudioRtp()->initAudioSymmetricRtpSession ();
		call->getAudioRtp()->initLocalCryptoInfo ();
526
		_info ("UserAgent: Start audio rtp session");
527
		call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
528
	} catch (...) {
529
        delete call;
530
        throw VoipLinkException ("Could not start rtp session for early media");
531
	}
532

533
534
	// init file name according to peer phone number
	call->initRecFileName (toUrl);
535

536
	// Building the local SDP offer
537
	call->getLocalSDP()->setLocalIP (addrSdp);
Rafaël Carré's avatar
Rafaël Carré committed
538
	call->getLocalSDP()->createOffer(account->getActiveCodecs());
539

Rafaël Carré's avatar
Rafaël Carré committed
540
	if (!SIPStartCall(call)) {
541
		delete call;
542
		throw VoipLinkException("Could not send outgoing INVITE request for new call");
543
	}
jpbl's avatar
jpbl committed
544

545
	return call;
546
}
547

548
void
549
SIPVoIPLink::answer (Call *c)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
550
{
551
    pjsip_tx_data *tdata;
552

553
    _debug ("UserAgent: Answering call");
554

555
    SIPCall *call = dynamic_cast<SIPCall*>(c);
Emmanuel Lepage's avatar
Emmanuel Lepage committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    if (call != NULL) {
       pjsip_inv_session *inv_session = call->inv;

       _debug ("UserAgent: SDP negotiation success! : call %s ", call->getCallId().c_str());
       // Create and send a 200(OK) response
       if (pjsip_inv_answer (inv_session, PJSIP_SC_OK, NULL, NULL, &tdata) != PJ_SUCCESS)
       throw VoipLinkException("Could not init invite request answer (200 OK)");
 
       if (pjsip_inv_send_msg (inv_session, tdata) != PJ_SUCCESS)
       throw VoipLinkException("Could not send invite request answer (200 OK)");

       call->setConnectionState (Call::Connected);
       call->setState (Call::Active);
    }
Yun Liu's avatar
Yun Liu committed
570
571
}

572
void
573
SIPVoIPLink::hangup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
574
{
575
    SIPCall* call = getSIPCall(id);
576

577
    std::string account_id(Manager::instance().getAccountFromCall(id));
578
    SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (account_id));
579
    if (account == NULL)
580
581
    	throw VoipLinkException("Could not find account for this call");

582
    pjsip_inv_session *inv = call->inv;
583
    if (inv == NULL)
584
        throw VoipLinkException("No invite session for this call");
585

586
    // Looks for sip routes
587
    if (not (account->getServiceRoute().empty())) {
588
        pjsip_route_hdr *route_set = createRouteSet(account, inv->pool);
589
590
        pjsip_dlg_set_route_set (inv->dlg, route_set);
    }
591

592
    pjsip_tx_data *tdata = NULL;
593
    // User hangup current call. Notify peer
594
595
    if (pjsip_inv_end_session (inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
        return;
596

597
598
    if (pjsip_inv_send_msg (inv, tdata) != PJ_SUCCESS)
        return;
599

600
601
    // Make sure user data is NULL in callbacks
    inv->mod_data[getModId()] = NULL;
602

Rafaël Carré's avatar
Rafaël Carré committed
603
604
	if (Manager::instance().isCurrentCall (id))
		call->getAudioRtp()->stop();
605
606

    removeCall (id);
alexandresavard's avatar
alexandresavard committed
607
608
}

609
void
610
SIPVoIPLink::peerHungup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
611
{
612
    SIPCall* call = getSIPCall(id);
Alexandre Savard's avatar
Alexandre Savard committed
613

614
    // User hangup current call. Notify peer
615
    pjsip_tx_data *tdata = NULL;
616
    if (pjsip_inv_end_session (call->inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
617
        return;
618

619
    if (pjsip_inv_send_msg (call->inv, tdata) != PJ_SUCCESS)
620
        return;
621

622
    // Make sure user data is NULL in callbacks
623
    call->inv->mod_data[getModId() ] = NULL;
624

Rafaël Carré's avatar
Rafaël Carré committed
625
626
	if (Manager::instance().isCurrentCall (id))
		call->getAudioRtp()->stop();
627

628
    removeCall (id);
629
630
}

631
void
632
SIPVoIPLink::onhold (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
633
{
634
    SIPCall *call = getSIPCall(id);
635
    call->setState (Call::Hold);
Rafaël Carré's avatar
Rafaël Carré committed
636
	call->getAudioRtp()->stop();
637

638
639
	Sdp *sdpSession = call->getLocalSDP();
    if (!sdpSession)
640
    	throw VoipLinkException("Could not find sdp session");
641
642
643
644
645

    sdpSession->removeAttributeFromLocalAudioMedia("sendrecv");
    sdpSession->removeAttributeFromLocalAudioMedia("sendonly");
    sdpSession->addAttributeToLocalAudioMedia("sendonly");

646
    SIPSessionReinvite (call);
647
648
}

649
void
650
SIPVoIPLink::offhold (const std::string& id)
651
{
652
    SIPCall *call = getSIPCall (id);
653

654
	Sdp *sdpSession = call->getLocalSDP();
655
    if (sdpSession == NULL)
656
    	throw VoipLinkException("Could not find sdp session");
657

658
659
    try {
        // Retreive previously selected codec
660
        int pl;
661
662
663
664
665
        sfl::Codec *sessionMedia = sdpSession->getSessionMedia();
        if (sessionMedia == NULL) {
    	    _warn("UserAgent: Session media not yet initialized, using default (ULAW)");
    	    pl = PAYLOAD_CODEC_ULAW;
        }
666
        else
667
    	    pl = (int) sessionMedia->getPayloadType();
668

669
        _debug ("UserAgent: Payload from session media %d", pl);
670

671

672
        // Create a new instance for this codec
673
        sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (pl);
674
        if (audiocodec == NULL)
675
    	    throw VoipLinkException("Could not instantiate codec");
676

677
678
        call->getAudioRtp()->initAudioRtpConfig ();
        call->getAudioRtp()->initAudioSymmetricRtpSession ();
679
        call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
680

681
    }
682
    catch (const SdpException &e) {
683
684
685
    	_error("UserAgent: Exception: %s", e.what());
    } 
    catch (...) {
686
    	throw VoipLinkException("Could not create audio rtp session");
687
688
689
690
691
692
    }

    sdpSession->removeAttributeFromLocalAudioMedia("sendrecv");
    sdpSession->removeAttributeFromLocalAudioMedia("sendonly");

    sdpSession->addAttributeToLocalAudioMedia("sendrecv");
693

694
    /* Create re-INVITE with new offer */
695
    if (SIPSessionReinvite (call) != PJ_SUCCESS)
696
        return;
697
698

    call->setState (Call::Active);
699
700
}

Rafaël Carré's avatar
Rafaël Carré committed
701
void
702
SIPVoIPLink::sendTextMessage (sfl::InstantMessaging *module, const std::string& callID, const std::string& message, const std::string& from)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
703
{
704
705
706
707
708
    SIPCall *call;
    try {
        call = getSIPCall (callID);
    }
    catch (const VoipLinkException &e) {
Rafaël Carré's avatar
Rafaël Carré committed
709
        return;
710
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
711

712
713
714
715
716
717
718
	/* Send IM message */
	sfl::InstantMessaging::UriList list;
	sfl::InstantMessaging::UriEntry entry;
	entry[sfl::IM_XML_URI] = std::string ("\"" + from + "\""); // add double quotes for xml formating

	list.push_front (entry);

Rafaël Carré's avatar
Rafaël Carré committed
719
	module->send_sip_message (call->inv, callID, module->appendUriList (message, list));
720
721
}

722
bool
723
SIPVoIPLink::transfer (const std::string& id, const std::string& to)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
724
{
725
    SIPCall *call = getSIPCall(id);
726

727
    call->stopRecording();
728

729
730
731
    std::string account_id(Manager::instance().getAccountFromCall(id));
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount (account_id));
    if (account == NULL)
732
    	throw VoipLinkException("Could not find account");
733

734
735
    std::string dest;
    pj_str_t pjDest;
736

737
    if (to.find ("@") == std::string::npos) {
738
        dest = account->getToUri(to);
739
        pj_cstr (&pjDest, dest.c_str());
740
    }
741

742
    _info ("UserAgent: Transfering to %s", dest.c_str());
743

744
    pjsip_evsub_user xfer_cb;
745
746
    /* Create xfer client subscription. */
    pj_bzero (&xfer_cb, sizeof (xfer_cb));
747
    xfer_cb.on_evsub_state = &transfer_client_cb;
748

749
    pjsip_evsub *sub;
750
    pj_status_t status = pjsip_xfer_create_uac (call->inv->dlg, &xfer_cb, &sub);
751
    if (status != PJ_SUCCESS)
752
    	throw VoipLinkException("Could not create xfer request");
753

754
755
    /* Associate this voiplink of call with the client subscription
     * We can not just associate call with the client subscription
756
     * because after this function, we can no find the cooresponding
757
758
759
     * voiplink from the call any more. But the voiplink is useful!
     */
    pjsip_evsub_set_mod_data (sub, getModId(), this);
760

761
762
763
    /*
     * Create REFER request.
     */
764
765
    pjsip_tx_data *tdata;

766
    status = pjsip_xfer_initiate (sub, &pjDest, &tdata);
767
    if (status != PJ_SUCCESS)
768
    	throw VoipLinkException("Could not create REFER request");
769

770
    // Put SIP call id in map in order to retrieve call during transfer callback
771
    std::string callidtransfer(call->inv->dlg->call_id->id.ptr, call->inv->dlg->call_id->id.slen);
772
    transferCallID[callidtransfer] = call->getCallId();