sipvoiplink.cpp 65.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 "sdes_negotiator.h"
48
49
50

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

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

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

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

99
100
101
102
static pj_caching_pool pool_cache, *cp_ = &pool_cache;
static pj_pool_t *pool_;
static pjsip_endpoint *endpt_;
static pjsip_module mod_ua_;
103
static pj_thread_t *thread;
Emmanuel Milou's avatar
Emmanuel Milou committed
104

105
106
107
108
109
110
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);
111
static void registration_cb(pjsip_regc_cbparam *param);
112
113
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
std::string loadSIPLocalIP()
{
    pj_sockaddr ip_addr;
132
133
134
135

    if (pj_gethostip(pj_AF_INET(), &ip_addr) == PJ_SUCCESS)
        return pj_inet_ntoa(ip_addr.ipv4.sin_addr);

136
137
    return "";
}
138
139
140

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

    size_t found = route.find(":");
145

146
    if (found != std::string::npos) {
147
148
149
150
151
152
153
154
155
        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);
156
157
158
159
    routing->name_addr.uri = (pjsip_uri*) url;
    pj_strdup2(hdr_pool, &url->host, host.c_str());
    url->port = port;

160
    pj_list_push_back(route_set, pjsip_hdr_clone(hdr_pool, routing));
161
162
163
164

    return route_set;
}

165
166
} // end anonymous namespace

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

169
170
171
172
173
174
175
176
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
177

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

183
184
    pj_caching_pool_init(cp_, &pj_pool_factory_default_policy, 0);
    pool_ = pj_pool_create(&cp_->factory, "sflphone", 4000, 4000, NULL);
185

186
    if (!pool_)
187
        throw VoipLinkException("UserAgent: Could not initialize memory pool");
Alexandre Savard's avatar
Alexandre Savard committed
188

189
    TRY(pjsip_endpt_create(&cp_->factory, pj_gethostname()->ptr, &endpt_));
190

191
192
193
    if (loadSIPLocalIP().empty())
        throw VoipLinkException("UserAgent: Unable to determine network capabilities");

194
195
196
197
    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_));
198
199

    // Initialize and register sflphone module
200
201
202
203
204
205
    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_));
206

207
208
    TRY(pjsip_evsub_init_module(endpt_));
    TRY(pjsip_xfer_init_module(endpt_));
209
210

    static const pjsip_inv_callback inv_cb = {
211
212
213
214
215
216
217
218
        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,
219
    };
220
    TRY(pjsip_inv_usage_init(endpt_, &inv_cb));
221
222

    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}
223
    pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed);
224
225

    static const pj_str_t text_plain = { (char*) "text/plain", 10 };
226
    pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, NULL, 1, &text_plain);
227
228

    static const pj_str_t accepted = { (char*) "application/sdp", 15 };
229
    pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ACCEPT, NULL, 1, &accepted);
230

231
    DEBUG("UserAgent: pjsip version %s for %s initialized", pj_get_version(), PJ_OS_NAME);
232

233
    TRY(pjsip_replaces_init_module(endpt_));
234
235

    evThread_->start();
yanmorin's avatar
   
yanmorin committed
236
}
jpbl's avatar
jpbl committed
237

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

    const pj_time_val tv = {0, 10};
245
246
    pjsip_endpt_handle_events(endpt_, &tv);
    pjsip_endpt_destroy(endpt_);
247

248
249
    pj_pool_release(pool_);
    pj_caching_pool_destroy(cp_);
250
251

    pj_shutdown();
252
253
}

254
SIPVoIPLink* SIPVoIPLink::instance()
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
255
{
256
257
    static SIPVoIPLink* instance = NULL;

258
259
    if (!instance)
        instance = new SIPVoIPLink;
Emmanuel Milou's avatar
Emmanuel Milou committed
260

261
    return instance;
yanmorin's avatar
   
yanmorin committed
262
263
}

264
void SIPVoIPLink::init() {}
Emmanuel Milou's avatar
Emmanuel Milou committed
265

266
void SIPVoIPLink::terminate() {}
jpbl's avatar
jpbl committed
267

268
void
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
269
270
SIPVoIPLink::getEvent()
{
271
    static pj_thread_desc desc;
272

273
    // We have to register the external thread so it could access the pjsip frameworks
274
    if (!pj_thread_is_registered())
275
        pj_thread_register(NULL, desc, &thread);
yanmorin's avatar
   
yanmorin committed
276

Rafaël Carré's avatar
Rafaël Carré committed
277
    static const pj_time_val timeout = {0, 10};
278
    pjsip_endpt_handle_events(endpt_, &timeout);
279
}
280

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

286
287
    account->setRegister(true);
    account->setRegistrationState(Trying);
288

289
    pjsip_regc *regc = account->getRegistrationInfo();
290

291
    if (pjsip_regc_create(endpt_, (void *) account, &registration_cb, &regc) != PJ_SUCCESS)
292
293
294
        throw VoipLinkException("UserAgent: Unable to create regc structure.");

    std::string srvUri(account->getServerUri());
295

296
297
    std::string address, port;
    findLocalAddressFromUri(srvUri, account->transport_, address, port);
298

299
300
301
302
303
    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());
304

305
306
    if (pjsip_regc_init(regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, account->getRegistrationExpire()) != PJ_SUCCESS)
        throw VoipLinkException("Unable to initialize account registration structure");
307

308
    if (!account->getServiceRoute().empty())
309
        pjsip_regc_set_route_set(regc, createRouteSet(account->getServiceRoute(), pool_));
310

311
    pjsip_regc_set_credentials(regc, account->getCredentialCount(), account->getCredInfo());
312

jpbl's avatar
jpbl committed
313

314
    pjsip_hdr hdr_list;
315
316
317
318
    pj_list_init(&hdr_list);
    std::string useragent(account->getUserAgentName());
    pj_str_t pJuseragent = pj_str((char*)useragent.c_str());
    const pj_str_t STR_USER_AGENT = { (char*) "User-Agent", 10 };
319

320
    pjsip_generic_string_hdr *h = pjsip_generic_string_hdr_create(pool_, &STR_USER_AGENT, &pJuseragent);
321
322
    pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
    pjsip_regc_add_headers(regc, &hdr_list);
jpbl's avatar
jpbl committed
323

324

325
    pjsip_tx_data *tdata;
Emmanuel Milou's avatar
Emmanuel Milou committed
326

327
328
    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
329

330
    if (pjsip_regc_set_transport(regc, initTransportSelector(account->transport_, pool_)) != PJ_SUCCESS)
331
        throw VoipLinkException("Unable to set transport");
332

333
334
    // decrease transport's ref count, counter incrementation is managed when acquiring transport
    pjsip_transport_dec_ref(account->transport_);
335

336
337
338
    // pjsip_regc_send increment the transport ref count by one,
    if (pjsip_regc_send(regc, tdata) != PJ_SUCCESS)
        throw VoipLinkException("Unable to send account registration request");
339

340
341
342
343
344
345
    // 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->transport_);

    account->setRegistrationInfo(regc);
346
347
}

348
void SIPVoIPLink::sendUnregister(Account *a)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
349
{
350
    SIPAccount *account = dynamic_cast<SIPAccount *>(a);
351

352
    // This may occurs if account failed to register and is in state INVALID
353
    if (!account->isRegister()) {
354
        account->setRegistrationState(Unregistered);
355
        return;
356
357
    }

358
    pjsip_regc *regc = account->getRegistrationInfo();
359

360
    if (!regc)
361
        throw VoipLinkException("Registration structure is NULL");
362

363
    pjsip_tx_data *tdata = NULL;
364
365

    if (pjsip_regc_unregister(regc, &tdata) != PJ_SUCCESS)
366
        throw VoipLinkException("Unable to unregister sip account");
367

368
    if (pjsip_regc_send(regc, tdata) != PJ_SUCCESS)
369
        throw VoipLinkException("Unable to send request to unregister sip account");
370

371
    account->setRegister(false);
jpbl's avatar
jpbl committed
372
373
}

374
Call *SIPVoIPLink::newOutgoingCall(const std::string& id, const std::string& toUrl)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
375
{
376
377
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id)));

378
    if (account == NULL) // TODO: We should investigate how we could get rid of this error and create a IP2IP call instead
379
        throw VoipLinkException("Could not get account for this call");
380

381
    SIPCall* call = new SIPCall(id, Call::Outgoing, cp_);
Emmanuel Milou's avatar
Emmanuel Milou committed
382

383
384
    // If toUri is not a well formated sip URI, use account information to process it
    std::string toUri;
385

386
387
    if (toUrl.find("sip:") != std::string::npos or
        toUrl.find("sips:") != std::string::npos)
388
        toUri = toUrl;
389
    else
390
        toUri = account->getToUri(toUrl);
391

392
393
    call->setPeerNumber(toUri);
    std::string localAddr(getInterfaceAddrFromName(account->getLocalInterface()));
394

395
    if (localAddr == "0.0.0.0")
396
        localAddr = loadSIPLocalIP();
397

398
    setCallMediaLocal(call, localAddr);
Emmanuel Milou's avatar
Emmanuel Milou committed
399

400
    // May use the published address as well
401
    std::string addrSdp = account->isStunEnabled() ?
402
403
    account->getPublishedAddress() :
    getInterfaceAddrFromName(account->getLocalInterface());
404

405
    if (addrSdp == "0.0.0.0")
406
        addrSdp = loadSIPLocalIP();
Emmanuel Milou's avatar
Emmanuel Milou committed
407

408
409
410
    // 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
411
412
    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);

413
    if (audiocodec == NULL) {
414
415
        delete call;
        throw VoipLinkException("Could not instantiate codec for early media");
416
    }
417

418
    try {
419
420
421
422
        call->getAudioRtp().initAudioRtpConfig();
        call->getAudioRtp().initAudioSymmetricRtpSession();
        call->getAudioRtp().initLocalCryptoInfo();
        call->getAudioRtp().start(static_cast<sfl::AudioCodec *>(audiocodec));
423
    } catch (...) {
424
        delete call;
425
426
        throw VoipLinkException("Could not start rtp session for early media");
    }
427

428
    call->initRecFileName(toUrl);
429

430
431
    call->getLocalSDP()->setLocalIP(addrSdp);
    call->getLocalSDP()->createOffer(account->getActiveCodecs());
432

433
434
435
436
    if (!SIPStartCall(call)) {
        delete call;
        throw VoipLinkException("Could not send outgoing INVITE request for new call");
    }
jpbl's avatar
jpbl committed
437

438
    return call;
439
}
440

441
void
442
SIPVoIPLink::answer(Call *c)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
443
{
444
    SIPCall *call = dynamic_cast<SIPCall*>(c);
445

446
    if (!call)
447
        return;
Emmanuel Lepage's avatar
Emmanuel Lepage committed
448

449
    pjsip_tx_data *tdata;
Emmanuel Lepage's avatar
Emmanuel Lepage committed
450

451
452
453
454
455
    if (pjsip_inv_answer(call->inv, PJSIP_SC_OK, NULL, NULL, &tdata) != PJ_SUCCESS)
        throw VoipLinkException("Could not init invite request answer (200 OK)");

    if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS)
        throw VoipLinkException("Could not send invite request answer (200 OK)");
456

457
458
    call->setConnectionState(Call::Connected);
    call->setState(Call::Active);
Yun Liu's avatar
Yun Liu committed
459
460
}

461
void
462
SIPVoIPLink::hangup(const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
463
{
464
    SIPCall* call = getSIPCall(id);
465

466
    std::string account_id(Manager::instance().getAccountFromCall(id));
467
468
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id));

469
    if (account == NULL)
470
        throw VoipLinkException("Could not find account for this call");
471

472
    pjsip_inv_session *inv = call->inv;
473

474
    if (inv == NULL)
475
        throw VoipLinkException("No invite session for this call");
476

477
    // Looks for sip routes
478
    if (not(account->getServiceRoute().empty())) {
479
        pjsip_route_hdr *route_set = createRouteSet(account->getServiceRoute(), inv->pool);
480
        pjsip_dlg_set_route_set(inv->dlg, route_set);
481
    }
482

483
    pjsip_tx_data *tdata = NULL;
484

485
    // User hangup current call. Notify peer
486
    if (pjsip_inv_end_session(inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
487
        return;
488

489
    if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS)
490
        return;
491

492
    // Make sure user data is NULL in callbacks
493
    inv->mod_data[mod_ua_.id] = NULL;
494

495
    if (Manager::instance().isCurrentCall(id))
496
        call->getAudioRtp().stop();
497

498
    removeCall(id);
alexandresavard's avatar
alexandresavard committed
499
500
}

501
void
502
SIPVoIPLink::peerHungup(const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
503
{
504
    SIPCall* call = getSIPCall(id);
Alexandre Savard's avatar
Alexandre Savard committed
505

506
    // User hangup current call. Notify peer
507
    pjsip_tx_data *tdata = NULL;
508
509

    if (pjsip_inv_end_session(call->inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
510
        return;
511

512
    if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS)
513
        return;
514

515
    // Make sure user data is NULL in callbacks
516
    call->inv->mod_data[mod_ua_.id ] = NULL;
517

518
    if (Manager::instance().isCurrentCall(id))
519
        call->getAudioRtp().stop();
520

521
    removeCall(id);
522
523
}

524
void
525
SIPVoIPLink::onhold(const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
526
{
527
    SIPCall *call = getSIPCall(id);
528
    call->setState(Call::Hold);
529
    call->getAudioRtp().stop();
530
531

    Sdp *sdpSession = call->getLocalSDP();
532

533
    if (!sdpSession)
534
        throw VoipLinkException("Could not find sdp session");
535
536
537
538
539

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

540
    SIPSessionReinvite(call);
541
542
}

543
void
544
SIPVoIPLink::offhold(const std::string& id)
545
{
546
547
548
    SIPCall *call = getSIPCall(id);

    Sdp *sdpSession = call->getLocalSDP();
549

550
    if (sdpSession == NULL)
551
        throw VoipLinkException("Could not find sdp session");
552

553
    try {
554
        int pl = PAYLOAD_CODEC_ULAW;
555
        sfl::Codec *sessionMedia = sdpSession->getSessionMedia();
556

557
        if (sessionMedia)
558
            pl = sessionMedia->getPayloadType();
559

560
        // Create a new instance for this codec
561
562
        sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec(pl);

563
        if (audiocodec == NULL)
564
            throw VoipLinkException("Could not instantiate codec");
565

566
567
568
        call->getAudioRtp().initAudioRtpConfig();
        call->getAudioRtp().initAudioSymmetricRtpSession();
        call->getAudioRtp().start(static_cast<sfl::AudioCodec *>(audiocodec));
569
    } catch (const SdpException &e) {
570
        ERROR("UserAgent: Exception: %s", e.what());
571
572
    } catch (...) {
        throw VoipLinkException("Could not create audio rtp session");
573
574
575
576
577
    }

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

579
    if (SIPSessionReinvite(call) == PJ_SUCCESS)
580
        call->setState(Call::Active);
581
582
}

Rafaël Carré's avatar
Rafaël Carré committed
583
void
584
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
585
{
586
    SIPCall *call;
587

588
    try {
589
590
        call = getSIPCall(callID);
    } catch (const VoipLinkException &e) {
Rafaël Carré's avatar
Rafaël Carré committed
591
        return;
592
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
593

594
595
596
597
    /* 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
598

599
    list.push_front(entry);
600

601
    module->send_sip_message(call->inv, callID, module->appendUriList(message, list));
602
603
}

Rafaël Carré's avatar
Rafaël Carré committed
604
605
bool
SIPVoIPLink::transferCommon(SIPCall *call, pj_str_t *dst)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
606
{
607
    pjsip_evsub_user xfer_cb;
608
    pj_bzero(&xfer_cb, sizeof(xfer_cb));
609
    xfer_cb.on_evsub_state = &transfer_client_cb;
610

611
    pjsip_evsub *sub;
612
613
614

    if (pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub) != PJ_SUCCESS)
        return false;
615

616
617
    /* Associate this voiplink of call with the client subscription
     * We can not just associate call with the client subscription
618
     * because after this function, we can no find the cooresponding
619
620
     * voiplink from the call any more. But the voiplink is useful!
     */
621
    pjsip_evsub_set_mod_data(sub, mod_ua_.id, this);
622

623
624
625
    /*
     * Create REFER request.
     */
626
627
    pjsip_tx_data *tdata;

628
629
    if (pjsip_xfer_initiate(sub, dst, &tdata) != PJ_SUCCESS)
        return false;
630

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

635
    /* Send. */
636
637
    if (pjsip_xfer_send_request(sub, tdata) != PJ_SUCCESS)
        return false;
Rafaël Carré's avatar
Rafaël Carré committed
638
639

    return true;
640
}
641

Rafaël Carré's avatar
Rafaël Carré committed
642
void
643
SIPVoIPLink::transfer(const std::string& id, const std::string& to)
644
{
Rafaël Carré's avatar
Rafaël Carré committed
645
646
647
648
    SIPCall *call = getSIPCall(id);
    call->stopRecording();

    std::string account_id(Manager::instance().getAccountFromCall(id));
649
650
    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id));

Rafaël Carré's avatar
Rafaël Carré committed
651
    if (account == NULL)
652
        throw VoipLinkException("Could not find account");
653

Rafaël Carré's avatar
Rafaël Carré committed
654
655
    std::string toUri;
    pj_str_t dst = { NULL, 0 };
656

657
    if (to.find("@") == std::string::npos) {
Rafaël Carré's avatar
Rafaël Carré committed
658
        toUri = account->getToUri(to);
659
        pj_cstr(&dst, toUri.c_str());
Rafaël Carré's avatar
Rafaël Carré committed
660
    }
661

Rafaël Carré's avatar
Rafaël Carré committed
662
    if (!transferCommon(getSIPCall(id), &dst))
663
        throw VoipLinkException("Couldn't transfer");
Rafaël Carré's avatar
Rafaël Carré committed
664
665
666
667
}

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

671
672
    char str_dest_buf[PJSIP_MAX_URL_SIZE*2] = { '<' };
    pj_str_t dst = { str_dest_buf, 1 };
Rafaël Carré's avatar
Rafaël Carré committed
673
674
675

    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,
676
677
678
679
680
681
682
683
684
685
686
    sizeof(str_dest_buf) - dst.slen,
    "?"
    "Replaces=%.*s"
    "%%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);
687

Rafaël Carré's avatar
Rafaël Carré committed
688
    return transferCommon(getSIPCall(id), &dst);
689
690
}

Rafaël Carré's avatar
Rafaël Carré committed
691
void
692
SIPVoIPLink::refuse(const std::string& id)
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
693
{
694
695
    SIPCall *call = getSIPCall(id);

Rafaël Carré's avatar
Rafaël Carré committed
696
    if (!call->isIncoming() or call->getConnectionState() == Call::Connected)
Rafaël Carré's avatar
Rafaël Carré committed
697
        return;
698

699
    call->getAudioRtp().stop();
700

701
    pjsip_tx_data *tdata;
702
703

    if (pjsip_inv_end_session(call->inv, PJSIP_SC_DECLINE, NULL, &tdata) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
704
        return;
705

706
    if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS)
Rafaël Carré's avatar
Rafaël Carré committed
707
        return;
708

709
    // Make sure the pointer is NULL in callbacks
710
    call->inv->mod_data[mod_ua_.id] = NULL;
711

712
    removeCall(id);
713
714
}

715
std::string
716
SIPVoIPLink::getCurrentCodecName(Call *call) const
Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
717
{
Rafaël Carré's avatar
Rafaël Carré committed
718
    return dynamic_cast<SIPCall*>(call)->getLocalSDP()->getCodecName();