sipvoiplink.cpp 72.7 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
3
4
 *  Copyright (C) 2004-2009 Savoir-Faire Linux inc.
 *
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
5
 *  Author: Yun Liu <yun.liu@savoirfairelinux.com>
jpbl's avatar
jpbl committed
6
7
8
 *
 *  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
9
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
10
11
12
13
14
15
16
17
18
19
20
 *  (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.
 */
Emmanuel Milou's avatar
Emmanuel Milou committed
21

jpbl's avatar
jpbl committed
22
23
#include "sipvoiplink.h"
#include "eventthread.h"
yanmorin's avatar
   
yanmorin committed
24
#include "sipcall.h"
Emmanuel Milou's avatar
nothing    
Emmanuel Milou committed
25
#include "sipaccount.h"
Yun Liu's avatar
Yun Liu committed
26
#include "audio/audiortp.h"
27

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

Emmanuel Milou's avatar
Emmanuel Milou committed
30
31
32
/*
 *  The global pool factory
 */
33
pj_caching_pool _cp;
Emmanuel Milou's avatar
Emmanuel Milou committed
34
35
36
37

/*
 * The pool to allocate memory
 */
38
pj_pool_t *_pool;    
Emmanuel Milou's avatar
Emmanuel Milou committed
39
40
41
42

/*
 *	The SIP endpoint
 */
43
pjsip_endpoint *_endpt;
Emmanuel Milou's avatar
Emmanuel Milou committed
44
45
46
47
48
49
50
51
52

/*
 *	The SIP module
 */
pjsip_module _mod_ua;  

/*
 * Thread related
 */
53
54
55
56
pj_thread_t *thread;
pj_thread_desc desc;


Emmanuel Milou's avatar
Emmanuel Milou committed
57
58
59
/**
 * Get the number of voicemail waiting in a SIP message
 */
60
61
62
63
64
65
66
67
void set_voicemail_info( AccountID account, pjsip_msg_body *body );

/**
 * Set audio (SDP) configuration for a call
 * localport, localip, localexternalport
 * @param call a SIPCall valid pointer
 * @return bool True
 */
Emmanuel Milou's avatar
Emmanuel Milou committed
68
bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server);
69

Emmanuel Milou's avatar
Emmanuel Milou committed
70
// Documentated from the PJSIP Developer's Guide, available on the pjsip website/
71

Emmanuel Milou's avatar
Emmanuel Milou committed
72
73
74
75
76
77
78
/*
 * Session callback
 * Called when the invite session state has changed.
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
 * @param	e	A pointer on a pjsip_event structure
 */
79
void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e);
Emmanuel Milou's avatar
Emmanuel Milou committed
80
81
82
83
84
85
86
87

/*
 * Session callback
 * Called after SDP offer/answer session has completed.
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
 * @param	status	A pj_status_t structure
 */
88
void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED);
Emmanuel Milou's avatar
Emmanuel Milou committed
89
90
91
92
93
94
95
96

/*
 * Called when the invote usage module has created a new dialog and invite
 * because of forked outgoing request.
 *
 * @param	inv	A pointer on a pjsip_inv_session structure
 * @param	e	A pointer on a pjsip_event structure
 */
97
void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
Emmanuel Milou's avatar
Emmanuel Milou committed
98
99
100
101
102
103
104
105
106
107

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

/*
 * Registration callback
 */
113
void regc_cb(struct pjsip_regc_cbparam *param);
Emmanuel Milou's avatar
Emmanuel Milou committed
114
115
116
117
118
119

/*
 * Called to handle incoming requests outside dialogs
 * @param   rdata
 * @return  pj_bool_t
 */
120
pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata);
Emmanuel Milou's avatar
Emmanuel Milou committed
121
122
123
124
125
126

/*
 * Called to handle incoming response
 * @param	rdata
 * @return	pj_bool_t
 */
127
pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) ;
Emmanuel Milou's avatar
Emmanuel Milou committed
128
129
130
131

/*
 * Transfer callbacks
 */
132
133
134
void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event);
void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event);
void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata);
Emmanuel Milou's avatar
Emmanuel Milou committed
135
136
137
138
139

/*************************************************************************************************/

SIPVoIPLink* SIPVoIPLink::_instance = NULL;

140
141
142

    SIPVoIPLink::SIPVoIPLink(const AccountID& accountID)
    : VoIPLink(accountID)
Emmanuel Milou's avatar
Emmanuel Milou committed
143
144
      , _nbTryListenAddr(2) // number of times to try to start SIP listener
      , _stunServer("")
145
    , _localExternAddress("") 
146
147
    , _localExternPort(0)
    , _audiortp(new AudioRtp())
148
    ,_regPort(DEFAULT_SIP_PORT)
Emmanuel Milou's avatar
Emmanuel Milou committed
149
150
    , _useStun(false)
    , _clients(0)
jpbl's avatar
jpbl committed
151
{
152
153
    _debug("SIPVoIPLink::~SIPVoIPLink(): sipvoiplink constructor called \n");    

154
155
156
    // to get random number for RANDOM_PORT
    srand (time(NULL));

Emmanuel Milou's avatar
Emmanuel Milou committed
157
    /* Instanciate the C++ thread */
158
    _evThread = new EventThread(this);
Emmanuel Milou's avatar
Emmanuel Milou committed
159
160
161

    /* Start pjsip initialization step */
    init();
yanmorin's avatar
   
yanmorin committed
162
}
jpbl's avatar
jpbl committed
163

yanmorin's avatar
   
yanmorin committed
164
165
SIPVoIPLink::~SIPVoIPLink()
{
166
    _debug("SIPVoIPLink::~SIPVoIPLink(): sipvoiplink destructor called \n");
167
    terminate();
yanmorin's avatar
   
yanmorin committed
168
169
}

Emmanuel Milou's avatar
Emmanuel Milou committed
170
SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id)
yanmorin's avatar
   
yanmorin committed
171
{
Emmanuel Milou's avatar
Emmanuel Milou committed
172
173
174
175
176
177

    if(!_instance ){
        _instance = new SIPVoIPLink( id );
    }

    return _instance;
jpbl's avatar
jpbl committed
178
179
}

Emmanuel Milou's avatar
Emmanuel Milou committed
180
181
182
183
184
185
186
187
void SIPVoIPLink::decrementClients (void)
{
    _clients--;
    if(_clients == 0){
        terminate();
        SIPVoIPLink::_instance=NULL;
    }
}
188
189

bool SIPVoIPLink::init()
yanmorin's avatar
   
yanmorin committed
190
{
Emmanuel Milou's avatar
Emmanuel Milou committed
191
    if(initDone())
192
        return false;
193
    /* Initialize the pjsip library */
Emmanuel Milou's avatar
Emmanuel Milou committed
194
195
    pjsip_init();
    initDone(true);
196
197

    return true;
jpbl's avatar
jpbl committed
198
199
}

200
    void 
yanmorin's avatar
   
yanmorin committed
201
202
SIPVoIPLink::terminate()
{
Emmanuel Milou's avatar
Emmanuel Milou committed
203
204
    delete _evThread; _evThread = NULL;

205
    /* Clean shutdown of pjsip library */
Emmanuel Milou's avatar
Emmanuel Milou committed
206
207
    if( initDone() )
    {
208
        pjsip_shutdown();
Emmanuel Milou's avatar
Emmanuel Milou committed
209
210
    }
    initDone(false);
jpbl's avatar
jpbl committed
211
212
}

213
    void
yanmorin's avatar
   
yanmorin committed
214
SIPVoIPLink::terminateSIPCall()
jpbl's avatar
jpbl committed
215
{
216
    _debug("SIPVoIPLink::terminateSIPCall(): function called \n");
217
    ost::MutexLock m(_callMapMutex);
218
219
220
221
222
    CallMap::iterator iter = _callMap.begin();
    SIPCall *call;
    while( iter != _callMap.end() ) {
        call = dynamic_cast<SIPCall*>(iter->second);
        if (call) {
223
            // terminate the sip call
224
      	    _debug("SIPVoIPLink::terminateSIPCall()::the call is deleted, should close recording file \n");
225
226
227
            delete call; call = 0;
        }
        iter++;
yanmorin's avatar
   
yanmorin committed
228
    }
229
    _callMap.clear();
jpbl's avatar
jpbl committed
230
231
}

alexandresavard's avatar
alexandresavard committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
    void
SIPVoIPLink::terminateOneCall(const CallID& id)
{
    _debug("SIPVoIPLink::terminateOneCall(): function called \n");
  
    SIPCall *call;
    
    call = getSIPCall(id);
    if (call) {
    // terminate the sip call
        _debug("SIPVoIPLink::terminateOneCall()::the call is deleted, should close recording file \n");
        delete call; call = 0;
    }
}



249
    void
yanmorin's avatar
   
yanmorin committed
250
SIPVoIPLink::getEvent()
jpbl's avatar
jpbl committed
251
{
252
253
254
255
256
257
258
    // We have to register the external thread so it could access the pjsip framework
    if(!pj_thread_is_registered())
        pj_thread_register( NULL, desc, &thread );

    // PJSIP polling
    pj_time_val timeout = {0, 10};
    pjsip_endpt_handle_events( _endpt, &timeout);
jpbl's avatar
jpbl committed
259
260
}

261
int SIPVoIPLink::sendRegister( AccountID id )
jpbl's avatar
jpbl committed
262
{
263
264
265
266
267
268
269
    pj_status_t status;
    int expire_value;
    char contactTmp[256];
    pj_str_t svr, aor, contact;
    pjsip_tx_data *tdata;
    std::string tmp, hostname, username, password;
    SIPAccount *account;
Emmanuel Milou's avatar
Emmanuel Milou committed
270
    pjsip_regc *regc;
271

272
273
274
275
    account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id));
    hostname = account->getHostname();
    username = account->getUsername();
    password = account->getPassword();
276
277
278

    _mutexSIP.enterMutex(); 

Emmanuel Milou's avatar
Emmanuel Milou committed
279
280
    /* Get the client registration information for this particular account */
    regc = account->getRegistrationInfo();
281
    /* If the registration already exists, delete it */
Emmanuel Milou's avatar
Emmanuel Milou committed
282
283
284
    if(regc) {
        status = pjsip_regc_destroy(regc);
        regc = NULL;
285
286
        PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 );
    }
287

Emmanuel Milou's avatar
Emmanuel Milou committed
288
    account->setRegister(true);
jpbl's avatar
jpbl committed
289

290
291
    /* Set the expire value of the message from the config file */
    expire_value = Manager::instance().getRegistrationExpireValue();
jpbl's avatar
jpbl committed
292

293
    /* Update the state of the voip link */
Emmanuel Milou's avatar
Emmanuel Milou committed
294
    account->setRegistrationState(Trying);
295

296
    if (!validStunServer) {
Emmanuel Milou's avatar
Emmanuel Milou committed
297
298
        account->setRegistrationState(ErrorExistStun);
        account->setRegister(false);
299
300
301
        _mutexSIP.leaveMutex(); 
        return false;
    }
jpbl's avatar
jpbl committed
302

303
    /* Create the registration according to the account ID */
Emmanuel Milou's avatar
Emmanuel Milou committed
304
    status = pjsip_regc_create(_endpt, (void*)account, &regc_cb, &regc);
305
306
307
308
309
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to create regc.\n");
        _mutexSIP.leaveMutex(); 
        return false;
    }
jpbl's avatar
jpbl committed
310

311
312
313
314
315
    tmp = "sip:" + hostname;
    pj_strdup2(_pool, &svr, tmp.data());

    tmp = "<sip:" + username + "@" + hostname + ">";
    pj_strdup2(_pool, &aor, tmp.data());
yanmorin's avatar
   
yanmorin committed
316

317
318
    sprintf(contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort);
    pj_strdup2(_pool, &contact, contactTmp);
319
    account->setContact(contactTmp);
320

Emmanuel Milou's avatar
Emmanuel Milou committed
321
    status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout);
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr);
        _mutexSIP.leaveMutex(); 
        return false;
    }

    pjsip_cred_info *cred = account->getCredInfo();

    if(!cred)
        cred = new pjsip_cred_info();

    pj_bzero(cred, sizeof (pjsip_cred_info));
    pj_strdup2(_pool, &cred->username, username.data());
    cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
    pj_strdup2(_pool, &cred->data, password.data());
    pj_strdup2(_pool, &cred->realm, "*");
    pj_strdup2(_pool, &cred->scheme, "digest");
Emmanuel Milou's avatar
Emmanuel Milou committed
339
    pjsip_regc_set_credentials(regc, 1, cred);
jpbl's avatar
jpbl committed
340

341
    account->setCredInfo(cred);
jpbl's avatar
jpbl committed
342

Emmanuel Milou's avatar
Emmanuel Milou committed
343
    status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
344
345
346
347
348
349
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to register regc.\n");
        _mutexSIP.leaveMutex(); 
        return false;
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
350
    status = pjsip_regc_send(regc, tdata);
351
352
353
354
355
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to send regc request.\n");
        _mutexSIP.leaveMutex(); 
        return false;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
356

357
358
    _mutexSIP.leaveMutex(); 

Emmanuel Milou's avatar
Emmanuel Milou committed
359
360
    account->setRegistrationInfo(regc);

361
    return true;
jpbl's avatar
jpbl committed
362
363
}

364
    int 
Emmanuel Milou's avatar
Emmanuel Milou committed
365
SIPVoIPLink::sendUnregister( AccountID id )
yanmorin's avatar
   
yanmorin committed
366
{
367
368
    pj_status_t status = 0;
    pjsip_tx_data *tdata = NULL;
Emmanuel Milou's avatar
Emmanuel Milou committed
369
370
    SIPAccount *account;
    pjsip_regc *regc;
371

Emmanuel Milou's avatar
Emmanuel Milou committed
372
373
374
375
376
    account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id));
    regc = account->getRegistrationInfo();

    if(!account->isRegister()){
        account->setRegistrationState(Unregistered); 
377
378
379
        return true;
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
380
381
    if(regc) {
        status = pjsip_regc_unregister(regc, &tdata);
382
383
384
385
386
        if(status != PJ_SUCCESS) {
            _debug("UserAgent: Unable to unregister regc.\n");
            return false;
        }

Emmanuel Milou's avatar
Emmanuel Milou committed
387
        status = pjsip_regc_send( regc, tdata );
388
389
390
391
392
393
394
395
396
        if(status != PJ_SUCCESS) {
            _debug("UserAgent: Unable to send regc request.\n");
            return false;
        }
    } else {
        _debug("UserAgent: regc is null!\n");
        return false;
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
397
398
    account->setRegistrationInfo(regc);
    account->setRegister(false);
399
400

    return true;
jpbl's avatar
jpbl committed
401
402
}

alexandresavard's avatar
alexandresavard committed
403
Call* 
yanmorin's avatar
   
yanmorin committed
404
SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl)
jpbl's avatar
jpbl committed
405
{
406
407
    Account* account;

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

410
    if (call) {
411
        account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id)));
Emmanuel Milou's avatar
Emmanuel Milou committed
412
413
414
415
416
417
418
419
        if(!account)
        {
            _debug("Error retrieving the account to the make the call with\n");
            call->setConnectionState(Call::Disconnected);
            call->setState(Call::Error);
            delete call; call=0;
            return call;
        }
420
        //call->setPeerNumber(toUrl);
421
        call->setPeerNumber(getSipTo(toUrl, account->getHostname()));
422
423
424
425
426
427
428
429
430
431
        _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data());
        // we have to add the codec before using it in SIPOutgoingInvite...
        call->setCodecMap(Manager::instance().getCodecDescriptorMap());
        if ( SIPOutgoingInvite(call) ) {
            call->setConnectionState(Call::Progressing);
            call->setState(Call::Active);
            addCall(call);
        } else {
            delete call; call = 0;
        }
jpbl's avatar
jpbl committed
432
    }
433
    return call;
jpbl's avatar
jpbl committed
434
435
}

436
    bool
yanmorin's avatar
   
yanmorin committed
437
SIPVoIPLink::answer(const CallID& id)
jpbl's avatar
jpbl committed
438
439
{

440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
    int i;
    SIPCall *call;
    pj_status_t status;
    pjsip_tx_data *tdata;

    _debug("- SIP Action: start answering\n");

    call = getSIPCall(id);

    if (call==0) {
        _debug("! SIP Failure: SIPCall doesn't exists\n");
        return false;
    }

    // User answered the incoming call, tell peer this news
    if (call->startNegociation(_pool)) {
        // Create and send a 200(OK) response
        _debug("UserAgent: Negociation success!\n");
        status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata);
        PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
        status = pjsip_inv_send_msg(call->getInvSession(), tdata);
        PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

        _debug("* SIP Info: Starting AudioRTP when answering\n");
        if (_audiortp->createNewSession(call) >= 0) {
            call->setAudioStart(true);
            call->setConnectionState(Call::Connected);
            call->setState(Call::Active);
            return true;
        } else {
            _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__);
        }
yanmorin's avatar
   
yanmorin committed
472
    }
alexandresavard's avatar
alexandresavard committed
473
    terminateOneCall(call->getCallId());
474
475
    removeCall(call->getCallId());
    return false;
yanmorin's avatar
   
yanmorin committed
476
477
}

478
    bool
yanmorin's avatar
   
yanmorin committed
479
SIPVoIPLink::hangup(const CallID& id)
jpbl's avatar
jpbl committed
480
{
481
482
483
484
485
486
    pj_status_t status;
    pjsip_tx_data *tdata = NULL;
    SIPCall* call;

    call = getSIPCall(id);

Emmanuel Milou's avatar
Emmanuel Milou committed
487
    if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; }  
yanmorin's avatar
   
yanmorin committed
488

489
490
491
    // User hangup current call. Notify peer
    status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata);
    if(status != PJ_SUCCESS)
Emmanuel Milou's avatar
Emmanuel Milou committed
492
        return false;
493
494
495
496
497
498

    if(tdata == NULL)
        return true;

    status = pjsip_inv_send_msg(call->getInvSession(), tdata);
    if(status != PJ_SUCCESS)
Emmanuel Milou's avatar
Emmanuel Milou committed
499
        return false;
500
501

    call->getInvSession()->mod_data[getModId()] = NULL;
alexandresavard's avatar
alexandresavard committed
502
    
503

Emmanuel Milou's avatar
Emmanuel Milou committed
504
505
506
507
508
    // Release RTP thread
    if (Manager::instance().isCurrentCall(id)) {
        _debug("* SIP Info: Stopping AudioRTP for hangup\n");
        _audiortp->closeRtpSession();
    }
509
 
alexandresavard's avatar
alexandresavard committed
510
    terminateOneCall(id);
Emmanuel Milou's avatar
Emmanuel Milou committed
511
    removeCall(id);
512

Emmanuel Milou's avatar
Emmanuel Milou committed
513
    return true;
jpbl's avatar
jpbl committed
514
515
}

516
    bool
yanmorin's avatar
   
yanmorin committed
517
SIPVoIPLink::cancel(const CallID& id)
jpbl's avatar
jpbl committed
518
{
519
520
    SIPCall* call = getSIPCall(id);
    if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; }  
yanmorin's avatar
   
yanmorin committed
521

522
    _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); 
jpbl's avatar
jpbl committed
523

alexandresavard's avatar
alexandresavard committed
524
    terminateOneCall(id);
525
    removeCall(id);
526

527
    return true;
jpbl's avatar
jpbl committed
528
529
}

530
    bool
yanmorin's avatar
   
yanmorin committed
531
SIPVoIPLink::onhold(const CallID& id)
jpbl's avatar
jpbl committed
532
533
{

534
535
536
537
538
    pj_status_t status;
    pjsip_tx_data *tdata;
    pjmedia_sdp_attr *attr;
    pjmedia_sdp_session* local_sdp;
    SIPCall* call;
Yun Liu's avatar
Yun Liu committed
539

540
541
542
    call = getSIPCall(id);

    if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; }  
Yun Liu's avatar
Yun Liu committed
543

544

545
546
547
548
    // Stop sound
    call->setAudioStart(false);
    call->setState(Call::Hold);
    _debug("* SIP Info: Stopping AudioRTP for onhold action\n");
549
550
551
552
    //_mutexSIP.enterMutex();
        _audiortp->closeRtpSession();
    //_mutexSIP.leaveMutex();
    
553
    local_sdp = call->getLocalSDPSession();
Emmanuel Milou's avatar
Emmanuel Milou committed
554

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
    if( local_sdp == NULL ){
        _debug("! SIP Failure: unable to find local_sdp\n");
        return false;
    }

    /* Create re-INVITE with new offer */
    // Remove all the attributes with the specified name
    pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv");
    attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL);
    pjmedia_sdp_media_add_attr(local_sdp->media[0], attr);

    status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata);
    if( status != PJ_SUCCESS )
    {
        _debug("On hold: creation of the Re-invite request failed\n");
        return false;
    }
    /* Send the request */
    status = pjsip_inv_send_msg( call->getInvSession(), tdata);

    return (status == PJ_SUCCESS);
jpbl's avatar
jpbl committed
576
577
}

578
    bool 
yanmorin's avatar
   
yanmorin committed
579
SIPVoIPLink::offhold(const CallID& id)
jpbl's avatar
jpbl committed
580
{
581
    SIPCall *call;
582
583
584
585
    pj_status_t status;
    pjsip_tx_data *tdata;
    pjmedia_sdp_attr *attr;
    pjmedia_sdp_session* local_sdp;
Yun Liu's avatar
Yun Liu committed
586

587
    call = getSIPCall(id);
588

589
590
591
592
    if (call==0) { 
        _debug("! SIP Error: Call doesn't exist\n"); 
        return false; 
    }
jpbl's avatar
jpbl committed
593

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    local_sdp = call->getLocalSDPSession();
    if( local_sdp == NULL ){
        _debug("! SIP Failure: unable to find local_sdp\n");
        return false;
    }

    /* Create re-INVITE with new offer */
    // Remove all the attributes with the specified name
    pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly");
    attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL);
    pjmedia_sdp_media_add_attr(local_sdp->media[0], attr);

    status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata);
    if( status != PJ_SUCCESS )
    {
        _debug("Off hold: creation of the Re-invite request failed\n");
        return false;
    }

    /* Send the request */
    status = pjsip_inv_send_msg( call->getInvSession(), tdata);
    if( status != PJ_SUCCESS )
616
617
618
619
620
621
622
623
624
625
        return false;

    // Enable audio
    _debug("* SIP Info: Starting AudioRTP when offhold\n");
    call->setState(Call::Active);
    // it's sure that this is the current call id...
    if (_audiortp->createNewSession(call) < 0) {
        _debug("! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__);
        return false;
    }
626

627
    return true;
jpbl's avatar
jpbl committed
628
629
}

630
    bool 
yanmorin's avatar
   
yanmorin committed
631
SIPVoIPLink::transfer(const CallID& id, const std::string& to)
jpbl's avatar
jpbl committed
632
{
633
634
    SIPCall *call;
    std::string tmp_to;
635
636
637
638
639
    pjsip_evsub *sub;
    pjsip_tx_data *tdata;
    struct pjsip_evsub_user xfer_cb;
    pj_status_t status;
    pj_str_t dest;
640
641
642
    AccountID account_id;
    Account* account;

yanmorin's avatar
   
yanmorin committed
643

644
    call = getSIPCall(id);
645
    call->stopRecording();
646
647
    account_id = Manager::instance().getAccountFromCall(id);
    account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id));
648

649
650
651
652
653
654
655
    if (call==0) { 
        _debug("! SIP Failure: Call doesn't exist\n"); 
        return false; 
    }  

    tmp_to = SIPToHeader(to);
    if (tmp_to.find("@") == std::string::npos) {
656
        tmp_to = tmp_to + "@" + account->getHostname();
657
    }
jpbl's avatar
jpbl committed
658

659
    _debug("In transfer, tmp_to is %s\n", tmp_to.data());
660

661
662
663
664
665
666
667
668
669
670
671
    pj_strdup2(_pool, &dest, to.data());

    /* Create xfer client subscription. */
    pj_bzero(&xfer_cb, sizeof(xfer_cb));
    xfer_cb.on_evsub_state = &xfer_func_cb;

    status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub);
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to create xfer -- %d\n", status);
        return false;
    }
yanmorin's avatar
   
yanmorin committed
672

673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
    /* 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 not find the cooresponding
     * voiplink from the call any more. But the voiplink is useful!
     */
    AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
    pjsip_evsub_set_mod_data(sub, getModId(), this);

    /*
     * Create REFER request.
     */
    status = pjsip_xfer_initiate(sub, &dest, &tdata);
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to create REFER request -- %d\n", status);
        return false;
    }
yanmorin's avatar
   
yanmorin committed
689

690
691
692
693
694
695
696
697
    /* Send. */
    status = pjsip_xfer_send_request(sub, tdata);
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to send REFER request -- %d\n", status);
        return false;
    }

    return true;
jpbl's avatar
jpbl committed
698
699
}

Yun Liu's avatar
Yun Liu committed
700
701
702
703
704
705
bool SIPVoIPLink::transferStep2()
{
    _audiortp->closeRtpSession();
    return true;
}

706
    bool
yanmorin's avatar
   
yanmorin committed
707
SIPVoIPLink::refuse (const CallID& id)
jpbl's avatar
jpbl committed
708
{
709
    SIPCall *call;
710
711
712
    pj_status_t status;
    pjsip_tx_data *tdata;

713
    _debug("SIPVoIPLink::refuse() : teh call is refused \n");
714
    call = getSIPCall(id);
yanmorin's avatar
   
yanmorin committed
715

716
717
718
719
    if (call==0) { 
        _debug("Call doesn't exist\n"); 
        return false; 
    }  
yanmorin's avatar
   
yanmorin committed
720

721
722
723
724
725
726
    // can't refuse outgoing call or connected
    if (!call->isIncoming() || call->getConnectionState() == Call::Connected) { 
        _debug("It's not an incoming call, or it's already answered\n");
        return false; 
    }

727
728
729
730
731
732
733
734
735
736
    // User refuse current call. Notify peer
    status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603
    if(status != PJ_SUCCESS)
        return false;

    status = pjsip_inv_send_msg(call->getInvSession(), tdata);
    if(status != PJ_SUCCESS)
        return false;

    call->getInvSession()->mod_data[getModId()] = NULL;
737

alexandresavard's avatar
alexandresavard committed
738
    terminateOneCall(id);
739
    return true;
jpbl's avatar
jpbl committed
740
741
}

alexandresavard's avatar
alexandresavard committed
742
743
744
void 
SIPVoIPLink::setRecording(const CallID& id)
{
alexandresavard's avatar
alexandresavard committed
745
746
747
748
749
  //SIPCall *call;
  //call = getSIPCall(id);
  
  //call->setRecording();

alexandresavard's avatar
alexandresavard committed
750
751
752
  _audiortp->setRecording();
}

alexandresavard's avatar
alexandresavard committed
753
754
755
756
757
758
759
760
761
bool
SIPVoIPLink::isRecording(const CallID& id)
{
  SIPCall *call;
  call = getSIPCall(id);
  
  return call->isRecording();
}

762
    bool 
763
SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code)
jpbl's avatar
jpbl committed
764
{
765

766
767
768
769
    SIPCall *call;
    int duration;
    const int body_len = 1000;
    char *dtmf_body;
770
771
772
773
774
    pj_status_t status;
    pjsip_tx_data *tdata;
    pj_str_t methodName, content;
    pjsip_method method;
    pjsip_media_type ctype;
775
776

    call = getSIPCall(id);
777

778
779
780
781
782
783
784
    if (call==0) { 
        _debug("Call doesn't exist\n"); 
        return false; 
    }

    duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH);
    dtmf_body = new char[body_len];
785

786
    snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration);
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818

    pj_strdup2(_pool, &methodName, "INFO");
    pjsip_method_init_np(&method, &methodName);

    /* Create request message. */
    status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, -1, &tdata);
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to create INFO request -- %d\n", status);
        return false;
    }

    /* Get MIME type */
    pj_strdup2(_pool, &ctype.type, "application");
    pj_strdup2(_pool, &ctype.subtype, "dtmf-relay");

    /* Create "application/dtmf-relay" message body. */
    pj_strdup2(_pool, &content, dtmf_body);
    tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, &ctype.subtype, &content);
    if (tdata->msg->body == NULL) {
        _debug("UserAgent: Unable to create msg body!\n");
        pjsip_tx_data_dec_ref(tdata);
        return false;
    }

    /* Send the request. */
    status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, getModId(), NULL);
    if (status != PJ_SUCCESS) {
        _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status);
        return false;
    }

    return true;
819
820
}

821
    bool
yanmorin's avatar
   
yanmorin committed
822
SIPVoIPLink::SIPOutgoingInvite(SIPCall* call) 
jpbl's avatar
jpbl committed
823
{
824
825
826
827
828
829
    // If no SIP proxy setting for direct call with only IP address
    if (!SIPStartCall(call, "")) {
        _debug("! SIP Failure: call not started\n");
        return false;
    }
    return true;
jpbl's avatar
jpbl committed
830
831
}

832
    bool
833
SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) 
jpbl's avatar
jpbl committed
834
{
835
836
837
838
839
840
841
    std::string strTo, strFrom;
    pj_status_t status;
    pjsip_dialog *dialog;
    pjsip_tx_data *tdata;
    pj_str_t from, to, contact;
    AccountID id;
    SIPAccount *account;
yanmorin's avatar
   
yanmorin committed
842

843
844
    if (!call) 
        return false;
jpbl's avatar
jpbl committed
845

846
    id = Manager::instance().getAccountFromCall(call->getCallId());
847
848
    // Get the basic information about the callee account
    account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id));
Yun Liu's avatar
Yun Liu committed
849

850
851
    strTo = getSipTo(call->getPeerNumber(), account->getHostname());
    _debug("            To: %s\n", strTo.data());
852
853

    // Generate the from URI
854
    strFrom = "sip:" + account->getUsername() + "@" + account->getHostname();
855
856
857
858
859
860
861
862
863
864
865
866
867
868

    // pjsip need the from and to information in pj_str_t format
    pj_strdup2(_pool, &from, strFrom.data());
    pj_strdup2(_pool, &to, strTo.data());
    pj_strdup2(_pool, &contact, account->getContact().data());

    // create the dialog (UAC)
    status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from,
            &contact,
            &to,
            NULL,
            &dialog);
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);

Emmanuel Milou's avatar
Emmanuel Milou committed
869
    setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer());
870
871
872
873
874
875
876
877
878
    call->setIp(getLocalIP());

    // Building the local SDP offer
    call->createInitialOffer(_pool);

    // Create the invite session for this call
    pjsip_inv_session *inv;
    status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv);
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
jpbl's avatar
jpbl committed
879

880
881
    // Set auth information
    pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo());
jpbl's avatar
jpbl committed
882

883
884
    // Associate current call in the invite session
    inv->mod_data[getModId()] = call;
885

886
887
888
889
890
891
892
893
894
895
896
897
    status = pjsip_inv_invite(inv, &tdata);
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);

    // Associate current invite session in the call
    call->setInvSession(inv);

    status = pjsip_inv_send_msg(inv, tdata);
    if(status != PJ_SUCCESS) {
        return false;
    }

    return true;
yanmorin's avatar
   
yanmorin committed
898
}
jpbl's avatar
jpbl committed
899

900
std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostname) {
901
902
    // Form the From header field basis on configuration panel
    //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true;
yanmorin's avatar
   
yanmorin committed
903

904
    // add a @host if we are registered and there is no one inside the url
Yun Liu's avatar
Yun Liu committed
905
    if (to_url.find("@") == std::string::npos) {// && isRegistered) {
906
907
        if(!hostname.empty()) {
            return SIPToHeader(to_url + "@" + hostname);
908
909
910
        }
    }
    return SIPToHeader(to_url);
jpbl's avatar
jpbl committed
911
912
    }

Emmanuel Milou's avatar
Emmanuel Milou committed
913
914
915
916
917
918
919
    std::string SIPVoIPLink::SIPToHeader(const std::string& to) 
    {
        if (to.find("sip:") == std::string::npos) {
            return ("sip:" + to );
        } else {
            return to;
        }
920
    }
jpbl's avatar
jpbl committed
921

922
923
924
925
926
927
    bool
        SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED)
        {
            return true;
        }

Emmanuel Milou's avatar
Emmanuel Milou committed
928
929
930
931
932
933
934
935
936
937
    bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) 
    {
        // Setting Audio
        unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT;
        unsigned int callLocalExternAudioPort = callLocalAudioPort;
        if (stun) {
            // If use Stun server
            if (Manager::instance().behindNat(server, callLocalAudioPort)) {
                callLocalExternAudioPort = Manager::instance().getFirewallPort();
            }
938
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
939
940
        _debug("            Setting local audio port to: %d\n", callLocalAudioPort);
        _debug("            Setting local audio port (external) to: %d\n", callLocalExternAudioPort);
941

Emmanuel Milou's avatar
Emmanuel Milou committed
942
943
944
945
        // Set local audio port for SIPCall(id)
        call->setLocalIp(localIP);
        call->setLocalAudioPort(callLocalAudioPort);
        call->setLocalExternAudioPort(callLocalExternAudioPort);
946

Emmanuel Milou's avatar
Emmanuel Milou committed
947
948
        return true;
    }
jpbl's avatar
jpbl committed
949

alexandresavard's avatar
alexandresavard committed
950
951
952
953
954
955
956
957
958
959
960
961
962
void
SIPVoIPLink::SIPCallServerFailure(SIPCall *call) 
{
    //if (!event->response) { return; }
    //switch(event->response->status_code) {
    //case SIP_SERVICE_UNAVAILABLE: // 500
    //case SIP_BUSY_EVRYWHERE:     // 600
    //case SIP_DECLINE:             // 603
    //SIPCall* call = findSIPCallWithCid(event->cid);
    if (call != 0) {
        _debug("Server error!\n");
        CallID id = call->getCallId();
        Manager::instance().callFailure(id);
alexandresavard's avatar
alexandresavard committed
963
        terminateOneCall(id);
alexandresavard's avatar
alexandresavard committed
964
965
966
967
968
        removeCall(id);
    }
    //break;
    //}
}
969

alexandresavard's avatar
alexandresavard committed
970
971
972
void
SIPVoIPLink::SIPCallClosed(SIPCall *call) 
{
973

974
    _debug("SIPVoIPLink::SIPCallClosed():: function called when peer hangup");
alexandresavard's avatar
alexandresavard committed
975
976
977
    // it was without did before
    //SIPCall* call = findSIPCallWithCid(event->cid);
    if (!call) { return; }
978

alexandresavard's avatar
alexandresavard committed
979
980
981
982
983
984
985
986
987
    CallID id = call->getCallId();
    //call->setDid(event->did);
    if (Manager::instance().isCurrentCall(id)) {
        call->setAudioStart(false);
        _debug("* SIP Info: Stopping AudioRTP when closing\n");
        _audiortp->closeRtpSession();
    }
    _debug("After close RTP\n");
    Manager::instance().peerHungupCall(id);
alexandresavard's avatar
alexandresavard committed
988
    terminateOneCall(id);
alexandresavard's avatar
alexandresavard committed
989
990
991
    removeCall(id);
    _debug("After remove call ID\n");
}
992

alexandresavard's avatar
alexandresavard committed
993
994
995
996
997
998
999
1000
void
SIPVoIPLink::SIPCallReleased(SIPCall *call)
{
    // do cleanup if exists
    // only cid because did is always 0 in these case..
    //SIPCall* call = findSIPCallWithCid(event->cid);
    if (!call) { return; }