8db8aab9d01ef3f13a36f9d0bced6096afc66bf0
[feed/telephony.git] / libs / pjproject / patches / 0130-sip_inv-Additional-multipart-support-2919-2920.patch
1 From 0ed41eb5fd0e4192e1b7dc374f819d17aef3e805 Mon Sep 17 00:00:00 2001
2 From: George Joseph <gtjoseph@users.noreply.github.com>
3 Date: Tue, 21 Dec 2021 19:32:22 -0700
4 Subject: [PATCH] sip_inv: Additional multipart support (#2919) (#2920)
5
6 ---
7 pjsip/include/pjsip-ua/sip_inv.h | 108 ++++++++++-
8 pjsip/src/pjsip-ua/sip_inv.c | 240 ++++++++++++++++++++-----
9 pjsip/src/test/inv_offer_answer_test.c | 103 ++++++++++-
10 3 files changed, 394 insertions(+), 57 deletions(-)
11
12 --- a/pjsip/include/pjsip-ua/sip_inv.h
13 +++ b/pjsip/include/pjsip-ua/sip_inv.h
14 @@ -451,11 +451,11 @@ struct pjsip_inv_session
15
16
17 /**
18 - * This structure represents SDP information in a pjsip_rx_data. Application
19 - * retrieve this information by calling #pjsip_rdata_get_sdp_info(). This
20 + * This structure represents SDP information in a pjsip_(rx|tx)_data. Application
21 + * retrieve this information by calling #pjsip_get_sdp_info(). This
22 * mechanism supports multipart message body.
23 */
24 -typedef struct pjsip_rdata_sdp_info
25 +typedef struct pjsip_sdp_info
26 {
27 /**
28 * Pointer and length of the text body in the incoming message. If
29 @@ -475,7 +475,15 @@ typedef struct pjsip_rdata_sdp_info
30 */
31 pjmedia_sdp_session *sdp;
32
33 -} pjsip_rdata_sdp_info;
34 +} pjsip_sdp_info;
35 +
36 +/**
37 + * For backwards compatibility and completeness,
38 + * pjsip_rdata_sdp_info and pjsip_tdata_sdp_info
39 + * are typedef'd to pjsip_sdp_info.
40 + */
41 +typedef pjsip_sdp_info pjsip_rdata_sdp_info;
42 +typedef pjsip_sdp_info pjsip_tdata_sdp_info;
43
44
45 /**
46 @@ -1046,6 +1054,44 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo
47 pjsip_msg_body **p_body);
48
49 /**
50 + * This is a utility function to create a multipart body with the
51 + * SIP body as the first part.
52 + *
53 + * @param pool Pool to allocate memory.
54 + * @param sdp SDP session to be put in the SIP message body.
55 + * @param p_body Pointer to receive SIP message body containing
56 + * the SDP session.
57 + *
58 + * @return PJ_SUCCESS on success.
59 + */
60 +PJ_DECL(pj_status_t) pjsip_create_multipart_sdp_body( pj_pool_t *pool,
61 + pjmedia_sdp_session *sdp,
62 + pjsip_msg_body **p_body);
63 +
64 +/**
65 + * Retrieve SDP information from a message body. Application should
66 + * prefer to use this function rather than parsing the SDP manually since
67 + * this function supports multipart message body.
68 + *
69 + * This function will only parse the SDP once, the first time it is called
70 + * on the same message. Subsequent call on the same message will just pick
71 + * up the already parsed SDP from the message.
72 + *
73 + * @param pool Pool to allocate memory.
74 + * @param body The message body.
75 + * @param msg_media_type From the rdata or tdata Content-Type header, if available.
76 + * If NULL, the content_type from the body will be used.
77 + * @param search_media_type The media type to search for.
78 + * If NULL, "application/sdp" will be used.
79 + *
80 + * @return The SDP info.
81 + */
82 +PJ_DECL(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool,
83 + pjsip_msg_body *body,
84 + pjsip_media_type *msg_media_type,
85 + const pjsip_media_type *search_media_type);
86 +
87 +/**
88 * Retrieve SDP information from an incoming message. Application should
89 * prefer to use this function rather than parsing the SDP manually since
90 * this function supports multipart message body.
91 @@ -1061,6 +1107,60 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo
92 PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata);
93
94
95 +/**
96 + * Retrieve SDP information from an incoming message. Application should
97 + * prefer to use this function rather than parsing the SDP manually since
98 + * this function supports multipart message body.
99 + *
100 + * This function will only parse the SDP once, the first time it is called
101 + * on the same message. Subsequent call on the same message will just pick
102 + * up the already parsed SDP from the message.
103 + *
104 + * @param rdata The incoming message.
105 + * @param search_media_type The SDP media type to search for.
106 + * If NULL, "application/sdp" will be used.
107 + *
108 + * @return The SDP info.
109 + */
110 +PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2(
111 + pjsip_rx_data *rdata,
112 + const pjsip_media_type *search_media_type);
113 +
114 +/**
115 + * Retrieve SDP information from an outgoing message. Application should
116 + * prefer to use this function rather than parsing the SDP manually since
117 + * this function supports multipart message body.
118 + *
119 + * This function will only parse the SDP once, the first time it is called
120 + * on the same message. Subsequent call on the same message will just pick
121 + * up the already parsed SDP from the message.
122 + *
123 + * @param tdata The outgoing message.
124 + *
125 + * @return The SDP info.
126 + */
127 +PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata);
128 +
129 +/**
130 + * Retrieve SDP information from an outgoing message. Application should
131 + * prefer to use this function rather than parsing the SDP manually since
132 + * this function supports multipart message body.
133 + *
134 + * This function will only parse the SDP once, the first time it is called
135 + * on the same message. Subsequent call on the same message will just pick
136 + * up the already parsed SDP from the message.
137 + *
138 + * @param tdata The outgoing message.
139 + * @param search_media_type The SDP media type to search for.
140 + * If NULL, "application/sdp" will be used.
141 + *
142 + * @return The SDP info.
143 + */
144 +PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2(
145 + pjsip_tx_data *tdata,
146 + const pjsip_media_type *search_media_type);
147 +
148 +
149 PJ_END_DECL
150
151 /**
152 --- a/pjsip/src/pjsip-ua/sip_inv.c
153 +++ b/pjsip/src/pjsip-ua/sip_inv.c
154 @@ -118,6 +118,8 @@ static pj_status_t handle_timer_response
155 static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv,
156 pjsip_event *e);
157
158 +static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len);
159 +
160 static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) =
161 {
162 &inv_on_state_null,
163 @@ -956,66 +958,170 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uac
164 return PJ_SUCCESS;
165 }
166
167 -PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
168 -{
169 - pjsip_rdata_sdp_info *sdp_info;
170 - pjsip_msg_body *body = rdata->msg_info.msg->body;
171 - pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype;
172 - pjsip_media_type app_sdp;
173 +PJ_DEF(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool,
174 + pjsip_msg_body *body,
175 + pjsip_media_type *msg_media_type,
176 + const pjsip_media_type *search_media_type)
177 +{
178 + pjsip_sdp_info *sdp_info;
179 + pjsip_media_type search_type;
180 + pjsip_media_type multipart_mixed;
181 + pjsip_media_type multipart_alternative;
182 + pjsip_media_type *msg_type;
183 + pj_status_t status;
184
185 - sdp_info = (pjsip_rdata_sdp_info*)
186 - rdata->endpt_info.mod_data[mod_inv.mod.id];
187 - if (sdp_info)
188 - return sdp_info;
189 + sdp_info = PJ_POOL_ZALLOC_T(pool,
190 + pjsip_sdp_info);
191
192 - sdp_info = PJ_POOL_ZALLOC_T(rdata->tp_info.pool,
193 - pjsip_rdata_sdp_info);
194 PJ_ASSERT_RETURN(mod_inv.mod.id >= 0, sdp_info);
195 - rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info;
196
197 - pjsip_media_type_init2(&app_sdp, "application", "sdp");
198 + if (!body) {
199 + return sdp_info;
200 + }
201
202 - if (body && ctype_hdr &&
203 - pj_stricmp(&ctype_hdr->media.type, &app_sdp.type)==0 &&
204 - pj_stricmp(&ctype_hdr->media.subtype, &app_sdp.subtype)==0)
205 + if (msg_media_type) {
206 + msg_type = msg_media_type;
207 + } else {
208 + if (body->content_type.type.slen == 0) {
209 + return sdp_info;
210 + }
211 + msg_type = &body->content_type;
212 + }
213 +
214 + if (!search_media_type) {
215 + pjsip_media_type_init2(&search_type, "application", "sdp");
216 + } else {
217 + pj_memcpy(&search_type, search_media_type, sizeof(search_type));
218 + }
219 +
220 + pjsip_media_type_init2(&multipart_mixed, "multipart", "mixed");
221 + pjsip_media_type_init2(&multipart_alternative, "multipart", "alternative");
222 +
223 + if (pjsip_media_type_cmp(msg_type, &search_type, PJ_FALSE) == 0)
224 {
225 - sdp_info->body.ptr = (char*)body->data;
226 - sdp_info->body.slen = body->len;
227 - } else if (body && ctype_hdr &&
228 - pj_stricmp2(&ctype_hdr->media.type, "multipart")==0 &&
229 - (pj_stricmp2(&ctype_hdr->media.subtype, "mixed")==0 ||
230 - pj_stricmp2(&ctype_hdr->media.subtype, "alternative")==0))
231 + /*
232 + * If the print_body function is print_sdp, we know that
233 + * body->data is a pjmedia_sdp_session object and came from
234 + * a tx_data. If not, it's the text representation of the
235 + * sdp from an rx_data.
236 + */
237 + if (body->print_body == print_sdp) {
238 + sdp_info->sdp = body->data;
239 + } else {
240 + sdp_info->body.ptr = (char*)body->data;
241 + sdp_info->body.slen = body->len;
242 + }
243 + } else if (pjsip_media_type_cmp(&multipart_mixed, msg_type, PJ_FALSE) == 0 ||
244 + pjsip_media_type_cmp(&multipart_alternative, msg_type, PJ_FALSE) == 0)
245 {
246 - pjsip_multipart_part *part;
247 + pjsip_multipart_part *part;
248 + part = pjsip_multipart_find_part(body, &search_type, NULL);
249 + if (part) {
250 + if (part->body->print_body == print_sdp) {
251 + sdp_info->sdp = part->body->data;
252 + } else {
253 + sdp_info->body.ptr = (char*)part->body->data;
254 + sdp_info->body.slen = part->body->len;
255 + }
256 + }
257 + }
258
259 - part = pjsip_multipart_find_part(body, &app_sdp, NULL);
260 - if (part) {
261 - sdp_info->body.ptr = (char*)part->body->data;
262 - sdp_info->body.slen = part->body->len;
263 - }
264 + /*
265 + * If the body was already a pjmedia_sdp_session, we can just
266 + * return it. If not and there wasn't a text representation
267 + * of the sdp either, we can also just return.
268 + */
269 + if (sdp_info->sdp || !sdp_info->body.ptr) {
270 + return sdp_info;
271 }
272
273 - if (sdp_info->body.ptr) {
274 - pj_status_t status;
275 - status = pjmedia_sdp_parse(rdata->tp_info.pool,
276 - sdp_info->body.ptr,
277 - sdp_info->body.slen,
278 - &sdp_info->sdp);
279 - if (status == PJ_SUCCESS)
280 - status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE);
281 + /*
282 + * If the body was the text representation of teh SDP, we need
283 + * to parse it to create a pjmedia_sdp_session object.
284 + */
285 + status = pjmedia_sdp_parse(pool,
286 + sdp_info->body.ptr,
287 + sdp_info->body.slen,
288 + &sdp_info->sdp);
289 + if (status == PJ_SUCCESS)
290 + status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE);
291
292 - if (status != PJ_SUCCESS) {
293 - sdp_info->sdp = NULL;
294 - PJ_PERROR(1,(THIS_FILE, status,
295 - "Error parsing/validating SDP body"));
296 - }
297 + if (status != PJ_SUCCESS) {
298 + sdp_info->sdp = NULL;
299 + PJ_PERROR(1, (THIS_FILE, status,
300 + "Error parsing/validating SDP body"));
301 + }
302 +
303 + sdp_info->sdp_err = status;
304 +
305 + return sdp_info;
306 +}
307 +
308 +PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2(
309 + pjsip_rx_data *rdata,
310 + const pjsip_media_type *search_media_type)
311 +{
312 + pjsip_media_type *msg_media_type = NULL;
313 + pjsip_rdata_sdp_info *sdp_info;
314
315 - sdp_info->sdp_err = status;
316 + if (rdata->endpt_info.mod_data[mod_inv.mod.id]) {
317 + return (pjsip_rdata_sdp_info *)rdata->endpt_info.mod_data[mod_inv.mod.id];
318 + }
319 +
320 + /*
321 + * rdata should have a Content-Type header at this point but we'll
322 + * make sure.
323 + */
324 + if (rdata->msg_info.ctype) {
325 + msg_media_type = &rdata->msg_info.ctype->media;
326 }
327 + sdp_info = pjsip_get_sdp_info(rdata->tp_info.pool,
328 + rdata->msg_info.msg->body,
329 + msg_media_type,
330 + search_media_type);
331 + rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info;
332
333 return sdp_info;
334 }
335
336 +PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
337 +{
338 + return pjsip_rdata_get_sdp_info2(rdata, NULL);
339 +}
340 +
341 +PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2(
342 + pjsip_tx_data *tdata,
343 + const pjsip_media_type *search_media_type)
344 +{
345 + pjsip_ctype_hdr *ctype_hdr = NULL;
346 + pjsip_media_type *msg_media_type = NULL;
347 + pjsip_tdata_sdp_info *sdp_info;
348 +
349 + if (tdata->mod_data[mod_inv.mod.id]) {
350 + return (pjsip_tdata_sdp_info *)tdata->mod_data[mod_inv.mod.id];
351 + }
352 + /*
353 + * tdata won't usually have a Content-Type header at this point
354 + * but we'll check just the same,
355 + */
356 + ctype_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTENT_TYPE, NULL);
357 + if (ctype_hdr) {
358 + msg_media_type = &ctype_hdr->media;
359 + }
360 +
361 + sdp_info = pjsip_get_sdp_info(tdata->pool,
362 + tdata->msg->body,
363 + msg_media_type,
364 + search_media_type);
365 + tdata->mod_data[mod_inv.mod.id] = sdp_info;
366 +
367 + return sdp_info;
368 +}
369 +
370 +PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata)
371 +{
372 + return pjsip_tdata_get_sdp_info2(tdata, NULL);
373 +}
374
375 /*
376 * Verify incoming INVITE request.
377 @@ -1740,13 +1846,55 @@ PJ_DEF(pj_status_t) pjsip_create_sdp_bod
378 return PJ_SUCCESS;
379 }
380
381 +static pjsip_multipart_part* create_sdp_part(pj_pool_t *pool, pjmedia_sdp_session *sdp)
382 +{
383 + pjsip_multipart_part *sdp_part;
384 + pjsip_media_type media_type;
385 +
386 + pjsip_media_type_init2(&media_type, "application", "sdp");
387 +
388 + sdp_part = pjsip_multipart_create_part(pool);
389 + PJ_ASSERT_RETURN(sdp_part != NULL, NULL);
390 +
391 + sdp_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
392 + PJ_ASSERT_RETURN(sdp_part->body != NULL, NULL);
393 +
394 + pjsip_media_type_cp(pool, &sdp_part->body->content_type, &media_type);
395 +
396 + sdp_part->body->data = sdp;
397 + sdp_part->body->clone_data = clone_sdp;
398 + sdp_part->body->print_body = print_sdp;
399 +
400 + return sdp_part;
401 +}
402 +
403 +PJ_DEF(pj_status_t) pjsip_create_multipart_sdp_body(pj_pool_t *pool,
404 + pjmedia_sdp_session *sdp,
405 + pjsip_msg_body **p_body)
406 +{
407 + pjsip_media_type media_type;
408 + pjsip_msg_body *multipart;
409 + pjsip_multipart_part *sdp_part;
410 +
411 + pjsip_media_type_init2(&media_type, "multipart", "mixed");
412 + multipart = pjsip_multipart_create(pool, &media_type, NULL);
413 + PJ_ASSERT_RETURN(multipart != NULL, PJ_ENOMEM);
414 +
415 + sdp_part = create_sdp_part(pool, sdp);
416 + PJ_ASSERT_RETURN(sdp_part != NULL, PJ_ENOMEM);
417 + pjsip_multipart_add_part(pool, multipart, sdp_part);
418 + *p_body = multipart;
419 +
420 + return PJ_SUCCESS;
421 +}
422 +
423 static pjsip_msg_body *create_sdp_body(pj_pool_t *pool,
424 const pjmedia_sdp_session *c_sdp)
425 {
426 pjsip_msg_body *body;
427 pj_status_t status;
428
429 - status = pjsip_create_sdp_body(pool,
430 + status = pjsip_create_sdp_body(pool,
431 pjmedia_sdp_session_clone(pool, c_sdp),
432 &body);
433
434 @@ -2069,6 +2217,7 @@ static pj_status_t inv_check_sdp_in_inco
435 )
436 )
437 {
438 + pjsip_sdp_info *tdata_sdp_info;
439 const pjmedia_sdp_session *reoffer_sdp = NULL;
440
441 PJ_LOG(4,(inv->obj_name, "Received %s response "
442 @@ -2077,14 +2226,15 @@ static pj_status_t inv_check_sdp_in_inco
443 (st_code/10==18? "early" : "final" )));
444
445 /* Retrieve original SDP offer from INVITE request */
446 - reoffer_sdp = (const pjmedia_sdp_session*)
447 - tsx->last_tx->msg->body->data;
448 + tdata_sdp_info = pjsip_tdata_get_sdp_info(tsx->last_tx);
449 + reoffer_sdp = tdata_sdp_info->sdp;
450
451 /* Feed the original offer to negotiator */
452 status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov,
453 inv->neg,
454 inv->sdp_neg_flags,
455 reoffer_sdp);
456 +
457 if (status != PJ_SUCCESS) {
458 PJ_LOG(1,(inv->obj_name, "Error updating local offer for "
459 "forked 2xx/18x response (err=%d)", status));
460 --- a/pjsip/src/test/inv_offer_answer_test.c
461 +++ b/pjsip/src/test/inv_offer_answer_test.c
462 @@ -137,6 +137,7 @@ typedef struct inv_test_param_t
463 pj_bool_t need_established;
464 unsigned count;
465 oa_t oa[4];
466 + pj_bool_t multipart_body;
467 } inv_test_param_t;
468
469 typedef struct inv_test_t
470 @@ -257,6 +258,17 @@ static void on_media_update(pjsip_inv_se
471 }
472 }
473
474 + /* Special handling for standard offer/answer */
475 + if (inv_test.param.count == 1 &&
476 + inv_test.param.oa[0] == OFFERER_UAC &&
477 + inv_test.param.need_established)
478 + {
479 + jobs[job_cnt].type = ESTABLISH_CALL;
480 + jobs[job_cnt].who = PJSIP_ROLE_UAS;
481 + job_cnt++;
482 + TRACE_((THIS_FILE, " C+++"));
483 + }
484 +
485 pj_assert(job_cnt <= PJ_ARRAY_SIZE(jobs));
486 }
487 }
488 @@ -333,6 +345,15 @@ static pj_bool_t on_rx_request(pjsip_rx_
489 NULL, &tdata);
490 pj_assert(status == PJ_SUCCESS);
491
492 + /* Use multipart body, if configured */
493 + if (sdp && inv_test.param.multipart_body) {
494 + status = pjsip_create_multipart_sdp_body(
495 + tdata->pool,
496 + pjmedia_sdp_session_clone(tdata->pool, sdp),
497 + &tdata->msg->body);
498 + }
499 + pj_assert(status == PJ_SUCCESS);
500 +
501 status = pjsip_inv_send_msg(inv_test.uas, tdata);
502 pj_assert(status == PJ_SUCCESS);
503
504 @@ -426,6 +447,7 @@ static int perform_test(inv_test_param_t
505 sdp = NULL;
506
507 status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac);
508 + //inv_test.uac->create_multipart = param->multipart_body;
509 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20);
510
511 TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without")));
512 @@ -436,8 +458,17 @@ static int perform_test(inv_test_param_t
513 status = pjsip_inv_invite(inv_test.uac, &tdata);
514 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
515
516 + /* Use multipart body, if configured */
517 + if (sdp && param->multipart_body) {
518 + status = pjsip_create_multipart_sdp_body(
519 + tdata->pool,
520 + pjmedia_sdp_session_clone(tdata->pool, sdp),
521 + &tdata->msg->body);
522 + }
523 + PJ_ASSERT_RETURN(status==PJ_SUCCESS, -40);
524 +
525 status = pjsip_inv_send_msg(inv_test.uac, tdata);
526 - PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
527 + PJ_ASSERT_RETURN(status==PJ_SUCCESS, -50);
528
529 /*
530 * Wait until test completes
531 @@ -525,13 +556,14 @@ static inv_test_param_t test_params[] =
532 200/INVITE (answer) <--
533 ACK -->
534 */
535 -#if 0
536 +#if 1
537 {
538 "Standard INVITE with offer",
539 0,
540 PJ_TRUE,
541 1,
542 - { OFFERER_UAC }
543 + { OFFERER_UAC },
544 + PJ_FALSE
545 },
546
547 {
548 @@ -539,7 +571,25 @@ static inv_test_param_t test_params[] =
549 PJSIP_INV_REQUIRE_100REL,
550 PJ_TRUE,
551 1,
552 - { OFFERER_UAC }
553 + { OFFERER_UAC },
554 + PJ_FALSE
555 + },
556 + {
557 + "Standard INVITE with offer, with Multipart",
558 + 0,
559 + PJ_TRUE,
560 + 1,
561 + { OFFERER_UAC },
562 + PJ_TRUE
563 + },
564 +
565 + {
566 + "Standard INVITE with offer, with 100rel, with Multipart",
567 + PJSIP_INV_REQUIRE_100REL,
568 + PJ_TRUE,
569 + 1,
570 + { OFFERER_UAC },
571 + PJ_TRUE
572 },
573 #endif
574
575 @@ -555,7 +605,8 @@ static inv_test_param_t test_params[] =
576 0,
577 PJ_TRUE,
578 1,
579 - { OFFERER_UAS }
580 + { OFFERER_UAS },
581 + PJ_FALSE
582 },
583
584 {
585 @@ -563,7 +614,25 @@ static inv_test_param_t test_params[] =
586 PJSIP_INV_REQUIRE_100REL,
587 PJ_TRUE,
588 1,
589 - { OFFERER_UAS }
590 + { OFFERER_UAS },
591 + PJ_FALSE
592 + },
593 + {
594 + "INVITE with no offer, with Multipart",
595 + 0,
596 + PJ_TRUE,
597 + 1,
598 + { OFFERER_UAS },
599 + PJ_TRUE
600 + },
601 +
602 + {
603 + "INVITE with no offer, with 100rel, with Multipart",
604 + PJSIP_INV_REQUIRE_100REL,
605 + PJ_TRUE,
606 + 1,
607 + { OFFERER_UAS },
608 + PJ_TRUE
609 },
610 #endif
611
612 @@ -584,14 +653,24 @@ static inv_test_param_t test_params[] =
613 0,
614 PJ_TRUE,
615 2,
616 - { OFFERER_UAC, OFFERER_UAC }
617 + { OFFERER_UAC, OFFERER_UAC },
618 + PJ_FALSE
619 + },
620 + {
621 + "INVITE and UPDATE by UAC, with Multipart",
622 + 0,
623 + PJ_TRUE,
624 + 2,
625 + { OFFERER_UAC, OFFERER_UAC },
626 + PJ_TRUE
627 },
628 {
629 "INVITE and UPDATE by UAC, with 100rel",
630 PJSIP_INV_REQUIRE_100REL,
631 PJ_TRUE,
632 2,
633 - { OFFERER_UAC, OFFERER_UAC }
634 + { OFFERER_UAC, OFFERER_UAC },
635 + PJ_FALSE
636 },
637 #endif
638
639 @@ -617,6 +696,14 @@ static inv_test_param_t test_params[] =
640 4,
641 { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS }
642 },
643 + {
644 + "INVITE and many UPDATE by UAC and UAS, with Multipart",
645 + 0,
646 + PJ_TRUE,
647 + 4,
648 + { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS },
649 + PJ_TRUE
650 + },
651
652 };
653