diff options
Diffstat (limited to 'pjsip/src')
-rw-r--r-- | pjsip/src/pjsip-simple/evsub_msg.c | 2 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/pidf.c | 7 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_100rel.c | 70 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 25 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_multipart.c | 5 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transaction.c | 28 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util.c | 17 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 56 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 73 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 35 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_media.c | 55 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 44 |
12 files changed, 320 insertions, 97 deletions
diff --git a/pjsip/src/pjsip-simple/evsub_msg.c b/pjsip/src/pjsip-simple/evsub_msg.c index 77e4b489..df2dd550 100644 --- a/pjsip/src/pjsip-simple/evsub_msg.c +++ b/pjsip/src/pjsip-simple/evsub_msg.c @@ -295,7 +295,7 @@ static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) */ PJ_DEF(void) pjsip_evsub_init_parser(void) { - pjsip_register_hdr_parser( "Event", NULL, + pjsip_register_hdr_parser( "Event", "o", &parse_hdr_event); pjsip_register_hdr_parser( "Subscription-State", NULL, diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c index 4787d9a8..b90725d4 100644 --- a/pjsip/src/pjsip-simple/pidf.c +++ b/pjsip/src/pjsip-simple/pidf.c @@ -324,15 +324,16 @@ PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); - pj_assert(node != NULL); + if (!node) + return PJ_FALSE; return pj_stricmp(&node->content, &OPEN)==0; } PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) { pj_xml_node *node = pj_xml_find_node(st, &BASIC); - pj_assert(node != NULL); - node->content = open ? OPEN : CLOSED; + if (node) + node->content = open ? OPEN : CLOSED; } PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c index 5d49731e..d2bb1d42 100644 --- a/pjsip/src/pjsip-ua/sip_100rel.c +++ b/pjsip/src/pjsip-ua/sip_100rel.c @@ -105,8 +105,10 @@ typedef struct uas_state_t /* UAC state */ typedef struct uac_state_t { - pj_int32_t cseq; - pj_uint32_t rseq; /* Initialized to -1 */ + pj_str_t tag; /* To tag */ + pj_int32_t cseq; + pj_uint32_t rseq; /* Initialized to -1 */ + struct uac_state_t *next; /* next call leg */ } uac_state_t; @@ -115,7 +117,7 @@ struct dlg_data { pjsip_inv_session *inv; uas_state_t *uas_state; - uac_state_t *uac_state; + uac_state_t *uac_state_list; }; @@ -231,6 +233,8 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_tx_data **p_tdata) { dlg_data *dd; + uac_state_t *uac_state = NULL; + const pj_str_t *to_tag = &rdata->msg_info.to->tag; pjsip_transaction *tsx; pjsip_msg *msg; pjsip_generic_string_hdr *rseq_hdr; @@ -261,41 +265,51 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); if (rseq_hdr == NULL) { PJ_LOG(4,(dd->inv->dlg->obj_name, - "Ignoring provisional response with no RSeq header")); + "Ignoring 100rel response with no RSeq header")); return PJSIP_EMISSINGHDR; } rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); + /* Find UAC state for the specified call leg */ + uac_state = dd->uac_state_list; + while (uac_state) { + if (pj_strcmp(&uac_state->tag, to_tag)==0) + break; + uac_state = uac_state->next; + } + /* Create new UAC state if we don't have one */ - if (dd->uac_state == NULL) { - dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, - uac_state_t); - dd->uac_state->cseq = rdata->msg_info.cseq->cseq; - dd->uac_state->rseq = rseq - 1; + if (uac_state == NULL) { + uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t); + uac_state->cseq = rdata->msg_info.cseq->cseq; + uac_state->rseq = rseq - 1; + pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag); + uac_state->next = dd->uac_state_list; + dd->uac_state_list = uac_state; } - /* If this is from new INVITE transaction, reset UAC state */ - if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) { - dd->uac_state->cseq = rdata->msg_info.cseq->cseq; - dd->uac_state->rseq = rseq - 1; + /* If this is from new INVITE transaction, reset UAC state. */ + if (rdata->msg_info.cseq->cseq != uac_state->cseq) { + uac_state->cseq = rdata->msg_info.cseq->cseq; + uac_state->rseq = rseq - 1; } /* Ignore provisional response retransmission */ - if (rseq <= dd->uac_state->rseq) { + if (rseq <= uac_state->rseq) { /* This should have been handled before */ return PJ_EIGNORED; /* Ignore provisional response with out-of-order RSeq */ - } else if (rseq != dd->uac_state->rseq + 1) { + } else if (rseq != uac_state->rseq + 1) { PJ_LOG(4,(dd->inv->dlg->obj_name, - "Ignoring provisional response because RSeq jump " + "Ignoring 100rel response because RSeq jump " "(expecting %u, got %u)", - dd->uac_state->rseq+1, rseq)); + uac_state->rseq+1, rseq)); return PJ_EIGNORED; } /* Update our RSeq */ - dd->uac_state->rseq = rseq; + uac_state->rseq = rseq; /* Create PRACK */ status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, @@ -303,6 +317,26 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, if (status != PJ_SUCCESS) return status; + /* If this response is a forked response from a different call-leg, + * update the req URI (https://trac.pjsip.org/repos/ticket/1364) + */ + if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { + const pjsip_contact_hdr *mhdr; + + mhdr = (const pjsip_contact_hdr*) + pjsip_msg_find_hdr(rdata->msg_info.msg, + PJSIP_H_CONTACT, NULL); + if (!mhdr || !mhdr->uri) { + PJ_LOG(4,(dd->inv->dlg->obj_name, + "Ignoring 100rel response with no or " + "invalid Contact header")); + pjsip_tx_data_dec_ref(tdata); + return PJ_EIGNORED; + } + tdata->msg->line.req.uri = (pjsip_uri*) + pjsip_uri_clone(tdata->pool, mhdr->uri); + } + /* Create RAck header */ rack.ptr = rack_buf; rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index eca737eb..46153c9f 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -195,8 +195,18 @@ void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, pjsip_event *e) { pjsip_inv_state prev_state = inv->state; + pj_bool_t dont_notify = PJ_FALSE; pj_status_t status; + /* Prevent STATE_CALLING from being reported more than once because + * of authentication + * https://trac.pjsip.org/repos/ticket/1318 + */ + if (state==PJSIP_INV_STATE_CALLING && + (inv->cb_called & (1 << PJSIP_INV_STATE_CALLING)) != 0) + { + dont_notify = PJ_TRUE; + } /* If state is confirmed, check that SDP negotiation is done, * otherwise disconnect the session. @@ -224,8 +234,11 @@ void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, pj_assert(inv->state != PJSIP_INV_STATE_DISCONNECTED || inv->cause != 0); + /* Mark the callback as called for this state */ + inv->cb_called |= (1 << state); + /* Call on_state_changed() callback. */ - if (mod_inv.cb.on_state_changed && inv->notify) + if (mod_inv.cb.on_state_changed && inv->notify && !dont_notify) (*mod_inv.cb.on_state_changed)(inv, e); /* Only decrement when previous state is not already DISCONNECTED */ @@ -4116,6 +4129,16 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) /* Not Acceptable */ const pjsip_hdr *accept; + /* The incoming SDP is unacceptable. If the SDP negotiator + * state has just been changed, i.e: DONE -> REMOTE_OFFER, + * revert it back. + */ + if (pjmedia_sdp_neg_get_state(inv->neg) == + PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) + { + pjmedia_sdp_neg_cancel_offer(inv->neg); + } + status = pjsip_dlg_create_response(inv->dlg, rdata, 488, NULL, &tdata); if (status != PJ_SUCCESS) diff --git a/pjsip/src/pjsip/sip_multipart.c b/pjsip/src/pjsip/sip_multipart.c index c4ae647e..45c7fcaa 100644 --- a/pjsip/src/pjsip/sip_multipart.c +++ b/pjsip/src/pjsip/sip_multipart.c @@ -81,10 +81,13 @@ static int multipart_print_body(struct pjsip_msg_body *msg_body, /* Print optional headers */ hdr = part->hdr.next; while (hdr != &part->hdr) { - int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT()); + int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, + SIZE_LEFT()-2); if (printed < 0) return -1; p += printed; + *p++ = '\r'; + *p++ = '\n'; hdr = hdr->next; } diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index bdc7b26f..c127162b 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -146,13 +146,6 @@ static pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, #define TIMER_INACTIVE 0 #define TIMER_ACTIVE 1 -/* Delay for 1xx retransmission (should be 60 seconds). - * Specify 0 to disable this feature - */ -#ifndef PJSIP_TSX_1XX_RETRANS_DELAY -# define PJSIP_TSX_1XX_RETRANS_DELAY 60 -#endif - /* Prototypes. */ static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck); @@ -2118,7 +2111,6 @@ PJ_DEF(pj_status_t) pjsip_tsx_retransmit_no_state(pjsip_transaction *tsx, */ static void tsx_resched_retransmission( pjsip_transaction *tsx ) { - pj_time_val timeout; pj_uint32_t msec_time; pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0); @@ -2151,11 +2143,15 @@ static void tsx_resched_retransmission( pjsip_transaction *tsx ) } } - timeout.sec = msec_time / 1000; - timeout.msec = msec_time % 1000; - tsx->retransmit_timer.id = TIMER_ACTIVE; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, - &timeout); + if (msec_time != 0) { + pj_time_val timeout; + + timeout.sec = msec_time / 1000; + timeout.msec = msec_time % 1000; + tsx->retransmit_timer.id = TIMER_ACTIVE; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, + &timeout); + } } /* @@ -2987,6 +2983,12 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, timeout.sec = timeout.msec = 0; } lock_timer(tsx); + /* In the short period above timer may have been inserted + * by set_timeout() (by CANCEL). Cancel it if necessary. See: + * https://trac.pjsip.org/repos/ticket/1374 + */ + if (tsx->timeout_timer.id) + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); tsx->timeout_timer.id = TIMER_ACTIVE; pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); unlock_timer(tsx); diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index bdf8fe6a..4f82a1a6 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -813,6 +813,8 @@ static pj_status_t get_dest_info(const pjsip_uri *target_uri, if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { pjsip_uri *uri = (pjsip_uri*) target_uri; const pjsip_sip_uri *url=(const pjsip_sip_uri*)pjsip_uri_get_uri(uri); + unsigned flag; + dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); if (url->maddr_param.slen) pj_strdup(pool, &dest_info->addr.host, &url->maddr_param); @@ -821,6 +823,18 @@ static pj_status_t get_dest_info(const pjsip_uri *target_uri, dest_info->addr.port = url->port; dest_info->type = pjsip_transport_get_type_from_name(&url->transport_param); + /* Double-check that the transport parameter match. + * Sample case: sips:host;transport=tcp + * See https://trac.pjsip.org/repos/ticket/1319 + */ + flag = pjsip_transport_get_flag_from_type(dest_info->type); + if ((flag & dest_info->flag) != dest_info->flag) { + pjsip_transport_type_e t; + + t = pjsip_transport_get_type_from_flag(dest_info->flag); + if (t != PJSIP_TRANSPORT_UNSPECIFIED) + dest_info->type = t; + } } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { pjsip_uri *uri = (pjsip_uri*) target_uri; @@ -1390,6 +1404,9 @@ static void send_raw_resolver_callback( pj_status_t status, pj_assert(addr->count != 0); + /* Avoid tdata destroyed by pjsip_tpmgr_send_raw(). */ + pjsip_tx_data_add_ref(sraw_data->tdata); + data_len = sraw_data->tdata->buf.cur - sraw_data->tdata->buf.start; status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(sraw_data->endpt), addr->entry[0].type, diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 9bcd1ddd..226f5d1f 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -470,9 +470,10 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, (int)cfg->id.slen, cfg->id.ptr, id)); /* If accounts has registration enabled, start registration */ - if (pjsua_var.acc[id].cfg.reg_uri.slen) - pjsua_acc_set_registration(id, PJ_TRUE); - else { + if (pjsua_var.acc[id].cfg.reg_uri.slen) { + if (pjsua_var.acc[id].cfg.register_on_acc_add) + pjsua_acc_set_registration(id, PJ_TRUE); + } else { /* Otherwise subscribe to MWI, if it's enabled */ if (pjsua_var.acc[id].cfg.mwi_enabled) pjsua_start_mwi(&pjsua_var.acc[id]); @@ -603,7 +604,7 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id) } /* Delete server presence subscription */ - pjsua_pres_delete_acc(acc_id); + pjsua_pres_delete_acc(acc_id, 0); /* Release account pool */ if (pjsua_var.acc[acc_id].pool) { @@ -833,7 +834,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, if (acc->cfg.publish_enabled != cfg->publish_enabled) { acc->cfg.publish_enabled = cfg->publish_enabled; if (!acc->cfg.publish_enabled) - pjsua_pres_unpublish(acc); + pjsua_pres_unpublish(acc, 0); else update_reg = PJ_TRUE; } @@ -992,6 +993,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, acc->cfg.unreg_timeout = cfg->unreg_timeout; acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite; acc->cfg.reg_retry_interval = cfg->reg_retry_interval; + acc->cfg.reg_first_retry_interval = cfg->reg_first_retry_interval; acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail; if (acc->cfg.reg_delay_before_refresh != cfg->reg_delay_before_refresh) { acc->cfg.reg_delay_before_refresh = cfg->reg_delay_before_refresh; @@ -1393,7 +1395,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, tp->type_name, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, - ob, + (acc->cfg.use_rfc5626? ob: ""), (int)acc->cfg.contact_params.slen, acc->cfg.contact_params.ptr); if (len < 1) { @@ -1691,11 +1693,14 @@ static void regc_cb(struct pjsip_regc_cbparam *param) pjsua_acc *acc = (pjsua_acc*) param->token; - if (param->regc != acc->regc) + PJSUA_LOCK(); + + if (param->regc != acc->regc) { + PJSUA_UNLOCK(); return; + } pj_log_push_indent(); - PJSUA_LOCK(); /* * Print registration status. @@ -2054,7 +2059,7 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, goto on_return; } - pjsua_pres_unpublish(&pjsua_var.acc[acc_id]); + pjsua_pres_unpublish(&pjsua_var.acc[acc_id], 0); status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata); } @@ -2070,6 +2075,10 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, ®_info); pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport; + + if (pjsua_var.ua_cfg.cb.on_reg_started) { + (*pjsua_var.ua_cfg.cb.on_reg_started)(acc_id, renew); + } } if (status != PJ_SUCCESS) { @@ -2529,10 +2538,11 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, /* Create the contact header */ contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, - "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s", + "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s", + (acc->display.slen?"\"" : ""), (int)acc->display.slen, acc->display.ptr, - (acc->display.slen?" " : ""), + (acc->display.slen?"\" " : ""), (secure ? PJSUA_SECURE_SCHEME : "sip"), (int)acc->user_part.slen, acc->user_part.ptr, @@ -2545,7 +2555,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, transport_param, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, - ob, + (acc->cfg.use_rfc5626? ob: ""), (int)acc->cfg.contact_params.slen, acc->cfg.contact_params.ptr); @@ -2687,10 +2697,11 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, /* Create the contact header */ contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, - "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + (acc->display.slen?"\"" : ""), (int)acc->display.slen, acc->display.ptr, - (acc->display.slen?" " : ""), + (acc->display.slen?"\" " : ""), (secure ? PJSUA_SECURE_SCHEME : "sip"), (int)acc->user_part.slen, acc->user_part.ptr, @@ -2807,8 +2818,23 @@ static void schedule_reregistration(pjsua_acc *acc) acc->auto_rereg.timer.user_data = acc; /* Reregistration attempt. The first attempt will be done immediately. */ - delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : 0; + delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : + acc->cfg.reg_first_retry_interval; delay.msec = 0; + + /* Randomize interval by +/- 10 secs */ + if (delay.sec >= 10) { + delay.msec = -10000 + (pj_rand() % 20000); + } else { + delay.sec = 0; + delay.msec = (pj_rand() % 10000); + } + pj_time_val_normalize(&delay); + + PJ_LOG(4,(THIS_FILE, + "Scheduling re-registration retry for acc %d in %u seconds..", + acc->index, delay.sec)); + pjsua_schedule_timer(&acc->auto_rereg.timer, &delay); } diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index ad96a4d6..02a78b93 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -971,7 +971,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) /* Verify that we can handle the request. */ options |= PJSIP_INV_SUPPORT_100REL; options |= PJSIP_INV_SUPPORT_TIMER; - if (pjsua_var.acc[acc_id].cfg.require_100rel) + if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY) options |= PJSIP_INV_REQUIRE_100REL; if (pjsua_var.media_cfg.enable_ice) options |= PJSIP_INV_SUPPORT_ICE; @@ -1047,6 +1047,19 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) options &= ~(PJSIP_INV_SUPPORT_TIMER); } + /* If 100rel is optional and UAC supports it, use it. */ + if ((options & PJSIP_INV_REQUIRE_100REL)==0 && + pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_OPTIONAL) + { + const pj_str_t token = { "100rel", 6}; + pjsip_dialog_cap_status cap_status; + + cap_status = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_SUPPORTED, NULL, + &token); + if (cap_status == PJSIP_DIALOG_CAP_SUPPORTED) + options |= PJSIP_INV_REQUIRE_100REL; + } + /* Create invite session: */ status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv); if (status != PJ_SUCCESS) { @@ -1288,6 +1301,7 @@ pj_status_t acquire_call(const char *title, pj_time_val time_start, timeout; pj_gettimeofday(&time_start); + timeout.sec = 0; timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT; pj_time_val_normalize(&timeout); @@ -1356,20 +1370,24 @@ pj_status_t acquire_call(const char *title, PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id) { pjsua_call *call; - pjsua_conf_port_id port_id; - pjsip_dialog *dlg; - pj_status_t status; + pjsua_conf_port_id port_id = PJSUA_INVALID_ID; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); - status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) - return PJSUA_INVALID_ID; + /* Use PJSUA_LOCK() instead of acquire_call(): + * https://trac.pjsip.org/repos/ticket/1371 + */ + PJSUA_LOCK(); + if (!pjsua_call_is_active(call_id)) + goto on_return; + + call = &pjsua_var.calls[call_id]; port_id = call->media[call->audio_idx].strm.a.conf_slot; - pjsip_dlg_dec_lock(dlg); +on_return: + PJSUA_UNLOCK(); return port_id; } @@ -1383,18 +1401,23 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, pjsua_call_info *info) { pjsua_call *call; - pjsip_dialog *dlg; unsigned mi; - pj_status_t status; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); pj_bzero(info, sizeof(*info)); - status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) { - return status; + /* Use PJSUA_LOCK() instead of acquire_call(): + * https://trac.pjsip.org/repos/ticket/1371 + */ + PJSUA_LOCK(); + + call = &pjsua_var.calls[call_id]; + + if (!call->inv) { + PJSUA_UNLOCK(); + return PJSIP_ESESSIONTERMINATED; } /* id and role */ @@ -1520,7 +1543,7 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, PJ_TIME_VAL_SUB(info->total_duration, call->start_time); } - pjsip_dlg_dec_lock(dlg); + PJSUA_UNLOCK(); return PJ_SUCCESS; } @@ -1961,10 +1984,14 @@ PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id, /* Add additional headers etc */ pjsua_process_msg_data( tdata, msg_data); + /* Record the tx_data to keep track the operation */ + call->hold_msg = (void*) tdata; + /* Send the request */ status = pjsip_inv_send_msg( call->inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); + call->hold_msg = NULL; goto on_return; } @@ -2531,14 +2558,15 @@ PJ_DEF(void) pjsua_call_hangup_all(void) PJ_LOG(4,(THIS_FILE, "Hangup all calls..")); pj_log_push_indent(); - PJSUA_LOCK(); + // This may deadlock, see https://trac.pjsip.org/repos/ticket/1305 + //PJSUA_LOCK(); for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { if (pjsua_var.calls[i].inv) pjsua_call_hangup(i, 0, NULL, NULL); } - PJSUA_UNLOCK(); + //PJSUA_UNLOCK(); pj_log_pop_indent(); } @@ -3971,9 +3999,22 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, &tsx->status_text); } } + } else if (tsx->role == PJSIP_ROLE_UAC && + tsx->last_tx == (pjsip_tx_data*)call->hold_msg && + tsx->state >= PJSIP_TSX_STATE_COMPLETED) + { + /* Monitor the status of call hold request */ + call->hold_msg = NULL; + if (tsx->status_code/100 != 2) { + /* Outgoing call hold failed */ + call->local_hold = PJ_FALSE; + PJ_LOG(3,(THIS_FILE, "Error putting call %d on hold (reason=%d)", + call->index, tsx->status_code)); + } } on_return: + PJSUA_UNLOCK(); pj_log_pop_indent(); } diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 31133b2c..52054226 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -234,6 +234,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) pj_list_init(&cfg->reg_hdr_list); pj_list_init(&cfg->sub_hdr_list); cfg->call_hold_type = PJSUA_CALL_HOLD_TYPE_DEFAULT; + cfg->register_on_acc_add = PJ_TRUE; } PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) @@ -1340,7 +1341,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait) /* * Destroy pjsua. */ -PJ_DEF(pj_status_t) pjsua_destroy(void) +PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) { int i; /* Must be signed */ @@ -1365,12 +1366,14 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) if (pjsua_var.endpt) { unsigned max_wait; - PJ_LOG(4,(THIS_FILE, "Shutting down...")); + PJ_LOG(4,(THIS_FILE, "Shutting down, flags=%d...", flags)); pj_log_push_indent(); /* Terminate all calls. */ - pjsua_call_hangup_all(); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + pjsua_call_hangup_all(); + } /* Set all accounts to offline */ for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { @@ -1381,10 +1384,10 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) } /* Terminate all presence subscriptions. */ - pjsua_pres_shutdown(); + pjsua_pres_shutdown(flags); /* Destroy media (to shutdown media transports etc) */ - pjsua_media_subsys_destroy(); + pjsua_media_subsys_destroy(flags); /* Wait for sometime until all publish client sessions are done * (ticket #364) @@ -1398,6 +1401,11 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec; } + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; + } + /* Second stage, wait for unpublications to complete */ for (i=0; i<(int)(max_wait/50); ++i) { unsigned j; @@ -1427,7 +1435,8 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) if (!pjsua_var.acc[i].valid) continue; - if (pjsua_var.acc[i].regc) { + if (pjsua_var.acc[i].regc && (flags & PJSUA_DESTROY_NO_TX_MSG)==0) + { pjsua_acc_set_registration(i, PJ_FALSE); } } @@ -1452,6 +1461,11 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) max_wait = pjsua_var.acc[i].cfg.unreg_timeout; } + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; + } + /* Second stage, wait for unregistrations to complete */ for (i=0; i<(int)(max_wait/50); ++i) { unsigned j; @@ -1472,8 +1486,9 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) /* Wait for some time to allow unregistration and ICE/TURN * transports shutdown to complete: */ - if (i < 20) + if (i < 20 && (flags & PJSUA_DESTROY_NO_RX_MSG) == 0) { busy_sleep(1000 - i*50); + } PJ_LOG(4,(THIS_FILE, "Destroying...")); @@ -1560,6 +1575,12 @@ PJ_DEF(pjsua_state) pjsua_get_state(void) return pjsua_var.state; } +PJ_DEF(pj_status_t) pjsua_destroy(void) +{ + return pjsua_destroy2(0); +} + + /** * Application is recommended to call this function after all initialization * is done, so that the library can do additional checking set up diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index e7743659..60aebbeb 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -396,7 +396,7 @@ pj_status_t pjsua_media_subsys_start(void) /* * Destroy pjsua media subsystem. */ -pj_status_t pjsua_media_subsys_destroy(void) +pj_status_t pjsua_media_subsys_destroy(unsigned flags) { unsigned i; @@ -441,6 +441,10 @@ pj_status_t pjsua_media_subsys_destroy(void) pjsua_media_channel_deinit(i); } if (call_med->tp && call_med->tp_auto_del) { + /* TODO: check if we're not allowed to send to network in the + * "flags", and if so do not do TURN allocation... + */ + PJ_UNUSED_ARG(flags); pjmedia_transport_close(call_med->tp); } call_med->tp = NULL; @@ -1294,11 +1298,18 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med, if (call_med->tp_st == PJSUA_MED_TP_CREATING) set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); + if (!call_med->tp_orig && + pjsua_var.ua_cfg.cb.on_create_media_transport) + { + call_med->use_custom_med_tp = PJ_TRUE; + } else + call_med->use_custom_med_tp = PJ_FALSE; + #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* This function may be called when SRTP transport already exists * (e.g: in re-invite, update), don't need to destroy/re-create. */ - if (!call_med->tp_orig || call_med->tp == call_med->tp_orig) { + if (!call_med->tp_orig) { pjmedia_srtp_setting srtp_opt; pjmedia_transport *srtp = NULL; @@ -1314,7 +1325,7 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med, /* Always create SRTP adapter */ pjmedia_srtp_setting_default(&srtp_opt); srtp_opt.close_member_tp = PJ_TRUE; - /* If media session has been ever established, let's use remote's + /* If media session has been ever established, let's use remote's * preference in SRTP usage policy, especially when it is stricter. */ if (call_med->rem_srtp_use > acc->cfg.use_srtp) @@ -1519,9 +1530,25 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id, call->async_call.dlg->pool); } - status = pjmedia_transport_media_create( - call_med->tp, tmp_pool, - 0, call->async_call.rem_sdp, mi); + if (call_med->use_custom_med_tp) { + unsigned custom_med_tp_flags = 0; + + /* Use custom media transport returned by the application */ + call_med->tp = + (*pjsua_var.ua_cfg.cb.on_create_media_transport) + (call_id, mi, call_med->tp, + custom_med_tp_flags); + if (!call_med->tp) { + status = + PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); + } + } + + if (call_med->tp) { + status = pjmedia_transport_media_create( + call_med->tp, tmp_pool, + 0, call->async_call.rem_sdp, mi); + } if (status != PJ_SUCCESS) { call->med_ch_info.status = status; call->med_ch_info.med_idx = mi; @@ -2104,6 +2131,7 @@ static void stop_media_session(pjsua_call_id call_id) PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed", call_id, mi)); + call_med->prev_state = call_med->state; call_med->state = PJSUA_CALL_MEDIA_NONE; } @@ -2133,12 +2161,19 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id)); pj_log_push_indent(); + for (mi=0; mi<call->med_cnt; ++mi) { + pjsua_call_media *call_med = &call->media[mi]; + + if (call_med->type == PJMEDIA_TYPE_AUDIO && call_med->strm.a.stream) + pjmedia_stream_send_rtcp_bye(call_med->strm.a.stream); + } + stop_media_session(call_id); for (mi=0; mi<call->med_cnt; ++mi) { pjsua_call_media *call_med = &call->media[mi]; - if (call_med->tp_st > PJSUA_MED_TP_IDLE) { + if (call_med->tp_st > PJSUA_MED_TP_IDLE) { pjmedia_transport_media_stop(call_med->tp); set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); } @@ -2153,6 +2188,7 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) pjmedia_transport_close(call_med->tp); call_med->tp = call_med->tp_orig = NULL; } + call_med->tp_orig = NULL; } check_snd_dev_idle(); @@ -2206,6 +2242,8 @@ static pj_status_t audio_channel_update(pjsua_call_media *call_med, if (status != PJ_SUCCESS) goto on_return; + si->rtcp_sdes_bye_disabled = PJ_TRUE; + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { /* Call media state */ @@ -2296,6 +2334,9 @@ static pj_status_t audio_channel_update(pjsua_call_media *call_med, goto on_return; } + if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE) + pjmedia_stream_send_rtcp_sdes(call_med->strm.a.stream); + /* If DTMF callback is installed by application, install our * callback to the session. */ diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 88ea317f..c78e8b5c 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -1320,13 +1320,17 @@ pj_status_t pjsua_pres_init_acc(int acc_id) /* Unpublish presence publication */ -void pjsua_pres_unpublish(pjsua_acc *acc) +void pjsua_pres_unpublish(pjsua_acc *acc, unsigned flags) { if (acc->publish_sess) { pjsua_acc_config *acc_cfg = &acc->cfg; acc->online_status = PJ_FALSE; - send_publish(acc->index, PJ_FALSE); + + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + send_publish(acc->index, PJ_FALSE); + } + /* By ticket #364, don't destroy the session yet (let the callback destroy it) if (acc->publish_sess) { @@ -1339,7 +1343,7 @@ void pjsua_pres_unpublish(pjsua_acc *acc) } /* Terminate server subscription for the account */ -void pjsua_pres_delete_acc(int acc_id) +void pjsua_pres_delete_acc(int acc_id, unsigned flags) { pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_srv_pres *uapres; @@ -1361,11 +1365,15 @@ void pjsua_pres_delete_acc(int acc_id) pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; pjsip_pres_set_status(uapres->sub, &pres_status); - if (pjsip_pres_notify(uapres->sub, - PJSIP_EVSUB_STATE_TERMINATED, NULL, - &reason, &tdata)==PJ_SUCCESS) - { - pjsip_pres_send_request(uapres->sub, tdata); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + if (pjsip_pres_notify(uapres->sub, + PJSIP_EVSUB_STATE_TERMINATED, NULL, + &reason, &tdata)==PJ_SUCCESS) + { + pjsip_pres_send_request(uapres->sub, tdata); + } + } else { + pjsip_pres_terminate(uapres->sub, PJ_FALSE); } uapres = next; @@ -1376,7 +1384,7 @@ void pjsua_pres_delete_acc(int acc_id) pj_list_init(&acc->pres_srv_list); /* Terminate presence publication, if any */ - pjsua_pres_unpublish(acc); + pjsua_pres_unpublish(acc, flags); } @@ -2251,6 +2259,10 @@ static void pres_timer_cb(pj_timer_heap_t *th, for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { pjsua_acc *acc = &pjsua_var.acc[i]; + /* Acc may not be ready yet, otherwise assertion will happen */ + if (!pjsua_acc_is_valid(i)) + continue; + /* Retry PUBLISH */ if (acc->cfg.publish_enabled && acc->publish_sess==NULL) pjsua_pres_init_publish_acc(acc->index); @@ -2324,7 +2336,7 @@ pj_status_t pjsua_pres_start(void) /* * Shutdown presence. */ -void pjsua_pres_shutdown(void) +void pjsua_pres_shutdown(unsigned flags) { unsigned i; @@ -2339,18 +2351,20 @@ void pjsua_pres_shutdown(void) for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { if (!pjsua_var.acc[i].valid) continue; - pjsua_pres_delete_acc(i); + pjsua_pres_delete_acc(i, flags); } for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { pjsua_var.buddy[i].monitor = 0; } - refresh_client_subscriptions(); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + refresh_client_subscriptions(); - for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { - if (pjsua_var.acc[i].valid) - pjsua_pres_update_acc(i, PJ_FALSE); + for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { + if (pjsua_var.acc[i].valid) + pjsua_pres_update_acc(i, PJ_FALSE); + } } pj_log_pop_indent(); |