sipvoiplink.cpp 63.6 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
56
#include "audio/audiolayer.h"

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

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

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

73
74
#include <map>

75
76
using namespace sfl;

77
namespace {
Rafaël Carré's avatar
Rafaël Carré committed
78
79

static pjsip_transport *_localUDPTransport = NULL; /** The default transport (5060) */
80
81
82

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

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

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

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

99
100
101
102
103
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
104

105
106
107
108
109
110
111
112
113
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
114

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

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

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

129
130
131
132
133
134
135
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 "";
}
136
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

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;
}

163
164
} // end anonymous namespace

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

167
168
169
170
171
172
173
174
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
175

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

181
182
183
184
    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
185

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
    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
232
}
jpbl's avatar
jpbl committed
233

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
234
235
SIPVoIPLink::~SIPVoIPLink()
{
236
	delete evThread_;
237
238
239
240
241
242
243
244
245
246
247
    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();
248
249
}

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

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

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

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

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

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

Rafaël Carré's avatar
Rafaël Carré committed
272
    static const pj_time_val timeout = {0, 10};
273
    pjsip_endpt_handle_events(_endpt, &timeout);
274
}
275

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

281
	account->setRegister(true);
Rafaël Carré's avatar
Rafaël Carré committed
282
	account->setRegistrationState (Trying);
283

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

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

290
291
	std::string address, port;
	findLocalAddressFromUri(srvUri, account->transport, address, port);
292

293
294
295
296
297
	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());
298

Rafaël Carré's avatar
Rafaël Carré committed
299
	if (pjsip_regc_init (regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, account->getRegistrationExpire()) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
300
		throw VoipLinkException("Unable to initialize account registration structure");
301

302
303
304
305
	if (!account->getServiceRoute().empty())
		pjsip_regc_set_route_set (regc, createRouteSet(account->getServiceRoute(), _pool));

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

jpbl's avatar
jpbl committed
307

308
    pjsip_hdr hdr_list;
Rafaël Carré's avatar
Rafaël Carré committed
309
	pj_list_init (&hdr_list);
310
311
	std::string useragent(account->getUserAgentName());
	pj_str_t pJuseragent = pj_str ((char*)useragent.c_str());
312
	const pj_str_t STR_USER_AGENT = { (char*) "User-Agent", 10 };
313

314
	pjsip_generic_string_hdr *h = pjsip_generic_string_hdr_create(_pool, &STR_USER_AGENT, &pJuseragent);
Rafaël Carré's avatar
Rafaël Carré committed
315
316
	pj_list_push_back (&hdr_list, (pjsip_hdr*) h);
	pjsip_regc_add_headers (regc, &hdr_list);
jpbl's avatar
jpbl committed
317

318

319
    pjsip_tx_data *tdata;
Rafaël Carré's avatar
Rafaël Carré committed
320
321
	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
322

323
324
	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
325

326
	// decrease transport's ref count, counter incrementation is managed when acquiring transport
327
	pjsip_transport_dec_ref(account->transport);
328

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

333
334
335
	// 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.
336
	pjsip_transport_dec_ref (account->transport);
337

338
    account->setRegistrationInfo (regc);
339
340
}

341
void SIPVoIPLink::sendUnregister (Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
342
{
343
    SIPAccount *account = dynamic_cast<SIPAccount *>(a);
344

345
    // This may occurs if account failed to register and is in state INVALID
346
347
    if (!account->isRegister()) {
        account->setRegistrationState (Unregistered);
348
        return;
349
350
    }

351
    pjsip_regc *regc = account->getRegistrationInfo();
352
    if (!regc)
353
        throw VoipLinkException("Registration structure is NULL");
354

355
    pjsip_tx_data *tdata = NULL;
356
    if (pjsip_regc_unregister (regc, &tdata) != PJ_SUCCESS)
357
        throw VoipLinkException("Unable to unregister sip account");
358

359
    if (pjsip_regc_send (regc, tdata) != PJ_SUCCESS)
360
        throw VoipLinkException("Unable to send request to unregister sip account");
361

362
    account->setRegister (false);
jpbl's avatar
jpbl committed
363
364
}

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

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

373
374
    // If toUri is not a well formated sip URI, use account information to process it
    std::string toUri;
375
    if((toUrl.find("sip:") != std::string::npos) or
376
    		toUrl.find("sips:") != std::string::npos)
377
    	toUri = toUrl;
378
    else
379
        toUri = account->getToUri(toUrl);
380

381
    call->setPeerNumber (toUri);
382
    std::string localAddr(getInterfaceAddrFromName (account->getLocalInterface ()));
383

384
    if (localAddr == "0.0.0.0")
385
    	localAddr = loadSIPLocalIP();
386

387
    setCallMediaLocal (call, localAddr);
Emmanuel Milou's avatar
Emmanuel Milou committed
388

389
    // May use the published address as well
390
391
    std::string addrSdp = account->isStunEnabled() ?
        account->getPublishedAddress() :
392
        getInterfaceAddrFromName(account->getLocalInterface());
393

394
    if (addrSdp == "0.0.0.0")
395
		addrSdp = loadSIPLocalIP();
Emmanuel Milou's avatar
Emmanuel Milou committed
396

397
398
399
    // 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
400
    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (PAYLOAD_CODEC_ULAW);
401
    if (audiocodec == NULL) {
402
403
    	delete call;
    	throw VoipLinkException ("Could not instantiate codec for early media");
404
    }
405

406
	try {
407
408
409
		call->getAudioRtp()->initAudioRtpConfig ();
		call->getAudioRtp()->initAudioSymmetricRtpSession ();
		call->getAudioRtp()->initLocalCryptoInfo ();
410
		call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
411
	} catch (...) {
412
        delete call;
413
        throw VoipLinkException ("Could not start rtp session for early media");
414
	}
415

416
	call->initRecFileName(toUrl);
417

418
	call->getLocalSDP()->setLocalIP (addrSdp);
Rafaël Carré's avatar
Rafaël Carré committed
419
	call->getLocalSDP()->createOffer(account->getActiveCodecs());
420

Rafaël Carré's avatar
Rafaël Carré committed
421
	if (!SIPStartCall(call)) {
422
		delete call;
423
		throw VoipLinkException("Could not send outgoing INVITE request for new call");
424
	}
jpbl's avatar
jpbl committed
425

426
	return call;
427
}
428

429
void
430
SIPVoIPLink::answer (Call *c)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
431
{
432
    SIPCall *call = dynamic_cast<SIPCall*>(c);
433
434
    if (!call)
    	return;
Emmanuel Lepage's avatar
Emmanuel Lepage committed
435

436
437
438
    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
439

440
441
442
443
444
    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
445
446
}

447
void
448
SIPVoIPLink::hangup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
449
{
450
    SIPCall* call = getSIPCall(id);
451

452
    std::string account_id(Manager::instance().getAccountFromCall(id));
453
    SIPAccount *account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (account_id));
454
    if (account == NULL)
455
456
    	throw VoipLinkException("Could not find account for this call");

457
    pjsip_inv_session *inv = call->inv;
458
    if (inv == NULL)
459
        throw VoipLinkException("No invite session for this call");
460

461
    // Looks for sip routes
462
    if (not (account->getServiceRoute().empty())) {
463
        pjsip_route_hdr *route_set = createRouteSet(account->getServiceRoute(), inv->pool);
464
465
        pjsip_dlg_set_route_set (inv->dlg, route_set);
    }
466

467
    pjsip_tx_data *tdata = NULL;
468
    // User hangup current call. Notify peer
469
470
    if (pjsip_inv_end_session (inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
        return;
471

472
473
    if (pjsip_inv_send_msg (inv, tdata) != PJ_SUCCESS)
        return;
474

475
    // Make sure user data is NULL in callbacks
476
    inv->mod_data[_mod_ua.id] = NULL;
477

Rafaël Carré's avatar
Rafaël Carré committed
478
479
	if (Manager::instance().isCurrentCall (id))
		call->getAudioRtp()->stop();
480
481

    removeCall (id);
alexandresavard's avatar
alexandresavard committed
482
483
}

484
void
485
SIPVoIPLink::peerHungup (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
486
{
487
    SIPCall* call = getSIPCall(id);
Alexandre Savard's avatar
Alexandre Savard committed
488

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

494
    if (pjsip_inv_send_msg (call->inv, tdata) != PJ_SUCCESS)
495
        return;
496

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

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

503
    removeCall (id);
504
505
}

506
void
507
SIPVoIPLink::onhold (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
508
{
509
    SIPCall *call = getSIPCall(id);
510
    call->setState (Call::Hold);
Rafaël Carré's avatar
Rafaël Carré committed
511
	call->getAudioRtp()->stop();
512

513
514
	Sdp *sdpSession = call->getLocalSDP();
    if (!sdpSession)
515
    	throw VoipLinkException("Could not find sdp session");
516
517
518
519
520

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

521
    SIPSessionReinvite (call);
522
523
}

524
void
525
SIPVoIPLink::offhold (const std::string& id)
526
{
527
    SIPCall *call = getSIPCall (id);
528

529
	Sdp *sdpSession = call->getLocalSDP();
530
    if (sdpSession == NULL)
531
    	throw VoipLinkException("Could not find sdp session");
532

533
    try {
534
        int pl = PAYLOAD_CODEC_ULAW;
535
        sfl::Codec *sessionMedia = sdpSession->getSessionMedia();
536
537
        if (sessionMedia)
    	    pl = sessionMedia->getPayloadType();
538

539
        // Create a new instance for this codec
540
        sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec (pl);
541
        if (audiocodec == NULL)
542
    	    throw VoipLinkException("Could not instantiate codec");
543

544
545
        call->getAudioRtp()->initAudioRtpConfig ();
        call->getAudioRtp()->initAudioSymmetricRtpSession ();
546
        call->getAudioRtp()->start (static_cast<sfl::AudioCodec *>(audiocodec));
547

548
    }
549
    catch (const SdpException &e) {
550
551
552
    	_error("UserAgent: Exception: %s", e.what());
    } 
    catch (...) {
553
    	throw VoipLinkException("Could not create audio rtp session");
554
555
556
557
558
    }

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

560
561
    if (SIPSessionReinvite(call) == PJ_SUCCESS)
    	call->setState (Call::Active);
562
563
}

Rafaël Carré's avatar
Rafaël Carré committed
564
void
565
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
566
{
567
568
569
570
571
    SIPCall *call;
    try {
        call = getSIPCall (callID);
    }
    catch (const VoipLinkException &e) {
Rafaël Carré's avatar
Rafaël Carré committed
572
        return;
573
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
574

575
576
577
578
579
580
581
	/* 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
582
	module->send_sip_message (call->inv, callID, module->appendUriList (message, list));
583
584
}

Rafaël Carré's avatar
Rafaël Carré committed
585
586
bool
SIPVoIPLink::transferCommon(SIPCall *call, pj_str_t *dst)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
587
{
588
    pjsip_evsub_user xfer_cb;
589
    pj_bzero (&xfer_cb, sizeof (xfer_cb));
590
    xfer_cb.on_evsub_state = &transfer_client_cb;
591

592
    pjsip_evsub *sub;
593
    if (pjsip_xfer_create_uac (call->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
594
    	return false;
595

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

603
604
605
    /*
     * Create REFER request.
     */
606
607
    pjsip_tx_data *tdata;

Rafaël Carré's avatar
Rafaël Carré committed
608
609
    if (pjsip_xfer_initiate (sub, dst, &tdata) != PJ_SUCCESS)
    	return false;
610

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

615
    /* Send. */
616
    if (pjsip_xfer_send_request (sub, tdata) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
617
618
619
    	return false;

    return true;
620
}
621

Rafaël Carré's avatar
Rafaël Carré committed
622
623
void
SIPVoIPLink::transfer (const std::string& id, const std::string& to)
624
{
Rafaël Carré's avatar
Rafaël Carré committed
625
626
627
628
629
630
631
    SIPCall *call = getSIPCall(id);
    call->stopRecording();

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

Rafaël Carré's avatar
Rafaël Carré committed
633
634
    std::string toUri;
    pj_str_t dst = { NULL, 0 };
635

Rafaël Carré's avatar
Rafaël Carré committed
636
637
638
639
    if (to.find ("@") == std::string::npos) {
        toUri = account->getToUri(to);
        pj_cstr (&dst, toUri.c_str());
    }
640

Rafaël Carré's avatar
Rafaël Carré committed
641
642
643
644
645
646
647
    if (!transferCommon(getSIPCall(id), &dst))
    	throw VoipLinkException("Couldn't transfer");
}

bool SIPVoIPLink::attendedTransfer(const std::string& id, const std::string& to)
{
	pjsip_dialog *target_dlg = getSIPCall(to)->inv->dlg;
648
	pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri);
649

Rafaël Carré's avatar
Rafaël Carré committed
650
651
652
653
654
655
	char str_dest_buf[PJSIP_MAX_URL_SIZE*2] = { '<' };
	pj_str_t dst = { str_dest_buf, 1 };

    dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf+1, sizeof(str_dest_buf)-1);
    dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen,
    		               sizeof(str_dest_buf) - dst.slen,
656
657
    			           "?"
    		               "Replaces=%.*s"
658
659
660
    			           "%%3Bto-tag%%3D%.*s"
    			           "%%3Bfrom-tag%%3D%.*s>",
    			           (int)target_dlg->call_id->id.slen,
Rafaël Carré's avatar
Rafaël Carré committed
661
							    target_dlg->call_id->id.ptr,
662
                           (int)target_dlg->remote.info->tag.slen,
Rafaël Carré's avatar
Rafaël Carré committed
663
							    target_dlg->remote.info->tag.ptr,
664
                           (int)target_dlg->local.info->tag.slen,
Rafaël Carré's avatar
Rafaël Carré committed
665
							    target_dlg->local.info->tag.ptr);
666

Rafaël Carré's avatar
Rafaël Carré committed
667
    return transferCommon(getSIPCall(id), &dst);
668
669
}

670
bool
671
SIPVoIPLink::refuse (const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
672
{
Rafaël Carré's avatar
Rafaël Carré committed
673
674
    SIPCall *call = getSIPCall (id);
    if (!call->isIncoming() or call->getConnectionState() == Call::Connected)
675
        return false;
676

677
678
    call->getAudioRtp()->stop();

679
    pjsip_tx_data *tdata;
Rafaël Carré's avatar
Rafaël Carré committed
680
    if (pjsip_inv_end_session (call->inv, PJSIP_SC_DECLINE, NULL, &tdata) != PJ_SUCCESS)
681
        return false;
682

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

686
    // Make sure the pointer is NULL in callbacks
687
    call->inv->mod_data[_mod_ua.id] = NULL;
688

689
    removeCall (id);
690

691
    return true;
692
693
}

694
std::string
695
SIPVoIPLink::getCurrentCodecName(Call *call) const
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
696
{
Rafaël Carré's avatar
Rafaël Carré committed
697
    return dynamic_cast<SIPCall*>(call)->getLocalSDP()->getCodecName();
698
699
}

Rafaël Carré's avatar
Rafaël Carré committed
700
void
701
SIPVoIPLink::carryingDTMFdigits (const std::string& id, char code)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
702
{
Rafaël Carré's avatar
Rafaël Carré committed
703
    std::string accountID(Manager::instance().getAccountFromCall(id));
704
    SIPAccount *account = static_cast<SIPAccount*>(Manager::instance().getAccount(accountID));
Rafaël Carré's avatar
Rafaël Carré committed
705
706
707
708
    if (account) try {
    	dtmfSend(getSIPCall(id), code, account->getDtmfType());
    } catch (VoipLinkException) {
    	// don't do anything if call doesn't exist
709
    }
710
}
711

712
void
Rafaël Carré's avatar
Rafaël Carré committed
713
SIPVoIPLink::dtmfSend (SIPCall *call, char code, DtmfType dtmf)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
714
{
Rafaël Carré's avatar
Rafaël Carré committed
715
716
717
718
719
720
721
722
	if (dtmf == OVERRTP) {
		call->getAudioRtp()->sendDtmfDigit(code - '0');
		return;
	}

	// else : dtmf == SIPINFO

    pj_str_t methodName = pj_str((char*)"INFO");
723
    pjsip_method method;
724
    pjsip_method_init_np (&method, &methodName);
725

726
    /* Create request message. */
727
    pjsip_tx_data *tdata;
728
    if (pjsip_dlg_create_request (call->inv->dlg, &method, -1, &tdata) != PJ_SUCCESS)
729
        return;
730

731
732
    int duration = Manager::instance().voipPreferences.getPulseLength();
    char dtmf_body[1000];
733
    snprintf(dtmf_body, sizeof dtmf_body - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration);
734

735
    /* Create "application/dtmf-relay" message body. */
Rafaël Carré's avatar
Rafaël Carré committed
736
737
738
739
    pj_str_t content = pj_str(dtmf_body);
    pj_str_t type = pj_str((char*)"application");
    pj_str_t subtype = pj_str((char*)"dtmf-relay");
    tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &content);
740

Rafaël Carré's avatar
Rafaël Carré committed
741
    if (tdata->msg->body == NULL)
742
        pjsip_tx_data_dec_ref (tdata);
Rafaël Carré's avatar
Rafaël Carré committed
743
    else
744
    	pjsip_dlg_send_request (call->inv->dlg, tdata, _mod_ua.id, NULL);
745
746
}

747
bool
748
SIPVoIPLink::SIPStartCall(SIPCall *call)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
749
{
750
751
    std::string id(Manager::instance().getAccountFromCall(call->getCallId()));
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id));
Rafaël Carré's avatar
Rafaël Carré committed
752
    if (account == NULL)
753
    	return false;
754

755
    std::string toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
756

757
758
    std::string address, port;
    findLocalAddressFromUri(toUri, account->transport, address, port);
759

760
761
762
763
764
    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 pjTo = pj_str((char*)toUri.c_str());
765

766
767
768