sipvoiplink.cpp 67.3 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
#include "pjsip/sip_uri.h"
#include <pjnath.h>
62

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

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

74
75
#include <map>

76
77
using namespace sfl;

78
namespace {
79
/** The default transport (5060) */
80
static pjsip_transport *_localUDPTransport = NULL;
81
82
83

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

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

88
/**
89
90
91
92
 * 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
93
void setCallMediaLocal (SIPCall* call, const std::string &localIP);
94

95
96
97
98
/**
 * Helper function to parser header from incoming sip messages
 */
std::string fetchHeaderValue (pjsip_msg *msg, std::string field);
99

100
101
102
103
104
static pj_caching_pool pool_cache, *_cp = &pool_cache;
static pj_pool_t *_pool;
static pjsip_endpoint *_endpt;
static pjsip_module _mod_ua;
static pj_thread_t *thread;
Emmanuel Milou's avatar
Emmanuel Milou committed
105

106
107
108
109
110
111
112
113
114
static void sdp_media_update_cb (pjsip_inv_session *inv, pj_status_t status UNUSED);
static void sdp_request_offer_cb (pjsip_inv_session *inv, const pjmedia_sdp_session *offer);
static void sdp_create_offer_cb (pjsip_inv_session *inv, pjmedia_sdp_session **p_offer);
static void invite_session_state_changed_cb (pjsip_inv_session *inv, pjsip_event *e);
static void outgoing_request_forked_cb (pjsip_inv_session *inv, pjsip_event *e);
static void transaction_state_changed_cb (pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
static void registration_cb (struct pjsip_regc_cbparam *param);
static pj_bool_t transaction_request_cb (pjsip_rx_data *rdata);
static pj_bool_t transaction_response_cb (pjsip_rx_data *rdata UNUSED) ;
Emmanuel Milou's avatar
Emmanuel Milou committed
115

116
static void transfer_client_cb (pjsip_evsub *sub, pjsip_event *event);
Emmanuel Milou's avatar
Emmanuel Milou committed
117

118
119
120
121
122
123
124
/**
 * 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 *);

125
126
127
/**
 * Helper function to process refer function on call transfer
 */
128
void onCallTransfered (pjsip_inv_session *inv, pjsip_rx_data *rdata);
Emmanuel Milou's avatar
Emmanuel Milou committed
129

130
131
132
133
134
135
136
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 "";
}
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

pjsip_route_hdr *createRouteSet(const std::string &route, pj_pool_t *hdr_pool)
{
	int port = 0;
	std::string host;

    size_t found = route.find(":");
    if (found != std::string::npos) {
		host = route.substr(0, found);
		port = atoi(route.substr(found + 1, route.length()).c_str());
	}
	else {
		host = route;
	}

	pjsip_route_hdr *route_set = pjsip_route_hdr_create (hdr_pool);
    pjsip_route_hdr *routing = pjsip_route_hdr_create (hdr_pool);
    pjsip_sip_uri *url = pjsip_sip_uri_create (hdr_pool, 0);
    routing->name_addr.uri = (pjsip_uri*) url;
    pj_strdup2(hdr_pool, &url->host, host.c_str());
    url->port = port;

    pj_list_push_back(route_set, pjsip_hdr_clone (hdr_pool, routing));

    return route_set;
}

164
165
} // end anonymous namespace

Emmanuel Milou's avatar
Emmanuel Milou committed
166
167
/*************************************************************************************************/

168
169
170
171
172
173
174
175
SIPVoIPLink::SIPVoIPLink() : evThread_(new EventThread(this))
{
#define TRY(ret) do { \
		if (ret != PJ_SUCCESS) \
			throw VoipLinkException(#ret " failed"); \
		} while(0)

    srand(time(NULL)); // to get random number for RANDOM_PORT
Emmanuel Milou's avatar
Emmanuel Milou committed
176

177
178
179
180
    TRY(pj_init());
    TRY(pjlib_util_init());
    pj_log_set_level(6);    // From 0 (min) to 6 (max)
    TRY(pjnath_init());
181

182
183
184
185
    pj_caching_pool_init (_cp, &pj_pool_factory_default_policy, 0);
    _pool = pj_pool_create (&_cp->factory, "sflphone", 4000, 4000, NULL);
    if (!_pool)
        throw VoipLinkException("UserAgent: Could not initialize memory pool");
Alexandre Savard's avatar
Alexandre Savard committed
186

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
    TRY(pjsip_endpt_create (&_cp->factory, pj_gethostname()->ptr, &_endpt));
    if (loadSIPLocalIP().empty())
        throw VoipLinkException("UserAgent: Unable to determine network capabilities");

    TRY(pjsip_tsx_layer_init_module(_endpt));
    TRY(pjsip_ua_init_module(_endpt, NULL));
    TRY(pjsip_replaces_init_module(_endpt)); // See the Replaces specification in RFC 3891
    TRY(pjsip_100rel_init_module(_endpt));

    // Initialize and register sflphone module
    _mod_ua.name = pj_str ( (char*) PACKAGE);
    _mod_ua.id = -1;
    _mod_ua.priority = PJSIP_MOD_PRIORITY_APPLICATION;
    _mod_ua.on_rx_request = &transaction_request_cb;
    _mod_ua.on_rx_response = &transaction_response_cb;
    TRY(pjsip_endpt_register_module(_endpt, &_mod_ua));

    TRY(pjsip_evsub_init_module(_endpt));
    TRY(pjsip_xfer_init_module(_endpt));

    static const pjsip_inv_callback inv_cb = {
    		invite_session_state_changed_cb,
    		outgoing_request_forked_cb,
    		transaction_state_changed_cb,
    		sdp_request_offer_cb,
    		sdp_create_offer_cb,
    		sdp_media_update_cb,
    		NULL,
    		NULL,
    };
    TRY(pjsip_inv_usage_init(_endpt, &inv_cb));

    static const pj_str_t allowed[] = { { (char*) "INFO", 4}, { (char*) "REGISTER", 8}, { (char*) "OPTIONS", 7}, { (char*) "MESSAGE", 7 } };       //  //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}
    pjsip_endpt_add_capability (_endpt, &_mod_ua, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE (allowed), allowed);

    static const pj_str_t text_plain = { (char*) "text/plain", 10 };
    pjsip_endpt_add_capability (_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &text_plain);

    static const pj_str_t accepted = { (char*) "application/sdp", 15 };
    pjsip_endpt_add_capability (_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted);

    _debug ("UserAgent: pjsip version %s for %s initialized", pj_get_version(), PJ_OS_NAME);

    TRY(pjsip_replaces_init_module(_endpt));

    evThread_->start();
yanmorin's avatar
   
yanmorin committed
233
}
jpbl's avatar
jpbl committed
234

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
235
236
SIPVoIPLink::~SIPVoIPLink()
{
237
	delete evThread_;
238
239
240
241
242
243
244
245
246
247
248
    pj_thread_join (thread);
    pj_thread_destroy (thread);

    const pj_time_val tv = {0, 10};
	pjsip_endpt_handle_events(_endpt, &tv);
	pjsip_endpt_destroy (_endpt);

	pj_pool_release (_pool);
	pj_caching_pool_destroy (_cp);

    pj_shutdown();
249
250
}

251
SIPVoIPLink* SIPVoIPLink::instance()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
252
{
253
254
255
	static SIPVoIPLink* instance = NULL;
    if (!instance)
        instance = new SIPVoIPLink;
Emmanuel Milou's avatar
Emmanuel Milou committed
256

257
    return instance;
yanmorin's avatar
   
yanmorin committed
258
259
}

260
void SIPVoIPLink::init() {}
Emmanuel Milou's avatar
Emmanuel Milou committed
261

262
void SIPVoIPLink::terminate() {}
jpbl's avatar
jpbl committed
263

264
void
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
265
266
SIPVoIPLink::getEvent()
{
267
268
	static pj_thread_desc desc;

269
    // We have to register the external thread so it could access the pjsip frameworks
270
    if (!pj_thread_is_registered())
271
        pj_thread_register(NULL, desc, &thread);
yanmorin's avatar
   
yanmorin committed
272

273
274
275
    // PJSIP polling
    pj_time_val timeout = {0, 10};

276
    pjsip_endpt_handle_events(_endpt, &timeout);
277
}
278

279
void SIPVoIPLink::sendRegister (Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
280
{
281
    SIPAccount *account = static_cast<SIPAccount*>(a);
Emmanuel Milou's avatar
Emmanuel Milou committed
282

283
284
    if (account->transport)
        pjsip_transport_dec_ref(account->transport);
285

286
    createSipTransport(account);
287
288
289
290
291
292
293
294
295
296
297

	if (!account->transport) {
		// Could not create new transport, this transport may already exists
		account->transport = transportMap_[account->getLocalPort()];
		if (account->transport) {
			pjsip_transport_add_ref(account->transport);
		} else {
			account->transport = _localUDPTransport;
			account->setLocalPort(_localUDPTransport->local_name.port);
		}
	}
298

299
	account->setRegister(true);
Rafaël Carré's avatar
Rafaël Carré committed
300
	account->setRegistrationState (Trying);
301

302
    pjsip_regc *regc = account->getRegistrationInfo();
303
	if (pjsip_regc_create(_endpt, (void *) account, &registration_cb, &regc) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
304
		throw VoipLinkException("UserAgent: Unable to create regc structure.");
305

Rafaël Carré's avatar
Rafaël Carré committed
306
	std::string srvUri(account->getServerUri());
307

308
309
	std::string address, port;
	findLocalAddressFromUri(srvUri, account->transport, address, port);
310

311
312
313
314
315
	std::string from(account->getFromUri());
	pj_str_t pjFrom = pj_str((char*)from.c_str());
	std::string contact(account->getContactHeader(address, port));
	pj_str_t pjContact = pj_str((char*)contact.c_str());
	pj_str_t pjSrv = pj_str((char*)srvUri.c_str());
316

317
318
319
320
321
	int expire_value = atoi(account->getRegistrationExpire().c_str());
	if (!expire_value)
		expire_value = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;


Rafaël Carré's avatar
Rafaël Carré committed
322
323
	if (pjsip_regc_init (regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, expire_value) != PJ_SUCCESS)
		throw VoipLinkException("Unable to initialize account registration structure");
324

325
326
327
328
	if (!account->getServiceRoute().empty())
		pjsip_regc_set_route_set (regc, createRouteSet(account->getServiceRoute(), _pool));

	pjsip_regc_set_credentials (regc, account->getCredentialCount(), account->getCredInfo());
329

jpbl's avatar
jpbl committed
330

331
    pjsip_hdr hdr_list;
Rafaël Carré's avatar
Rafaël Carré committed
332
	pj_list_init (&hdr_list);
333
334
	std::string useragent(account->getUserAgentName());
	pj_str_t pJuseragent = pj_str ((char*)useragent.c_str());
335
	const pj_str_t STR_USER_AGENT = { (char*) "User-Agent", 10 };
336

337
	pjsip_generic_string_hdr *h = pjsip_generic_string_hdr_create(_pool, &STR_USER_AGENT, &pJuseragent);
Rafaël Carré's avatar
Rafaël Carré committed
338
339
	pj_list_push_back (&hdr_list, (pjsip_hdr*) h);
	pjsip_regc_add_headers (regc, &hdr_list);
jpbl's avatar
jpbl committed
340

341

342
    pjsip_tx_data *tdata;
Rafaël Carré's avatar
Rafaël Carré committed
343
344
	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
345

346
347
	if (pjsip_regc_set_transport (regc, initTransportSelector (account->transport, _pool)) != PJ_SUCCESS)
		throw VoipLinkException("Unable to set transport");
Emmanuel Milou's avatar
Emmanuel Milou committed
348

349
	// decrease transport's ref count, counter incrementation is managed when acquiring transport
350
	pjsip_transport_dec_ref(account->transport);
351

Rafaël Carré's avatar
Rafaël Carré committed
352
	// pjsip_regc_send increment the transport ref count by one,
353
354
	if (pjsip_regc_send(regc, tdata) != PJ_SUCCESS)
		throw VoipLinkException("Unable to send account registration request");
355

356
357
358
	// 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.
359
	pjsip_transport_dec_ref (account->transport);
360

361
    account->setRegistrationInfo (regc);
362
363
}

364
void SIPVoIPLink::sendUnregister (Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
365
{
366
    SIPAccount *account = dynamic_cast<SIPAccount *>(a);
367

368
    // This may occurs if account failed to register and is in state INVALID
369
370
    if (!account->isRegister()) {
        account->setRegistrationState (Unregistered);
371
        return;
372
373
    }

374
    pjsip_regc *regc = account->getRegistrationInfo();
375
    if (!regc)
376
        throw VoipLinkException("Registration structure is NULL");
377

378
    pjsip_tx_data *tdata = NULL;
379
    if (pjsip_regc_unregister (regc, &tdata) != PJ_SUCCESS)
380
        throw VoipLinkException("Unable to unregister sip account");
381

382
    if (pjsip_regc_send (regc, tdata) != PJ_SUCCESS)
383
        throw VoipLinkException("Unable to send request to unregister sip account");
384

385
    account->setRegister (false);
jpbl's avatar
jpbl committed
386
387
}

388
Call *SIPVoIPLink::newOutgoingCall (const std::string& id, const std::string& toUrl)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
389
{
390
    SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (Manager::instance().getAccountFromCall (id)));
391
    if (account == NULL) // TODO: We should investigate how we could get rid of this error and create a IP2IP call instead
392
    	throw VoipLinkException("Could not get account for this call");
393
394

    SIPCall* call = new SIPCall (id, Call::Outgoing, _cp);
Emmanuel Milou's avatar
Emmanuel Milou committed
395

396
397
    // If toUri is not a well formated sip URI, use account information to process it
    std::string toUri;
398
    if((toUrl.find("sip:") != std::string::npos) or
399
    		toUrl.find("sips:") != std::string::npos)
400
    	toUri = toUrl;
401
    else
402
        toUri = account->getToUri(toUrl);
403

404
    call->setPeerNumber (toUri);
405
    std::string localAddr(getInterfaceAddrFromName (account->getLocalInterface ()));
406

407
    if (localAddr == "0.0.0.0")
408
    	localAddr = loadSIPLocalIP();
409

410
    setCallMediaLocal (call, localAddr);
Emmanuel Milou's avatar
Emmanuel Milou committed
411

412
    // May use the published address as well
413
414
    std::string addrSdp = account->isStunEnabled() ?
        account->getPublishedAddress() :
415
        getInterfaceAddrFromName(account->getLocalInterface());
416

417
    if (addrSdp == "0.0.0.0")
418
		addrSdp = loadSIPLocalIP();
Emmanuel Milou's avatar
Emmanuel Milou committed
419

420
421
422
    // 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
423
    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (PAYLOAD_CODEC_ULAW);
424
    if (audiocodec == NULL) {
425
426
    	delete call;
    	throw VoipLinkException ("Could not instantiate codec for early media");
427
    }
428

429
	try {
430
431
432
		call->getAudioRtp()->initAudioRtpConfig ();
		call->getAudioRtp()->initAudioSymmetricRtpSession ();
		call->getAudioRtp()->initLocalCryptoInfo ();
433
		call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
434
	} catch (...) {
435
        delete call;
436
        throw VoipLinkException ("Could not start rtp session for early media");
437
	}
438

439
	call->initRecFileName(toUrl);
440

441
	call->getLocalSDP()->setLocalIP (addrSdp);
Rafaël Carré's avatar
Rafaël Carré committed
442
	call->getLocalSDP()->createOffer(account->getActiveCodecs());
443

Rafaël Carré's avatar
Rafaël Carré committed
444
	if (!SIPStartCall(call)) {
445
		delete call;
446
		throw VoipLinkException("Could not send outgoing INVITE request for new call");
447
	}
jpbl's avatar
jpbl committed
448

449
	return call;
450
}
451

452
void
453
SIPVoIPLink::answer (Call *c)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
454
{
455
    SIPCall *call = dynamic_cast<SIPCall*>(c);
456
457
    if (!call)
    	return;
Emmanuel Lepage's avatar
Emmanuel Lepage committed
458

459
460
461
    pjsip_tx_data *tdata;
    if (pjsip_inv_answer (call->inv, PJSIP_SC_OK, NULL, NULL, &tdata) != PJ_SUCCESS)
	   throw VoipLinkException("Could not init invite request answer (200 OK)");
Emmanuel Lepage's avatar
Emmanuel Lepage committed
462

463
464
465
466
467
    if (pjsip_inv_send_msg (call->inv, 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
468
469
}

470
void
471
SIPVoIPLink::hangup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
472
{
473
    SIPCall* call = getSIPCall(id);
474

475
    std::string account_id(Manager::instance().getAccountFromCall(id));
476
    SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (account_id));
477
    if (account == NULL)
478
479
    	throw VoipLinkException("Could not find account for this call");

480
    pjsip_inv_session *inv = call->inv;
481
    if (inv == NULL)
482
        throw VoipLinkException("No invite session for this call");
483

484
    // Looks for sip routes
485
    if (not (account->getServiceRoute().empty())) {
486
        pjsip_route_hdr *route_set = createRouteSet(account->getServiceRoute(), inv->pool);
487
488
        pjsip_dlg_set_route_set (inv->dlg, route_set);
    }
489

490
    pjsip_tx_data *tdata = NULL;
491
    // User hangup current call. Notify peer
492
493
    if (pjsip_inv_end_session (inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
        return;
494

495
496
    if (pjsip_inv_send_msg (inv, tdata) != PJ_SUCCESS)
        return;
497

498
    // Make sure user data is NULL in callbacks
499
    inv->mod_data[_mod_ua.id] = NULL;
500

Rafaël Carré's avatar
Rafaël Carré committed
501
502
	if (Manager::instance().isCurrentCall (id))
		call->getAudioRtp()->stop();
503
504

    removeCall (id);
alexandresavard's avatar
alexandresavard committed
505
506
}

507
void
508
SIPVoIPLink::peerHungup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
509
{
510
    SIPCall* call = getSIPCall(id);
Alexandre Savard's avatar
Alexandre Savard committed
511

512
    // User hangup current call. Notify peer
513
    pjsip_tx_data *tdata = NULL;
514
    if (pjsip_inv_end_session (call->inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
515
        return;
516

517
    if (pjsip_inv_send_msg (call->inv, tdata) != PJ_SUCCESS)
518
        return;
519

520
    // Make sure user data is NULL in callbacks
521
    call->inv->mod_data[_mod_ua.id ] = NULL;
522

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

526
    removeCall (id);
527
528
}

529
void
530
SIPVoIPLink::onhold (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
531
{
532
    SIPCall *call = getSIPCall(id);
533
    call->setState (Call::Hold);
Rafaël Carré's avatar
Rafaël Carré committed
534
	call->getAudioRtp()->stop();
535

536
537
	Sdp *sdpSession = call->getLocalSDP();
    if (!sdpSession)
538
    	throw VoipLinkException("Could not find sdp session");
539
540
541
542
543

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

544
    SIPSessionReinvite (call);
545
546
}

547
void
548
SIPVoIPLink::offhold (const std::string& id)
549
{
550
    SIPCall *call = getSIPCall (id);
551

552
	Sdp *sdpSession = call->getLocalSDP();
553
    if (sdpSession == NULL)
554
    	throw VoipLinkException("Could not find sdp session");
555

556
    try {
557
        int pl = PAYLOAD_CODEC_ULAW;
558
        sfl::Codec *sessionMedia = sdpSession->getSessionMedia();
559
560
        if (sessionMedia)
    	    pl = sessionMedia->getPayloadType();
561

562
        // Create a new instance for this codec
563
        sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (pl);
564
        if (audiocodec == NULL)
565
    	    throw VoipLinkException("Could not instantiate codec");
566

567
568
        call->getAudioRtp()->initAudioRtpConfig ();
        call->getAudioRtp()->initAudioSymmetricRtpSession ();
569
        call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
570

571
    }
572
    catch (const SdpException &e) {
573
574
575
    	_error("UserAgent: Exception: %s", e.what());
    } 
    catch (...) {
576
    	throw VoipLinkException("Could not create audio rtp session");
577
578
579
580
581
582
    }

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

    sdpSession->addAttributeToLocalAudioMedia("sendrecv");
583

584
    /* Create re-INVITE with new offer */
585
586
    if (SIPSessionReinvite(call) == PJ_SUCCESS)
    	call->setState (Call::Active);
587
588
}

Rafaël Carré's avatar
Rafaël Carré committed
589
void
590
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
591
{
592
593
594
595
596
    SIPCall *call;
    try {
        call = getSIPCall (callID);
    }
    catch (const VoipLinkException &e) {
Rafaël Carré's avatar
Rafaël Carré committed
597
        return;
598
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
599

600
601
602
603
604
605
606
	/* 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
607
	module->send_sip_message (call->inv, callID, module->appendUriList (message, list));
608
609
}

610
void
611
SIPVoIPLink::transfer (const std::string& id, const std::string& to)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
612
{
613
    SIPCall *call = getSIPCall(id);
614

615
    call->stopRecording();
616

617
618
619
    std::string account_id(Manager::instance().getAccountFromCall(id));
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount (account_id));
    if (account == NULL)
620
    	throw VoipLinkException("Could not find account");
621

622
623
    std::string dest;
    pj_str_t pjDest;
624

625
    if (to.find ("@") == std::string::npos) {
626
        dest = account->getToUri(to);
627
        pj_cstr (&pjDest, dest.c_str());
628
    }
629

630
    pjsip_evsub_user xfer_cb;
631
    pj_bzero (&xfer_cb, sizeof (xfer_cb));
632
    xfer_cb.on_evsub_state = &transfer_client_cb;
633

634
    pjsip_evsub *sub;
635
    if (pjsip_xfer_create_uac (call->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
636
    	throw VoipLinkException("Could not create xfer request");
637

638
639
    /* Associate this voiplink of call with the client subscription
     * We can not just associate call with the client subscription
640
     * because after this function, we can no find the cooresponding
641
642
     * voiplink from the call any more. But the voiplink is useful!
     */
643
    pjsip_evsub_set_mod_data (sub, _mod_ua.id, this);
644

645
646
647
    /*
     * Create REFER request.
     */
648
649
    pjsip_tx_data *tdata;

650
    if (pjsip_xfer_initiate (sub, &pjDest, &tdata) != PJ_SUCCESS)
651
    	throw VoipLinkException("Could not create REFER request");
652

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

657
    /* Send. */
658
    if (pjsip_xfer_send_request (sub, tdata) != PJ_SUCCESS)
659
    	throw VoipLinkException("Could not send xfer request");
660
}
661

662
bool SIPVoIPLink::attendedTransfer(const std::string& transferId, const std::string& targetId)
663
664
{
	char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
665

666
	pjsip_dialog *target_dlg = getSIPCall(targetId)->inv->dlg;
667
668

    /* Print URI */
669
	pj_str_t str_dest = { NULL, 0 };
670
671
672
    str_dest_buf[0] = '<';
    str_dest.slen = 1;

673
	pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
674
675
676
    int len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
                              str_dest_buf+1, sizeof(str_dest_buf)-1);
    str_dest.slen += len;
677

678
679
    len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
    		               sizeof(str_dest_buf) - str_dest.slen,
680
681
    			           "?"
    		               "Replaces=%.*s"
682
683
684
685
686
687
688
689
690
691
692
693
    			           "%%3Bto-tag%%3D%.*s"
    			           "%%3Bfrom-tag%%3D%.*s>",
    			           (int)target_dlg->call_id->id.slen,
                           target_dlg->call_id->id.ptr,
                           (int)target_dlg->remote.info->tag.slen,
                           target_dlg->remote.info->tag.ptr,
                           (int)target_dlg->local.info->tag.slen,
                           target_dlg->local.info->tag.ptr);

    str_dest.ptr = str_dest_buf;
    str_dest.slen += len;

694
    SIPCall *transferCall = getSIPCall(transferId);
695
696

    /* Create xfer client subscription. */
697
	struct pjsip_evsub_user xfer_cb;
698
    pj_bzero (&xfer_cb, sizeof (xfer_cb));
699
    xfer_cb.on_evsub_state = &transfer_client_cb;
700
	pjsip_evsub *sub;
701

702
    if (pjsip_xfer_create_uac (transferCall->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
703
704
705
706
707
708
709
    	return false;

    /* Associate this voiplink of call with the client subscription
     * We can not just associate call with the client subscription
     * because after this function, we can no find the cooresponding
     * voiplink from the call any more. But the voiplink is useful!
     */
710
    pjsip_evsub_set_mod_data (sub, _mod_ua.id, this);
711

712
	pjsip_tx_data *tdata;
713
    if (pjsip_xfer_initiate (sub, &str_dest, &tdata) != PJ_SUCCESS)
714
715
716
    	return false;

    // Put SIP call id in map in order to retrieve call during transfer callback
717
    pj_str_t *callidtr = &transferCall->inv->dlg->call_id->id;
718
    transferCallID[std::string (callidtr->ptr, callidtr->slen)] = transferCall->getCallId();
719

720
    return pjsip_xfer_send_request (sub, tdata) == PJ_SUCCESS;
721
722
}

723
bool
724
SIPVoIPLink::refuse (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
725
{
Rafaël Carré's avatar
Rafaël Carré committed
726
727
    SIPCall *call = getSIPCall (id);
    if (!call->isIncoming() or call->getConnectionState() == Call::Connected)
728
        return false;
729

730
731
    call->getAudioRtp()->stop();

732
    pjsip_tx_data *tdata;
Rafaël Carré's avatar
Rafaël Carré committed
733
    if (pjsip_inv_end_session (call->inv, PJSIP_SC_DECLINE, NULL, &tdata) != PJ_SUCCESS)
734
        return false;
735

Rafaël Carré's avatar
Rafaël Carré committed
736
    if (pjsip_inv_send_msg (call->inv, tdata) != PJ_SUCCESS)
737
        return false;
738

739
    // Make sure the pointer is NULL in callbacks
740
    call->inv->mod_data[_mod_ua.id] = NULL;
741

742
    removeCall (id);
743

744
    return true;
745
746
}

747
std::string
748
SIPVoIPLink::getCurrentCodecName(Call *call) const
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
749
{
Rafaël Carré's avatar
Rafaël Carré committed
750
    return dynamic_cast<SIPCall*>(call)->getLocalSDP()->getCodecName();
751
752
}

Rafaël Carré's avatar
Rafaël Carré committed
753
void
754
SIPVoIPLink::carryingDTMFdigits (const std::string& id, char code)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
755
{
Rafaël Carré's avatar
Rafaël Carré committed
756
    std::string accountID(Manager::instance().getAccountFromCall(id));
757
    SIPAccount *account = static_cast<SIPAccount*>(Manager::instance().getAccount(accountID));
Rafaël Carré's avatar
Rafaël Carré committed
758
759
760
761
    if (account) try {
    	dtmfSend(getSIPCall(id), code, account->getDtmfType());
    } catch (VoipLinkException) {
    	// don't do anything if call doesn't exist
762
    }
763
}
764

765
void
Rafaël Carré's avatar
Rafaël Carré committed
766
SIPVoIPLink::dtmfSend (SIPCall *call, char code, DtmfType dtmf)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
767
{