summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip-ua/sip_inv.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsip-ua/sip_inv.c')
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c396
1 files changed, 336 insertions, 60 deletions
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 63250e3f..a41f7f6e 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -200,10 +200,7 @@ static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
/* On receipt ACK request, when state is CONNECTING,
* move state to CONFIRMED.
*/
- if (method->id == PJSIP_ACK_METHOD && inv &&
- inv->state != PJSIP_INV_STATE_CONFIRMED)
- {
- pjsip_event event;
+ if (method->id == PJSIP_ACK_METHOD && inv) {
/* Terminate INVITE transaction, if it's still present. */
if (inv->invite_tsx &&
@@ -214,8 +211,12 @@ static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
inv->invite_tsx = NULL;
}
- PJSIP_EVENT_INIT_RX_MSG(event, rdata);
- inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event);
+ if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+ pjsip_event event;
+
+ PJSIP_EVENT_INIT_RX_MSG(event, rdata);
+ inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event);
+ }
}
return PJ_FALSE;
@@ -250,7 +251,9 @@ static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata)
* If it is, we need to send ACK.
*/
if (msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code/100==2 &&
- rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) {
+ rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
+ inv->invite_tsx == NULL)
+ {
inv_send_ack(inv, rdata);
return PJ_TRUE;
@@ -865,13 +868,16 @@ PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv,
{
pjsip_tx_data *tdata;
const pjsip_hdr *hdr;
+ pj_bool_t has_sdp;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
- /* State MUST be NULL. */
- PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL, PJ_EINVAL);
+ /* State MUST be NULL or CONFIRMED. */
+ PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL ||
+ inv->state == PJSIP_INV_STATE_CONFIRMED,
+ PJ_EINVALIDOP);
/* Create the INVITE request. */
status = pjsip_dlg_create_request(inv->dlg, &pjsip_invite_method, -1,
@@ -879,10 +885,37 @@ PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv,
if (status != PJ_SUCCESS)
return status;
+ /* If this is the first INVITE, then copy the headers from inv_hdr.
+ * These are the headers parsed from the request URI when the
+ * dialog was created.
+ */
+ if (inv->state == PJSIP_INV_STATE_NULL) {
+ hdr = inv->dlg->inv_hdr.next;
+
+ while (hdr != &inv->dlg->inv_hdr) {
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, hdr));
+ hdr = hdr->next;
+ }
+ }
+
+ /* See if we have SDP to send. */
+ if (inv->neg) {
+ pjmedia_sdp_neg_state neg_state;
+
+ neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+
+ has_sdp = (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
+ (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
+ pjmedia_sdp_neg_has_local_answer(inv->neg)));
+
+
+ } else {
+ has_sdp = PJ_FALSE;
+ }
+
/* Add SDP, if any. */
- if (inv->neg &&
- pjmedia_sdp_neg_get_state(inv->neg)==PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
- {
+ if (has_sdp) {
const pjmedia_sdp_session *offer;
status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
@@ -940,9 +973,9 @@ static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv )
/*
* Check in incoming message for SDP offer/answer.
*/
-static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
- pjsip_transaction *tsx,
- pjsip_rx_data *rdata)
+static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+ pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
{
struct tsx_inv_data *tsx_inv_data;
static const pj_str_t str_application = { "application", 11 };
@@ -963,21 +996,21 @@ static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
*/
if (tsx_inv_data->sdp_done)
- return;
+ return PJ_SUCCESS;
/* Check if SDP is present in the message. */
msg = rdata->msg_info.msg;
if (msg->body == NULL) {
/* Message doesn't have body. */
- return;
+ return PJ_SUCCESS;
}
if (pj_stricmp(&msg->body->content_type.type, &str_application) ||
pj_stricmp(&msg->body->content_type.subtype, &str_sdp))
{
/* Message body is not "application/sdp" */
- return;
+ return PJMEDIA_SDP_EINSDP;
}
/* Parse the SDP body. */
@@ -989,7 +1022,7 @@ static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error parsing SDP in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
- return;
+ return PJMEDIA_SDP_EINSDP;
}
/* The SDP can be an offer or answer, depending on negotiator's state */
@@ -1015,13 +1048,16 @@ static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error processing SDP offer in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
- return;
+ return PJMEDIA_SDP_EINSDP;
}
/* Inform application about remote offer. */
- if (mod_inv.cb.on_rx_offer)
- (*mod_inv.cb.on_rx_offer)(inv);
+ if (mod_inv.cb.on_rx_offer) {
+
+ (*mod_inv.cb.on_rx_offer)(inv, sdp);
+
+ }
} else if (pjmedia_sdp_neg_get_state(inv->neg) ==
PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
@@ -1041,7 +1077,7 @@ static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error processing SDP answer in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
- return;
+ return PJMEDIA_SDP_EINSDP;
}
/* Negotiate SDP */
@@ -1059,12 +1095,68 @@ static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv->neg))));
}
+ return PJ_SUCCESS;
}
+/*
+ * Process INVITE answer, for both initial and subsequent re-INVITE
+ */
+static pj_status_t process_answer( pjsip_inv_session *inv,
+ int st_code,
+ pjsip_tx_data *tdata )
+{
+ pj_status_t status;
+ pjmedia_sdp_session *sdp = NULL;
+
+ /* Include SDP for 18x and 2xx response.
+ * Also if SDP negotiator is ready, start negotiation.
+ */
+ if (st_code/10 == 18 || st_code/10 == 20) {
+
+ pjmedia_sdp_neg_state neg_state;
+
+ neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) :
+ PJMEDIA_SDP_NEG_STATE_NULL;
+
+ if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
+
+ status = pjmedia_sdp_neg_get_neg_local(inv->neg, &sdp);
+
+ } else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
+ pjmedia_sdp_neg_has_local_answer(inv->neg) )
+ {
+
+ status = inv_negotiate_sdp(inv);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp);
+ }
+
+ }
+
+
+
+ /* Include SDP when it's available.
+ * Subsequent response will include this SDP.
+ */
+ if (sdp) {
+ tdata->msg->body = create_sdp_body(tdata->pool, sdp);
+ }
+
+ /* Remove message body if this is a non-2xx final response */
+ if (st_code >= 300)
+ tdata->msg->body = NULL;
+
+
+ return PJ_SUCCESS;
+}
+
/*
- * Answer initial INVITE.
+ * Answer initial INVITE
+ * Re-INVITE will be answered automatically, and will not use this function.
*/
PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
int st_code,
@@ -1087,7 +1179,7 @@ PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
/* If local_sdp is specified, then we MUST NOT have answered the
* offer before.
*/
- if (local_sdp) {
+ if (local_sdp && (st_code/100==1 || st_code/100==2)) {
if (inv->neg == NULL) {
status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp,
@@ -1108,47 +1200,43 @@ PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
return status;
}
- last_res = inv->invite_tsx->last_tx;
+
+
/* Modify last response. */
+ last_res = inv->invite_tsx->last_tx;
status = pjsip_dlg_modify_response(inv->dlg, last_res, st_code, st_text);
if (status != PJ_SUCCESS)
return status;
- /* Include SDP for 18x and 2xx response.
- * Also if SDP negotiator is ready, start negotiation.
- */
- if (st_code/10 == 18 || st_code/10 == 20) {
- pjmedia_sdp_neg_state neg_state;
+ /* Process SDP in answer */
+ status = process_answer(inv, st_code, last_res);
+ if (status != PJ_SUCCESS)
+ return status;
- neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) :
- PJMEDIA_SDP_NEG_STATE_NULL;
- if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
- neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
- {
- const pjmedia_sdp_session *local;
+ *p_tdata = last_res;
- status = pjmedia_sdp_neg_get_neg_local(inv->neg, &local);
- if (status == PJ_SUCCESS)
- last_res->msg->body = create_sdp_body(last_res->pool, local);
- }
+ return PJ_SUCCESS;
+}
- /* Start negotiation, if ready. */
- if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
- status = inv_negotiate_sdp(inv);
- if (status != PJ_SUCCESS) {
- pjsip_tx_data_dec_ref(last_res);
- return status;
- }
- }
- }
+/*
+ * Set SDP answer.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_set_sdp_answer( pjsip_inv_session *inv,
+ const pjmedia_sdp_session *sdp )
+{
+ pj_status_t status;
- *p_tdata = last_res;
+ PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL);
- return PJ_SUCCESS;
+ pjsip_dlg_inc_lock(inv->dlg);
+ status = pjmedia_sdp_neg_set_local_answer( inv->pool, inv->neg, sdp);
+ pjsip_dlg_dec_lock(inv->dlg);
+
+ return status;
}
@@ -1205,8 +1293,9 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
tdata = inv->invite_tsx->last_tx;
PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);
- status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code,
- st_text);
+ //status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code,
+ // st_text);
+ status = pjsip_inv_answer(inv, st_code, st_text, NULL, &tdata);
}
break;
@@ -1247,13 +1336,82 @@ PJ_DEF(pj_status_t) pjsip_inv_reinvite( pjsip_inv_session *inv,
const pjmedia_sdp_session *new_offer,
pjsip_tx_data **p_tdata )
{
- PJ_UNUSED_ARG(inv);
- PJ_UNUSED_ARG(new_contact);
- PJ_UNUSED_ARG(new_offer);
- PJ_UNUSED_ARG(p_tdata);
+ pj_status_t status;
+ pjsip_contact_hdr *contact_hdr = NULL;
- PJ_TODO(CREATE_REINVITE_REQUEST);
- return PJ_ENOTSUP;
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
+
+ /* Must NOT have a pending INVITE transaction */
+ PJ_ASSERT_RETURN(inv->invite_tsx==NULL, PJ_EINVALIDOP);
+
+
+ pjsip_dlg_inc_lock(inv->dlg);
+
+ if (new_contact) {
+ pj_str_t tmp;
+ const pj_str_t STR_CONTACT = { "Contact", 7 };
+
+ pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
+ contact_hdr = pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT,
+ tmp.ptr, tmp.slen, NULL);
+ if (!contact_hdr) {
+ status = PJSIP_EINVALIDURI;
+ goto on_return;
+ }
+ }
+
+
+ if (new_offer) {
+ if (!inv->neg) {
+ status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, new_offer,
+ &inv->neg);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ } else switch (pjmedia_sdp_neg_get_state(inv->neg)) {
+
+ case PJMEDIA_SDP_NEG_STATE_NULL:
+ pj_assert(!"Unexpected SDP neg state NULL");
+ status = PJ_EBUG;
+ goto on_return;
+
+ case PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER:
+ PJ_LOG(4,(inv->obj_name,
+ "pjsip_inv_reinvite: already have an offer, new "
+ "offer is ignored"));
+ break;
+
+ case PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER:
+ status = pjmedia_sdp_neg_set_local_answer(inv->pool, inv->neg,
+ new_offer);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ break;
+
+ case PJMEDIA_SDP_NEG_STATE_WAIT_NEGO:
+ PJ_LOG(4,(inv->obj_name,
+ "pjsip_inv_reinvite: SDP in WAIT_NEGO state, new "
+ "offer is ignored"));
+ break;
+
+ case PJMEDIA_SDP_NEG_STATE_DONE:
+ status = pjmedia_sdp_neg_modify_local_offer(inv->pool,inv->neg,
+ new_offer);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ break;
+ }
+ }
+
+ if (contact_hdr)
+ inv->dlg->local.contact = contact_hdr;
+
+ status = pjsip_inv_invite(inv, p_tdata);
+
+on_return:
+ pjsip_dlg_dec_lock(inv->dlg);
+ return status;
}
/*
@@ -1908,6 +2066,124 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
inv_respond_incoming_bye( inv, tsx, e->body.tsx_state.src.rdata, e );
}
+ else if (tsx->method.id == PJSIP_INVITE_METHOD &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+
+ /*
+ * Handle incoming re-INVITE
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+
+ pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Check if we have INVITE pending. */
+ if (inv->invite_tsx && inv->invite_tsx!=tsx) {
+
+ /* Can not receive re-INVITE while another one is pending. */
+ status = pjsip_dlg_create_response( inv->dlg, rdata, 500, NULL,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ status = pjsip_dlg_send_response( inv->dlg, tsx, tdata);
+
+
+ return;
+ }
+
+ /* Save the invite transaction. */
+ inv->invite_tsx = tsx;
+
+ /* Process SDP in incoming message. */
+ status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata);
+
+ if (status != PJ_SUCCESS) {
+
+ /* Not Acceptable */
+ const pjsip_hdr *accept;
+
+ status = pjsip_dlg_create_response(inv->dlg, rdata,
+ 488, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+
+ accept = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_ACCEPT,
+ NULL);
+ if (accept) {
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_clone(tdata->pool, accept));
+ }
+
+ status = pjsip_dlg_send_response(dlg, tsx, tdata);
+
+ return;
+ }
+
+ /* Create 2xx ANSWER */
+ status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ /* Process SDP in the answer */
+ status = process_answer(inv, 200, tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ status = pjsip_inv_send_msg(inv, tdata, NULL);
+
+ }
+
+ }
+ else if (tsx->method.id == PJSIP_INVITE_METHOD &&
+ tsx->role == PJSIP_ROLE_UAC)
+ {
+ /*
+ * Handle outgoing re-INVITE
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
+ tsx->status_code/100 == 2)
+ {
+
+ /* Re-INVITE was accepted. */
+
+ /* Process SDP */
+ inv_check_sdp_in_incoming_msg(inv, tsx,
+ e->body.tsx_state.src.rdata);
+
+ /* Send ACK */
+ inv_send_ack(inv, e->body.tsx_state.src.rdata);
+
+ } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
+ (tsx->status_code==401 || tsx->status_code==407))
+ {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Handle authentication challenge. */
+ status = pjsip_auth_clt_reinit_req( &dlg->auth_sess,
+ e->body.tsx_state.src.rdata,
+ tsx->last_tx,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ /* Send re-INVITE */
+ status = pjsip_inv_send_msg( inv, tdata, NULL);
+
+ } else if (tsx->status_code==PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ||
+ tsx->status_code==PJSIP_SC_REQUEST_TIMEOUT ||
+ tsx->status_code >= 700)
+ {
+ /*
+ * Handle responses that terminates dialog.
+ */
+ inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+ }
+ }
}
/*