+++ /dev/null
-From 0ed41eb5fd0e4192e1b7dc374f819d17aef3e805 Mon Sep 17 00:00:00 2001
-From: George Joseph <gtjoseph@users.noreply.github.com>
-Date: Tue, 21 Dec 2021 19:32:22 -0700
-Subject: [PATCH] sip_inv: Additional multipart support (#2919) (#2920)
-
----
- pjsip/include/pjsip-ua/sip_inv.h | 108 ++++++++++-
- pjsip/src/pjsip-ua/sip_inv.c | 240 ++++++++++++++++++++-----
- pjsip/src/test/inv_offer_answer_test.c | 103 ++++++++++-
- 3 files changed, 394 insertions(+), 57 deletions(-)
-
---- a/pjsip/include/pjsip-ua/sip_inv.h
-+++ b/pjsip/include/pjsip-ua/sip_inv.h
-@@ -451,11 +451,11 @@ struct pjsip_inv_session
-
-
- /**
-- * This structure represents SDP information in a pjsip_rx_data. Application
-- * retrieve this information by calling #pjsip_rdata_get_sdp_info(). This
-+ * This structure represents SDP information in a pjsip_(rx|tx)_data. Application
-+ * retrieve this information by calling #pjsip_get_sdp_info(). This
- * mechanism supports multipart message body.
- */
--typedef struct pjsip_rdata_sdp_info
-+typedef struct pjsip_sdp_info
- {
- /**
- * Pointer and length of the text body in the incoming message. If
-@@ -475,7 +475,15 @@ typedef struct pjsip_rdata_sdp_info
- */
- pjmedia_sdp_session *sdp;
-
--} pjsip_rdata_sdp_info;
-+} pjsip_sdp_info;
-+
-+/**
-+ * For backwards compatibility and completeness,
-+ * pjsip_rdata_sdp_info and pjsip_tdata_sdp_info
-+ * are typedef'd to pjsip_sdp_info.
-+ */
-+typedef pjsip_sdp_info pjsip_rdata_sdp_info;
-+typedef pjsip_sdp_info pjsip_tdata_sdp_info;
-
-
- /**
-@@ -1046,6 +1054,44 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo
- pjsip_msg_body **p_body);
-
- /**
-+ * This is a utility function to create a multipart body with the
-+ * SIP body as the first part.
-+ *
-+ * @param pool Pool to allocate memory.
-+ * @param sdp SDP session to be put in the SIP message body.
-+ * @param p_body Pointer to receive SIP message body containing
-+ * the SDP session.
-+ *
-+ * @return PJ_SUCCESS on success.
-+ */
-+PJ_DECL(pj_status_t) pjsip_create_multipart_sdp_body( pj_pool_t *pool,
-+ pjmedia_sdp_session *sdp,
-+ pjsip_msg_body **p_body);
-+
-+/**
-+ * Retrieve SDP information from a message body. Application should
-+ * prefer to use this function rather than parsing the SDP manually since
-+ * this function supports multipart message body.
-+ *
-+ * This function will only parse the SDP once, the first time it is called
-+ * on the same message. Subsequent call on the same message will just pick
-+ * up the already parsed SDP from the message.
-+ *
-+ * @param pool Pool to allocate memory.
-+ * @param body The message body.
-+ * @param msg_media_type From the rdata or tdata Content-Type header, if available.
-+ * If NULL, the content_type from the body will be used.
-+ * @param search_media_type The media type to search for.
-+ * If NULL, "application/sdp" will be used.
-+ *
-+ * @return The SDP info.
-+ */
-+PJ_DECL(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool,
-+ pjsip_msg_body *body,
-+ pjsip_media_type *msg_media_type,
-+ const pjsip_media_type *search_media_type);
-+
-+/**
- * Retrieve SDP information from an incoming message. Application should
- * prefer to use this function rather than parsing the SDP manually since
- * this function supports multipart message body.
-@@ -1061,6 +1107,60 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo
- PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata);
-
-
-+/**
-+ * Retrieve SDP information from an incoming message. Application should
-+ * prefer to use this function rather than parsing the SDP manually since
-+ * this function supports multipart message body.
-+ *
-+ * This function will only parse the SDP once, the first time it is called
-+ * on the same message. Subsequent call on the same message will just pick
-+ * up the already parsed SDP from the message.
-+ *
-+ * @param rdata The incoming message.
-+ * @param search_media_type The SDP media type to search for.
-+ * If NULL, "application/sdp" will be used.
-+ *
-+ * @return The SDP info.
-+ */
-+PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2(
-+ pjsip_rx_data *rdata,
-+ const pjsip_media_type *search_media_type);
-+
-+/**
-+ * Retrieve SDP information from an outgoing message. Application should
-+ * prefer to use this function rather than parsing the SDP manually since
-+ * this function supports multipart message body.
-+ *
-+ * This function will only parse the SDP once, the first time it is called
-+ * on the same message. Subsequent call on the same message will just pick
-+ * up the already parsed SDP from the message.
-+ *
-+ * @param tdata The outgoing message.
-+ *
-+ * @return The SDP info.
-+ */
-+PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata);
-+
-+/**
-+ * Retrieve SDP information from an outgoing message. Application should
-+ * prefer to use this function rather than parsing the SDP manually since
-+ * this function supports multipart message body.
-+ *
-+ * This function will only parse the SDP once, the first time it is called
-+ * on the same message. Subsequent call on the same message will just pick
-+ * up the already parsed SDP from the message.
-+ *
-+ * @param tdata The outgoing message.
-+ * @param search_media_type The SDP media type to search for.
-+ * If NULL, "application/sdp" will be used.
-+ *
-+ * @return The SDP info.
-+ */
-+PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2(
-+ pjsip_tx_data *tdata,
-+ const pjsip_media_type *search_media_type);
-+
-+
- PJ_END_DECL
-
- /**
---- a/pjsip/src/pjsip-ua/sip_inv.c
-+++ b/pjsip/src/pjsip-ua/sip_inv.c
-@@ -118,6 +118,8 @@ static pj_status_t handle_timer_response
- static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv,
- pjsip_event *e);
-
-+static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len);
-+
- static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) =
- {
- &inv_on_state_null,
-@@ -956,66 +958,170 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uac
- return PJ_SUCCESS;
- }
-
--PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
--{
-- pjsip_rdata_sdp_info *sdp_info;
-- pjsip_msg_body *body = rdata->msg_info.msg->body;
-- pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype;
-- pjsip_media_type app_sdp;
-+PJ_DEF(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool,
-+ pjsip_msg_body *body,
-+ pjsip_media_type *msg_media_type,
-+ const pjsip_media_type *search_media_type)
-+{
-+ pjsip_sdp_info *sdp_info;
-+ pjsip_media_type search_type;
-+ pjsip_media_type multipart_mixed;
-+ pjsip_media_type multipart_alternative;
-+ pjsip_media_type *msg_type;
-+ pj_status_t status;
-
-- sdp_info = (pjsip_rdata_sdp_info*)
-- rdata->endpt_info.mod_data[mod_inv.mod.id];
-- if (sdp_info)
-- return sdp_info;
-+ sdp_info = PJ_POOL_ZALLOC_T(pool,
-+ pjsip_sdp_info);
-
-- sdp_info = PJ_POOL_ZALLOC_T(rdata->tp_info.pool,
-- pjsip_rdata_sdp_info);
- PJ_ASSERT_RETURN(mod_inv.mod.id >= 0, sdp_info);
-- rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info;
-
-- pjsip_media_type_init2(&app_sdp, "application", "sdp");
-+ if (!body) {
-+ return sdp_info;
-+ }
-
-- if (body && ctype_hdr &&
-- pj_stricmp(&ctype_hdr->media.type, &app_sdp.type)==0 &&
-- pj_stricmp(&ctype_hdr->media.subtype, &app_sdp.subtype)==0)
-+ if (msg_media_type) {
-+ msg_type = msg_media_type;
-+ } else {
-+ if (body->content_type.type.slen == 0) {
-+ return sdp_info;
-+ }
-+ msg_type = &body->content_type;
-+ }
-+
-+ if (!search_media_type) {
-+ pjsip_media_type_init2(&search_type, "application", "sdp");
-+ } else {
-+ pj_memcpy(&search_type, search_media_type, sizeof(search_type));
-+ }
-+
-+ pjsip_media_type_init2(&multipart_mixed, "multipart", "mixed");
-+ pjsip_media_type_init2(&multipart_alternative, "multipart", "alternative");
-+
-+ if (pjsip_media_type_cmp(msg_type, &search_type, PJ_FALSE) == 0)
- {
-- sdp_info->body.ptr = (char*)body->data;
-- sdp_info->body.slen = body->len;
-- } else if (body && ctype_hdr &&
-- pj_stricmp2(&ctype_hdr->media.type, "multipart")==0 &&
-- (pj_stricmp2(&ctype_hdr->media.subtype, "mixed")==0 ||
-- pj_stricmp2(&ctype_hdr->media.subtype, "alternative")==0))
-+ /*
-+ * If the print_body function is print_sdp, we know that
-+ * body->data is a pjmedia_sdp_session object and came from
-+ * a tx_data. If not, it's the text representation of the
-+ * sdp from an rx_data.
-+ */
-+ if (body->print_body == print_sdp) {
-+ sdp_info->sdp = body->data;
-+ } else {
-+ sdp_info->body.ptr = (char*)body->data;
-+ sdp_info->body.slen = body->len;
-+ }
-+ } else if (pjsip_media_type_cmp(&multipart_mixed, msg_type, PJ_FALSE) == 0 ||
-+ pjsip_media_type_cmp(&multipart_alternative, msg_type, PJ_FALSE) == 0)
- {
-- pjsip_multipart_part *part;
-+ pjsip_multipart_part *part;
-+ part = pjsip_multipart_find_part(body, &search_type, NULL);
-+ if (part) {
-+ if (part->body->print_body == print_sdp) {
-+ sdp_info->sdp = part->body->data;
-+ } else {
-+ sdp_info->body.ptr = (char*)part->body->data;
-+ sdp_info->body.slen = part->body->len;
-+ }
-+ }
-+ }
-
-- part = pjsip_multipart_find_part(body, &app_sdp, NULL);
-- if (part) {
-- sdp_info->body.ptr = (char*)part->body->data;
-- sdp_info->body.slen = part->body->len;
-- }
-+ /*
-+ * If the body was already a pjmedia_sdp_session, we can just
-+ * return it. If not and there wasn't a text representation
-+ * of the sdp either, we can also just return.
-+ */
-+ if (sdp_info->sdp || !sdp_info->body.ptr) {
-+ return sdp_info;
- }
-
-- if (sdp_info->body.ptr) {
-- pj_status_t status;
-- status = pjmedia_sdp_parse(rdata->tp_info.pool,
-- sdp_info->body.ptr,
-- sdp_info->body.slen,
-- &sdp_info->sdp);
-- if (status == PJ_SUCCESS)
-- status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE);
-+ /*
-+ * If the body was the text representation of teh SDP, we need
-+ * to parse it to create a pjmedia_sdp_session object.
-+ */
-+ status = pjmedia_sdp_parse(pool,
-+ sdp_info->body.ptr,
-+ sdp_info->body.slen,
-+ &sdp_info->sdp);
-+ if (status == PJ_SUCCESS)
-+ status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE);
-
-- if (status != PJ_SUCCESS) {
-- sdp_info->sdp = NULL;
-- PJ_PERROR(1,(THIS_FILE, status,
-- "Error parsing/validating SDP body"));
-- }
-+ if (status != PJ_SUCCESS) {
-+ sdp_info->sdp = NULL;
-+ PJ_PERROR(1, (THIS_FILE, status,
-+ "Error parsing/validating SDP body"));
-+ }
-+
-+ sdp_info->sdp_err = status;
-+
-+ return sdp_info;
-+}
-+
-+PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2(
-+ pjsip_rx_data *rdata,
-+ const pjsip_media_type *search_media_type)
-+{
-+ pjsip_media_type *msg_media_type = NULL;
-+ pjsip_rdata_sdp_info *sdp_info;
-
-- sdp_info->sdp_err = status;
-+ if (rdata->endpt_info.mod_data[mod_inv.mod.id]) {
-+ return (pjsip_rdata_sdp_info *)rdata->endpt_info.mod_data[mod_inv.mod.id];
-+ }
-+
-+ /*
-+ * rdata should have a Content-Type header at this point but we'll
-+ * make sure.
-+ */
-+ if (rdata->msg_info.ctype) {
-+ msg_media_type = &rdata->msg_info.ctype->media;
- }
-+ sdp_info = pjsip_get_sdp_info(rdata->tp_info.pool,
-+ rdata->msg_info.msg->body,
-+ msg_media_type,
-+ search_media_type);
-+ rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info;
-
- return sdp_info;
- }
-
-+PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
-+{
-+ return pjsip_rdata_get_sdp_info2(rdata, NULL);
-+}
-+
-+PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2(
-+ pjsip_tx_data *tdata,
-+ const pjsip_media_type *search_media_type)
-+{
-+ pjsip_ctype_hdr *ctype_hdr = NULL;
-+ pjsip_media_type *msg_media_type = NULL;
-+ pjsip_tdata_sdp_info *sdp_info;
-+
-+ if (tdata->mod_data[mod_inv.mod.id]) {
-+ return (pjsip_tdata_sdp_info *)tdata->mod_data[mod_inv.mod.id];
-+ }
-+ /*
-+ * tdata won't usually have a Content-Type header at this point
-+ * but we'll check just the same,
-+ */
-+ ctype_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTENT_TYPE, NULL);
-+ if (ctype_hdr) {
-+ msg_media_type = &ctype_hdr->media;
-+ }
-+
-+ sdp_info = pjsip_get_sdp_info(tdata->pool,
-+ tdata->msg->body,
-+ msg_media_type,
-+ search_media_type);
-+ tdata->mod_data[mod_inv.mod.id] = sdp_info;
-+
-+ return sdp_info;
-+}
-+
-+PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata)
-+{
-+ return pjsip_tdata_get_sdp_info2(tdata, NULL);
-+}
-
- /*
- * Verify incoming INVITE request.
-@@ -1740,13 +1846,55 @@ PJ_DEF(pj_status_t) pjsip_create_sdp_bod
- return PJ_SUCCESS;
- }
-
-+static pjsip_multipart_part* create_sdp_part(pj_pool_t *pool, pjmedia_sdp_session *sdp)
-+{
-+ pjsip_multipart_part *sdp_part;
-+ pjsip_media_type media_type;
-+
-+ pjsip_media_type_init2(&media_type, "application", "sdp");
-+
-+ sdp_part = pjsip_multipart_create_part(pool);
-+ PJ_ASSERT_RETURN(sdp_part != NULL, NULL);
-+
-+ sdp_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
-+ PJ_ASSERT_RETURN(sdp_part->body != NULL, NULL);
-+
-+ pjsip_media_type_cp(pool, &sdp_part->body->content_type, &media_type);
-+
-+ sdp_part->body->data = sdp;
-+ sdp_part->body->clone_data = clone_sdp;
-+ sdp_part->body->print_body = print_sdp;
-+
-+ return sdp_part;
-+}
-+
-+PJ_DEF(pj_status_t) pjsip_create_multipart_sdp_body(pj_pool_t *pool,
-+ pjmedia_sdp_session *sdp,
-+ pjsip_msg_body **p_body)
-+{
-+ pjsip_media_type media_type;
-+ pjsip_msg_body *multipart;
-+ pjsip_multipart_part *sdp_part;
-+
-+ pjsip_media_type_init2(&media_type, "multipart", "mixed");
-+ multipart = pjsip_multipart_create(pool, &media_type, NULL);
-+ PJ_ASSERT_RETURN(multipart != NULL, PJ_ENOMEM);
-+
-+ sdp_part = create_sdp_part(pool, sdp);
-+ PJ_ASSERT_RETURN(sdp_part != NULL, PJ_ENOMEM);
-+ pjsip_multipart_add_part(pool, multipart, sdp_part);
-+ *p_body = multipart;
-+
-+ return PJ_SUCCESS;
-+}
-+
- static pjsip_msg_body *create_sdp_body(pj_pool_t *pool,
- const pjmedia_sdp_session *c_sdp)
- {
- pjsip_msg_body *body;
- pj_status_t status;
-
-- status = pjsip_create_sdp_body(pool,
-+ status = pjsip_create_sdp_body(pool,
- pjmedia_sdp_session_clone(pool, c_sdp),
- &body);
-
-@@ -2069,6 +2217,7 @@ static pj_status_t inv_check_sdp_in_inco
- )
- )
- {
-+ pjsip_sdp_info *tdata_sdp_info;
- const pjmedia_sdp_session *reoffer_sdp = NULL;
-
- PJ_LOG(4,(inv->obj_name, "Received %s response "
-@@ -2077,14 +2226,15 @@ static pj_status_t inv_check_sdp_in_inco
- (st_code/10==18? "early" : "final" )));
-
- /* Retrieve original SDP offer from INVITE request */
-- reoffer_sdp = (const pjmedia_sdp_session*)
-- tsx->last_tx->msg->body->data;
-+ tdata_sdp_info = pjsip_tdata_get_sdp_info(tsx->last_tx);
-+ reoffer_sdp = tdata_sdp_info->sdp;
-
- /* Feed the original offer to negotiator */
- status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov,
- inv->neg,
- inv->sdp_neg_flags,
- reoffer_sdp);
-+
- if (status != PJ_SUCCESS) {
- PJ_LOG(1,(inv->obj_name, "Error updating local offer for "
- "forked 2xx/18x response (err=%d)", status));
---- a/pjsip/src/test/inv_offer_answer_test.c
-+++ b/pjsip/src/test/inv_offer_answer_test.c
-@@ -137,6 +137,7 @@ typedef struct inv_test_param_t
- pj_bool_t need_established;
- unsigned count;
- oa_t oa[4];
-+ pj_bool_t multipart_body;
- } inv_test_param_t;
-
- typedef struct inv_test_t
-@@ -257,6 +258,17 @@ static void on_media_update(pjsip_inv_se
- }
- }
-
-+ /* Special handling for standard offer/answer */
-+ if (inv_test.param.count == 1 &&
-+ inv_test.param.oa[0] == OFFERER_UAC &&
-+ inv_test.param.need_established)
-+ {
-+ jobs[job_cnt].type = ESTABLISH_CALL;
-+ jobs[job_cnt].who = PJSIP_ROLE_UAS;
-+ job_cnt++;
-+ TRACE_((THIS_FILE, " C+++"));
-+ }
-+
- pj_assert(job_cnt <= PJ_ARRAY_SIZE(jobs));
- }
- }
-@@ -333,6 +345,15 @@ static pj_bool_t on_rx_request(pjsip_rx_
- NULL, &tdata);
- pj_assert(status == PJ_SUCCESS);
-
-+ /* Use multipart body, if configured */
-+ if (sdp && inv_test.param.multipart_body) {
-+ status = pjsip_create_multipart_sdp_body(
-+ tdata->pool,
-+ pjmedia_sdp_session_clone(tdata->pool, sdp),
-+ &tdata->msg->body);
-+ }
-+ pj_assert(status == PJ_SUCCESS);
-+
- status = pjsip_inv_send_msg(inv_test.uas, tdata);
- pj_assert(status == PJ_SUCCESS);
-
-@@ -426,6 +447,7 @@ static int perform_test(inv_test_param_t
- sdp = NULL;
-
- status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac);
-+ //inv_test.uac->create_multipart = param->multipart_body;
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20);
-
- TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without")));
-@@ -436,8 +458,17 @@ static int perform_test(inv_test_param_t
- status = pjsip_inv_invite(inv_test.uac, &tdata);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
-
-+ /* Use multipart body, if configured */
-+ if (sdp && param->multipart_body) {
-+ status = pjsip_create_multipart_sdp_body(
-+ tdata->pool,
-+ pjmedia_sdp_session_clone(tdata->pool, sdp),
-+ &tdata->msg->body);
-+ }
-+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -40);
-+
- status = pjsip_inv_send_msg(inv_test.uac, tdata);
-- PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
-+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -50);
-
- /*
- * Wait until test completes
-@@ -525,13 +556,14 @@ static inv_test_param_t test_params[] =
- 200/INVITE (answer) <--
- ACK -->
- */
--#if 0
-+#if 1
- {
- "Standard INVITE with offer",
- 0,
- PJ_TRUE,
- 1,
-- { OFFERER_UAC }
-+ { OFFERER_UAC },
-+ PJ_FALSE
- },
-
- {
-@@ -539,7 +571,25 @@ static inv_test_param_t test_params[] =
- PJSIP_INV_REQUIRE_100REL,
- PJ_TRUE,
- 1,
-- { OFFERER_UAC }
-+ { OFFERER_UAC },
-+ PJ_FALSE
-+ },
-+ {
-+ "Standard INVITE with offer, with Multipart",
-+ 0,
-+ PJ_TRUE,
-+ 1,
-+ { OFFERER_UAC },
-+ PJ_TRUE
-+ },
-+
-+ {
-+ "Standard INVITE with offer, with 100rel, with Multipart",
-+ PJSIP_INV_REQUIRE_100REL,
-+ PJ_TRUE,
-+ 1,
-+ { OFFERER_UAC },
-+ PJ_TRUE
- },
- #endif
-
-@@ -555,7 +605,8 @@ static inv_test_param_t test_params[] =
- 0,
- PJ_TRUE,
- 1,
-- { OFFERER_UAS }
-+ { OFFERER_UAS },
-+ PJ_FALSE
- },
-
- {
-@@ -563,7 +614,25 @@ static inv_test_param_t test_params[] =
- PJSIP_INV_REQUIRE_100REL,
- PJ_TRUE,
- 1,
-- { OFFERER_UAS }
-+ { OFFERER_UAS },
-+ PJ_FALSE
-+ },
-+ {
-+ "INVITE with no offer, with Multipart",
-+ 0,
-+ PJ_TRUE,
-+ 1,
-+ { OFFERER_UAS },
-+ PJ_TRUE
-+ },
-+
-+ {
-+ "INVITE with no offer, with 100rel, with Multipart",
-+ PJSIP_INV_REQUIRE_100REL,
-+ PJ_TRUE,
-+ 1,
-+ { OFFERER_UAS },
-+ PJ_TRUE
- },
- #endif
-
-@@ -584,14 +653,24 @@ static inv_test_param_t test_params[] =
- 0,
- PJ_TRUE,
- 2,
-- { OFFERER_UAC, OFFERER_UAC }
-+ { OFFERER_UAC, OFFERER_UAC },
-+ PJ_FALSE
-+ },
-+ {
-+ "INVITE and UPDATE by UAC, with Multipart",
-+ 0,
-+ PJ_TRUE,
-+ 2,
-+ { OFFERER_UAC, OFFERER_UAC },
-+ PJ_TRUE
- },
- {
- "INVITE and UPDATE by UAC, with 100rel",
- PJSIP_INV_REQUIRE_100REL,
- PJ_TRUE,
- 2,
-- { OFFERER_UAC, OFFERER_UAC }
-+ { OFFERER_UAC, OFFERER_UAC },
-+ PJ_FALSE
- },
- #endif
-
-@@ -617,6 +696,14 @@ static inv_test_param_t test_params[] =
- 4,
- { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS }
- },
-+ {
-+ "INVITE and many UPDATE by UAC and UAS, with Multipart",
-+ 0,
-+ PJ_TRUE,
-+ 4,
-+ { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS },
-+ PJ_TRUE
-+ },
-
- };
-