sipvoiplink.cpp 56.5 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Savoir-Faire Linux Inc.
3
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
4
 *  Author: Yun Liu <yun.liu@savoirfairelinux.com>
5
 *  Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
6
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
7
 *
jpbl's avatar
jpbl committed
8 9
 *  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
10
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
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.
21 22 23 24 25 26 27 28 29 30 31
 *
 *  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
32
 */
Emmanuel Milou's avatar
Emmanuel Milou committed
33

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

38 39
#include "sip_utils.h"

jpbl's avatar
jpbl committed
40
#include "sipvoiplink.h"
41
#include "manager.h"
42
#include "logger.h"
43

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

#include "dbus/dbusmanager.h"
#include "dbus/callmanager.h"
52
#include "dbus/configurationmanager.h"
53

54
#include "im/instant_messaging.h"
55

56 57
#include "audio/audiolayer.h"

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

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

69 70
#include <map>

71 72
using namespace sfl;

73 74 75
SIPVoIPLink *SIPVoIPLink::instance_ = 0;
bool SIPVoIPLink::destroyed_ = false;

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

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

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

84
/**
85 86 87 88
 * Set audio (SDP) configuration for a call
 * localport, localip, localexternalport
 * @param call a SIPCall valid pointer
 */
89
void setCallMediaLocal(SIPCall* call, const std::string &localIP);
90

91 92 93 94
static pj_caching_pool pool_cache, *cp_ = &pool_cache;
static pj_pool_t *pool_;
static pjsip_endpoint *endpt_;
static pjsip_module mod_ua_;
95
static pj_thread_t *thread_;
Emmanuel Milou's avatar
Emmanuel Milou committed
96

97
void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status);
98 99 100 101 102 103 104
void sdp_request_offer_cb(pjsip_inv_session *inv, const pjmedia_sdp_session *offer);
void sdp_create_offer_cb(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer);
void invite_session_state_changed_cb(pjsip_inv_session *inv, pjsip_event *e);
void outgoing_request_forked_cb(pjsip_inv_session *inv, pjsip_event *e);
void transaction_state_changed_cb(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
void registration_cb(pjsip_regc_cbparam *param);
pj_bool_t transaction_request_cb(pjsip_rx_data *rdata);
105
pj_bool_t transaction_response_cb(pjsip_rx_data *rdata) ;
Emmanuel Milou's avatar
Emmanuel Milou committed
106

107
void transfer_client_cb(pjsip_evsub *sub, pjsip_event *event);
Emmanuel Milou's avatar
Emmanuel Milou committed
108

109 110 111 112 113 114 115
/**
 * 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 *);

116 117 118
/**
 * Helper function to process refer function on call transfer
 */
119
void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata);
120

121 122 123 124 125 126 127 128 129 130 131
void handleIncomingOptions(pjsip_rx_data *rdata)
{
    pjsip_tx_data *tdata;

    if (pjsip_endpt_create_response(endpt_, rdata, PJSIP_SC_OK, NULL, &tdata) != PJ_SUCCESS)
        return;

#define ADD_HDR(hdr) do { \
    const pjsip_hdr *cap_hdr = hdr; \
    if (cap_hdr) \
    pjsip_msg_add_hdr (tdata->msg, (pjsip_hdr*) pjsip_hdr_clone (tdata->pool, cap_hdr)); \
Tristan Matthews's avatar
Tristan Matthews committed
132
} while (0)
133 134 135 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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
#define ADD_CAP(cap) ADD_HDR(pjsip_endpt_get_capability(endpt_, cap, NULL));

    ADD_CAP(PJSIP_H_ALLOW);
    ADD_CAP(PJSIP_H_ACCEPT);
    ADD_CAP(PJSIP_H_SUPPORTED);
    ADD_HDR(pjsip_evsub_get_allow_events_hdr(NULL));

    pjsip_response_addr res_addr;
    pjsip_get_response_addr(tdata->pool, rdata, &res_addr);

    if (pjsip_endpt_send_response(endpt_, &res_addr, tdata, NULL, NULL) != PJ_SUCCESS)
        pjsip_tx_data_dec_ref(tdata);
}

pj_bool_t transaction_response_cb(pjsip_rx_data *rdata)
{
    pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);

    if (!dlg)
        return PJ_SUCCESS;

    pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);

    if (!tsx or tsx->method.id != PJSIP_INVITE_METHOD)
        return PJ_SUCCESS;

    if (tsx->status_code / 100 == 2) {
        /**
         * Send an ACK message inside a transaction. PJSIP send automatically, non-2xx ACK response.
         * ACK for a 2xx response must be send using this method.
         */
        pjsip_tx_data *tdata;
        pjsip_dlg_create_request(dlg, &pjsip_ack_method, rdata->msg_info.cseq->cseq, &tdata);
        pjsip_dlg_send_request(dlg, tdata, -1, NULL);
    }

    return PJ_SUCCESS;
}

pj_bool_t transaction_request_cb(pjsip_rx_data *rdata)
{
    pjsip_method *method = &rdata->msg_info.msg->line.req.method;

    if (method->id == PJSIP_ACK_METHOD && pjsip_rdata_get_dlg(rdata))
        return true;

    pjsip_sip_uri *sip_to_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(rdata->msg_info.to->uri);
    pjsip_sip_uri *sip_from_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(rdata->msg_info.from->uri);
    std::string userName(sip_to_uri->user.ptr, sip_to_uri->user.slen);
    std::string server(sip_from_uri->host.ptr, sip_from_uri->host.slen);
    std::string account_id(Manager::instance().getAccountIdFromNameAndServer(userName, server));

185
    std::string displayName(sip_utils::parseDisplayName(rdata->msg_info.msg_buf));
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

    if (method->id == PJSIP_OTHER_METHOD) {
        pj_str_t *str = &method->name;
        std::string request(str->ptr, str->slen);

        if (request.find("NOTIFY") != (size_t)-1) {
            int voicemail;

            if (sscanf((const char*)rdata->msg_info.msg->body->data, "Voice-Message: %d/", &voicemail) == 1 && voicemail != 0)
                Manager::instance().startVoiceMessageNotification(account_id, voicemail);
        }

        pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_OK, NULL, NULL, NULL);

        return true;
    } else if (method->id == PJSIP_OPTIONS_METHOD) {
        handleIncomingOptions(rdata);
        return true;
    } else if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
        pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL);
        return true;
    }

    SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id));

    pjmedia_sdp_session *r_sdp;
    pjsip_msg_body *body = rdata->msg_info.msg->body;

    if (!body || pjmedia_sdp_parse(rdata->tp_info.pool, (char*) body->data, body->len, &r_sdp) != PJ_SUCCESS)
        r_sdp = NULL;

    if (account->getActiveCodecs().empty()) {
        pjsip_endpt_respond_stateless(endpt_, rdata,
                                      PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL,
                                      NULL);
        return false;
    }

    // Verify that we can handle the request
    unsigned options = 0;

    if (pjsip_inv_verify_request(rdata, &options, NULL, NULL, endpt_, NULL) != PJ_SUCCESS) {
        pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL);
        return true;
    }

Tristan Matthews's avatar
Tristan Matthews committed
232
    Manager::instance().hookPreference.runHook(rdata->msg_info.msg);
233 234 235 236 237

    SIPCall* call = new SIPCall(Manager::instance().getNewCallID(), Call::INCOMING, cp_);
    Manager::instance().associateCallToAccount(call->getCallId(), account_id);

    // May use the published address as well
238
    std::string addrToUse = SipTransport::getInterfaceAddrFromName(account->getLocalInterface());
239 240 241 242
    std::string addrSdp = account->isStunEnabled()
                          ? account->getPublishedAddress()
                          : addrToUse;

243
    pjsip_tpselector *tp = SIPVoIPLink::instance()->sipTransport.initTransportSelector(account->transport_, call->getMemoryPool());
244 245

    if (addrToUse == "0.0.0.0")
246
        addrToUse = SipTransport::getSIPLocalIP();
247 248 249 250 251 252 253

    if (addrSdp == "0.0.0.0")
        addrSdp = addrToUse;

    char tmp[PJSIP_MAX_URL_SIZE];
    int length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
    std::string peerNumber(tmp, length);
254
    sip_utils::stripSipUriPrefix(peerNumber);
255 256 257 258 259 260 261 262 263 264

    call->setConnectionState(Call::PROGRESSING);
    call->setPeerNumber(peerNumber);
    call->setDisplayName(displayName);
    call->initRecFilename(peerNumber);

    setCallMediaLocal(call, addrToUse);

    call->getLocalSDP()->setLocalIP(addrSdp);

265 266
    call->getAudioRtp().initConfig();
    call->getAudioRtp().initSession();
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

    if (rdata->msg_info.msg->body) {
        char sdpbuffer[1000];
        int len = rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, sdpbuffer, sizeof sdpbuffer);

        if (len == -1) // error
            len = 0;

        std::string sdpoffer(sdpbuffer, len);
        size_t start = sdpoffer.find("a=crypto:");

        // Found crypto header in SDP
        if (start != std::string::npos) {
            CryptoOffer crypto_offer;
            crypto_offer.push_back(std::string(sdpoffer.substr(start, (sdpoffer.size() - start) - 1)));

283
            std::vector<sfl::CryptoSuiteDefinition> localCapabilities;
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

            for (int i = 0; i < 3; i++)
                localCapabilities.push_back(sfl::CryptoSuites[i]);

            sfl::SdesNegotiator sdesnego(localCapabilities, crypto_offer);

            if (sdesnego.negotiate()) {
                call->getAudioRtp().setRemoteCryptoInfo(sdesnego);
                call->getAudioRtp().initLocalCryptoInfo();
            }
        }
    }

    call->getLocalSDP()->receiveOffer(r_sdp, account->getActiveCodecs());

    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);
    call->getAudioRtp().start(static_cast<sfl::AudioCodec *>(audiocodec));

    pjsip_dialog* dialog;

    if (pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog) != PJ_SUCCESS) {
        delete call;
        pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL);
        return false;
    }

    pjsip_inv_create_uas(dialog, rdata, call->getLocalSDP()->getLocalSdpSession(), 0, &call->inv);

    PJ_ASSERT_RETURN(pjsip_dlg_set_transport(dialog, tp) == PJ_SUCCESS, 1);

    call->inv->mod_data[mod_ua_.id] = call;

    // Check whether Replaces header is present in the request and process accordingly.
    pjsip_dialog *replaced_dlg;
    pjsip_tx_data *response;

    if (pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response) != PJ_SUCCESS) {
        ERROR("Something wrong with Replaces request.");
        pjsip_endpt_respond_stateless(endpt_, rdata, 500 /* internal server error */, NULL, NULL, NULL);
    }

    // Check if call has been transfered
    pjsip_tx_data *tdata;

    if (replaced_dlg) { // If Replace header present
        // Always answer the new INVITE with 200, regardless whether
        // the replaced call is in early or confirmed state.
        if (pjsip_inv_answer(call->inv, 200, NULL, NULL, &response) == PJ_SUCCESS)
            pjsip_inv_send_msg(call->inv, response);

        // Get the INVITE session associated with the replaced dialog.
        pjsip_inv_session *replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);

        // Disconnect the "replaced" INVITE session.
        if (pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS && tdata)
            pjsip_inv_send_msg(replaced_inv, tdata);
    } else { // Prooceed with normal call flow
        PJ_ASSERT_RETURN(pjsip_inv_initial_answer(call->inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata) == PJ_SUCCESS, 1);
        PJ_ASSERT_RETURN(pjsip_inv_send_msg(call->inv, tdata) == PJ_SUCCESS, 1);

        call->setConnectionState(Call::RINGING);

        Manager::instance().incomingCall(call, account_id);
        Manager::instance().getAccountLink(account_id)->addCall(call);
    }

    return true;
}
352 353
} // end anonymous namespace

Emmanuel Milou's avatar
Emmanuel Milou committed
354 355
/*************************************************************************************************/

356
SIPVoIPLink::SIPVoIPLink() : sipTransport(endpt_, cp_, pool_), evThread_(this)
357 358
{
#define TRY(ret) do { \
359 360
    if (ret != PJ_SUCCESS) \
    throw VoipLinkException(#ret " failed"); \
Tristan Matthews's avatar
Tristan Matthews committed
361
} while (0)
362 363

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

365 366
    TRY(pj_init());
    TRY(pjlib_util_init());
367 368
    // From 0 (min) to 6 (max)
    pj_log_set_level(Logger::getDebugMode() ? 6 : 0);
369
    TRY(pjnath_init());
370

371 372
    pj_caching_pool_init(cp_, &pj_pool_factory_default_policy, 0);
    pool_ = pj_pool_create(&cp_->factory, "sflphone", 4000, 4000, NULL);
373

374
    if (!pool_)
375
        throw VoipLinkException("UserAgent: Could not initialize memory pool");
Alexandre Savard's avatar
Alexandre Savard committed
376

377
    TRY(pjsip_endpt_create(&cp_->factory, pj_gethostname()->ptr, &endpt_));
378

379 380 381 382 383
    sipTransport.setEndpoint(endpt_);
    sipTransport.setCachingPool(cp_);
    sipTransport.setPool(pool_);

    if (SipTransport::getSIPLocalIP().empty())
384 385
        throw VoipLinkException("UserAgent: Unable to determine network capabilities");

386 387 388 389
    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_));
390 391

    // Initialize and register sflphone module
392 393 394 395 396 397
    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_));
398

399 400
    TRY(pjsip_evsub_init_module(endpt_));
    TRY(pjsip_xfer_init_module(endpt_));
401 402

    static const pjsip_inv_callback inv_cb = {
403 404 405 406 407 408 409 410
        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,
411
    };
412
    TRY(pjsip_inv_usage_init(endpt_, &inv_cb));
413 414

    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}
415
    pjsip_endpt_add_capability(endpt_, &mod_ua_, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed);
416 417

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

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

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

425
    TRY(pjsip_replaces_init_module(endpt_));
Tristan Matthews's avatar
Tristan Matthews committed
426
#undef TRY
427

428 429
    handlingEvents_ = true;
    evThread_.start();
yanmorin's avatar
 
yanmorin committed
430
}
jpbl's avatar
jpbl committed
431

Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
432 433
SIPVoIPLink::~SIPVoIPLink()
{
434 435 436 437 438 439 440
    handlingEvents_ = false;
    if (thread_) {
        pj_thread_join(thread_);
        pj_thread_destroy(thread_);
        DEBUG("PJ thread destroy finished");
        thread_ = 0;
    }
441 442

    const pj_time_val tv = {0, 10};
443 444
    pjsip_endpt_handle_events(endpt_, &tv);
    pjsip_endpt_destroy(endpt_);
445

446 447
    pj_pool_release(pool_);
    pj_caching_pool_destroy(cp_);
448 449

    pj_shutdown();
450 451
}

452
SIPVoIPLink* SIPVoIPLink::instance()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
453
{
454 455 456 457 458 459 460 461 462 463 464
    assert(!destroyed_);
    if (!instance_)
        instance_ = new SIPVoIPLink;
    return instance_;
}

void SIPVoIPLink::destroy()
{
    delete instance_;
    destroyed_ = true;
    instance_ = 0;
yanmorin's avatar
 
yanmorin committed
465 466
}

467 468
// Called from EventThread::run (not main thread)
bool SIPVoIPLink::getEvent()
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
469
{
470
    static pj_thread_desc desc;
471

472
    // We have to register the external thread so it could access the pjsip frameworks
473 474
    if (!pj_thread_is_registered()) {
        DEBUG("%s: Registering thread", __PRETTY_FUNCTION__);
475
        pj_thread_register(NULL, desc, &thread_);
476
    }
yanmorin's avatar
 
yanmorin committed
477

Rafaël Carré's avatar
Rafaël Carré committed
478
    static const pj_time_val timeout = {0, 10};
479
    pjsip_endpt_handle_events(endpt_, &timeout);
480
    return handlingEvents_;
481
}
482

483
void SIPVoIPLink::sendRegister(Account *a)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
484
{
485
    SIPAccount *account = dynamic_cast<SIPAccount*>(a);
486
    sipTransport.createSipTransport(account);
487

488 489
    account->setRegister(true);
    account->setRegistrationState(Trying);
490

491
    pjsip_regc *regc = account->getRegistrationInfo();
492

493
    if (pjsip_regc_create(endpt_, (void *) account, &registration_cb, &regc) != PJ_SUCCESS)
494 495 496
        throw VoipLinkException("UserAgent: Unable to create regc structure.");

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

498 499 500
    // std::string address, port;
    // findLocalAddressFromUri(srvUri, account->transport_, address, port);
    pj_str_t pjSrv = pj_str((char*) srvUri.c_str());
501

502
    // Generate the FROM header
503
    std::string from(account->getFromUri());
504
    pj_str_t pjFrom = pj_str((char*) from.c_str());
505

506
    // Get the contact header for this account
507
    std::string contact(account->getContactHeader());
508
    pj_str_t pjContact = pj_str((char*) contact.c_str());
509

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

513
    if (!account->getServiceRoute().empty())
514
        pjsip_regc_set_route_set(regc, sip_utils::createRouteSet(account->getServiceRoute(), pool_));
515

516
    pjsip_regc_set_credentials(regc, account->getCredentialCount(), account->getCredInfo());
517

518
    pjsip_hdr hdr_list;
519 520
    pj_list_init(&hdr_list);
    std::string useragent(account->getUserAgentName());
521
    pj_str_t pJuseragent = pj_str((char*) useragent.c_str());
522
    const pj_str_t STR_USER_AGENT = { (char*) "User-Agent", 10 };
523

524
    pjsip_generic_string_hdr *h = pjsip_generic_string_hdr_create(pool_, &STR_USER_AGENT, &pJuseragent);
525 526
    pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
    pjsip_regc_add_headers(regc, &hdr_list);
jpbl's avatar
jpbl committed
527

528

529
    pjsip_tx_data *tdata;
Emmanuel Milou's avatar
Emmanuel Milou committed
530

531 532
    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
533

534
    if (pjsip_regc_set_transport(regc, sipTransport.initTransportSelector(account->transport_, pool_)) != PJ_SUCCESS)
535
        throw VoipLinkException("Unable to set transport");
536

537 538
    // decrease transport's ref count, counter incrementation is managed when acquiring transport
    pjsip_transport_dec_ref(account->transport_);
539

540 541 542
    // 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");
543

544 545 546 547 548 549
    // 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);
550 551 552 553

    // start the periodic registration request based on Expire header
    // account determines itself if a keep alive is required
    account->startKeepAliveTimer();
554 555
}

556
void SIPVoIPLink::sendUnregister(Account *a)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
557
{
558
    SIPAccount *account = dynamic_cast<SIPAccount *>(a);
559

560
    // This may occurs if account failed to register and is in state INVALID
561
    if (!account->isRegistered()) {
562
        account->setRegistrationState(Unregistered);
563
        return;
564 565
    }

566 567 568
    // Make sure to cancel any ongoing timers before unregister
    account->stopKeepAliveTimer();

569
    pjsip_regc *regc = account->getRegistrationInfo();
570

571
    if (!regc)
572
        throw VoipLinkException("Registration structure is NULL");
573

574
    pjsip_tx_data *tdata = NULL;
575 576

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

579
    if (pjsip_regc_send(regc, tdata) != PJ_SUCCESS)
580
        throw VoipLinkException("Unable to send request to unregister sip account");
581

582
    account->setRegister(false);
jpbl's avatar
jpbl committed
583 584
}

585
void SIPVoIPLink::registerKeepAliveTimer(pj_timer_entry &timer, pj_time_val &delay)
586
{
587
    if (timer.id == -1)
588 589
        WARN("UserAgent: Timer already scheduled");

590 591 592 593 594 595 596 597 598 599 600 601
    switch (pjsip_endpt_schedule_timer(endpt_, &timer, &delay)) {
        case PJ_SUCCESS:
            break;
        default:
            ERROR("UserAgent: Could not schedule new timer in pjsip endpoint");
            /* fallthrough */
        case PJ_EINVAL:
            ERROR("UserAgent: Invalid timer or delay entry");
            break;
        case PJ_EINVALIDOP:
            ERROR("Invalid timer entry, maybe already scheduled");
            break;
602
    }
603 604 605 606
}

void SIPVoIPLink::cancelKeepAliveTimer(pj_timer_entry& timer)
{
607
    pjsip_endpt_cancel_timer(endpt_, &timer);
608 609
}

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

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

617
    SIPCall* call = new SIPCall(id, Call::OUTGOING, cp_);
Emmanuel Milou's avatar
Emmanuel Milou committed
618

619
    // If toUri is not a well formatted sip URI, use account information to process it
620
    std::string toUri;
621

622 623
    if (toUrl.find("sip:") != std::string::npos or
        toUrl.find("sips:") != std::string::npos)
624
        toUri = toUrl;
625
    else
626
        toUri = account->getToUri(toUrl);
627

628
    call->setPeerNumber(toUri);
629
    std::string localAddr(SipTransport::getInterfaceAddrFromName(account->getLocalInterface()));
630

631
    if (localAddr == "0.0.0.0")
632
        localAddr = SipTransport::getSIPLocalIP();
633

634
    setCallMediaLocal(call, localAddr);
Emmanuel Milou's avatar
Emmanuel Milou committed
635

636
    // May use the published address as well
637
    std::string addrSdp = account->isStunEnabled() ?
638
    account->getPublishedAddress() :
639
    SipTransport::getInterfaceAddrFromName(account->getLocalInterface());
640

641
    if (addrSdp == "0.0.0.0")
642
        addrSdp = SipTransport::getSIPLocalIP();
Emmanuel Milou's avatar
Emmanuel Milou committed
643

644 645 646
    // 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
647 648
    sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);

649
    if (audiocodec == NULL) {
650 651
        delete call;
        throw VoipLinkException("Could not instantiate codec for early media");
652
    }
653

654
    try {
655 656
        call->getAudioRtp().initConfig();
        call->getAudioRtp().initSession();
657 658
        call->getAudioRtp().initLocalCryptoInfo();
        call->getAudioRtp().start(static_cast<sfl::AudioCodec *>(audiocodec));
659
    } catch (...) {
660
        delete call;
661 662
        throw VoipLinkException("Could not start rtp session for early media");
    }
663

664
    call->initRecFilename(toUrl);
665

666 667
    call->getLocalSDP()->setLocalIP(addrSdp);
    call->getLocalSDP()->createOffer(account->getActiveCodecs());
668

669 670 671 672
    if (!SIPStartCall(call)) {
        delete call;
        throw VoipLinkException("Could not send outgoing INVITE request for new call");
    }
jpbl's avatar
jpbl committed
673

674
    return call;
675
}
676

677
void
678
SIPVoIPLink::answer(Call *call)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
679
{
680
    if (!call)
681
        return;
682
    call->answer();
Yun Liu's avatar
Yun Liu committed
683 684
}

685
void
686
SIPVoIPLink::hangup(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
687
{
688
    SIPCall* call = getSIPCall(id);
689

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

693
    if (account == NULL)
694
        throw VoipLinkException("Could not find account for this call");
695

696
    pjsip_inv_session *inv = call->inv;
697

698
    if (inv == NULL)
699
        throw VoipLinkException("No invite session for this call");
700

701
    // Looks for sip routes
Tristan Matthews's avatar
Tristan Matthews committed
702
    if (not account->getServiceRoute().empty()) {
703
        pjsip_route_hdr *route_set = sip_utils::createRouteSet(account->getServiceRoute(), inv->pool);
704
        pjsip_dlg_set_route_set(inv->dlg, route_set);
705
    }
706

707
    pjsip_tx_data *tdata = NULL;
708

709
    // User hangup current call. Notify peer
710
    if (pjsip_inv_end_session(inv, 404, NULL, &tdata) != PJ_SUCCESS || !tdata)
711
        return;
712

713
    if (pjsip_inv_send_msg(inv, tdata) != PJ_SUCCESS)
714
        return;
715

716
    // Make sure user data is NULL in callbacks
717
    inv->mod_data[mod_ua_.id] = NULL;
718

719
    if (Manager::instance().isCurrentCall(id))
720
        call->getAudioRtp().stop();
721

722
    removeCall(id);
alexandresavard's avatar
alexandresavard committed
723 724
}

725
void
726
SIPVoIPLink::peerHungup(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
727
{
728
    SIPCall* call = getSIPCall(id);
Alexandre Savard's avatar
Alexandre Savard committed
729

730
    // User hangup current call. Notify peer
731
    pjsip_tx_data *tdata = NULL;
732 733

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

736
    if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS)
737
        return;
738

739
    // Make sure user data is NULL in callbacks
740
    call->inv->mod_data[mod_ua_.id ] = NULL;
741

742
    if (Manager::instance().isCurrentCall(id))
743
        call->getAudioRtp().stop();
744

745
    removeCall(id);
746 747
}

748
void
749
SIPVoIPLink::onhold(const std::string& id)
Emmanuel Milou's avatar
[#2402]  
Emmanuel Milou committed
750
{
751
    SIPCall *call = getSIPCall(id);
752
    call->setState(Call::HOLD);
753
    call->getAudioRtp().stop();
754 755

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

757
    if (!sdpSession)
758
        throw VoipLinkException("Could not find sdp session");
759 760 761 762 763

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

764
    SIPSessionReinvite(call);
765 766
}

767
void
768
SIPVoIPLink::offhold(const std::string& id)
769
{
770 771 772
    SIPCall *call = getSIPCall(id);

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

774
    if (sdpSession == NULL)
775
        throw VoipLinkException("Could not find sdp session");
776

777
    try {
778
        int pl = PAYLOAD_CODEC_ULAW;
779
        sfl::Codec *sessionMedia = sdpSession->getSessionMedia();
780

781
        if (sessionMedia)
782
            pl = sessionMedia->getPayloadType();
783

784
        // Create a new instance for this codec
785 786
        sfl::Codec* audiocodec = Manager::instance().audioCodecFactory.instantiateCodec(pl);

787
        if (audiocodec == NULL)
788
            throw VoipLinkException("Could not instantiate codec");
789

790 791
        call->getAudioRtp().initConfig();
        call->getAudioRtp().initSession();
792
        call->getAudioRtp().start(static_cast<sfl::AudioCodec *>(audiocodec));
793
    } catch (const SdpException &e) {
794
        ERROR("UserAgent: Exception: %s", e.what());
795 796
    } catch (...) {
        throw VoipLinkException("Could not create audio rtp session");
797 798 799 800 801
    }

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

803
    if (SIPSessionReinvite(call) == PJ_SUCCESS)
Alexandre Savard's avatar