Merge pull request #761 from micmac1/21.02-AST-2022-01-02-03

[21.02] bump asterisk to 18.11.2
This commit is contained in:
Jiri Slachta 2022-04-19 12:29:43 +02:00 committed by GitHub
commit 818a3776c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1718 additions and 167 deletions

View file

@ -11,7 +11,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=pjproject
PKG_VERSION:=2.10
PKG_RELEASE:=6
PKG_RELEASE:=7
# download "vX.Y.tar.gz" as "pjproject-vX.Y.tar.gz"
PKG_SOURCE_URL_FILE:=$(PKG_VERSION).tar.gz

View file

@ -0,0 +1,27 @@
From 2ae784030b0d9cf217c3d562af20e4967f19a3dc Mon Sep 17 00:00:00 2001
From: George Joseph <gjoseph@sangoma.com>
Date: Tue, 14 Sep 2021 10:47:29 -0600
Subject: [PATCH] pjmedia_sdp_attr_get_rtpmap: Strip param trailing whitespace
Use pj_scan_get() to parse the param part of rtpmap so
trailing whitespace is automatically stripped.
Fixes #2827
---
pjmedia/src/pjmedia/sdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/pjmedia/src/pjmedia/sdp.c
+++ b/pjmedia/src/pjmedia/sdp.c
@@ -313,9 +313,9 @@ PJ_DEF(pj_status_t) pjmedia_sdp_attr_get
/* Expecting either '/' or EOF */
if (*scanner.curptr == '/') {
+ /* Skip the '/' */
pj_scan_get_char(&scanner);
- rtpmap->param.ptr = scanner.curptr;
- rtpmap->param.slen = scanner.end - scanner.curptr;
+ pj_scan_get(&scanner, &cs_token, &rtpmap->param);
} else {
rtpmap->param.slen = 0;
}

View file

@ -0,0 +1,653 @@
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
+ },
};

View file

@ -0,0 +1,116 @@
From 3faf1d2b4da553bbaee04f9a13a5d084b381e5fb Mon Sep 17 00:00:00 2001
From: sauwming <ming@teluu.com>
Date: Tue, 4 Jan 2022 15:28:49 +0800
Subject: [PATCH] Fix incorrect unescaping of tokens during parsing (#2933)
---
pjsip/src/pjsip/sip_parser.c | 29 +++++++++++++++++++++++++----
pjsip/src/test/msg_test.c | 6 +++---
2 files changed, 28 insertions(+), 7 deletions(-)
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -378,17 +378,23 @@ static pj_status_t init_parser()
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
pj_cis_add_str( &pconst.pjsip_TOKEN_SPEC, TOKEN);
+ /* Token is allowed to have '%' so we do not need this. */
+ /*
status = pj_cis_dup(&pconst.pjsip_TOKEN_SPEC_ESC, &pconst.pjsip_TOKEN_SPEC);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
pj_cis_del_str(&pconst.pjsip_TOKEN_SPEC_ESC, "%");
+ */
status = pj_cis_dup(&pconst.pjsip_VIA_PARAM_SPEC, &pconst.pjsip_TOKEN_SPEC);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC, "[:]");
+ /* Token is allowed to have '%' */
+ /*
status = pj_cis_dup(&pconst.pjsip_VIA_PARAM_SPEC_ESC, &pconst.pjsip_TOKEN_SPEC_ESC);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC_ESC, "[:]");
+ */
status = pj_cis_dup(&pconst.pjsip_HOST_SPEC, &pconst.pjsip_ALNUM_SPEC);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
@@ -1210,7 +1216,11 @@ static void parse_param_imp( pj_scanner
unsigned option)
{
/* pname */
- parser_get_and_unescape(scanner, pool, spec, esc_spec, pname);
+ if (!esc_spec) {
+ pj_scan_get(scanner, spec, pname);
+ } else {
+ parser_get_and_unescape(scanner, pool, spec, esc_spec, pname);
+ }
/* init pvalue */
pvalue->ptr = NULL;
@@ -1240,7 +1250,12 @@ static void parse_param_imp( pj_scanner
// pj_scan_get_until_ch(scanner, ']', pvalue);
// pj_scan_get_char(scanner);
} else if(pj_cis_match(spec, *scanner->curptr)) {
- parser_get_and_unescape(scanner, pool, spec, esc_spec, pvalue);
+ if (!esc_spec) {
+ pj_scan_get(scanner, spec, pvalue);
+ } else {
+ parser_get_and_unescape(scanner, pool, spec, esc_spec,
+ pvalue);
+ }
}
}
}
@@ -1252,7 +1267,10 @@ PJ_DEF(void) pjsip_parse_param_imp(pj_sc
unsigned option)
{
parse_param_imp(scanner, pool, pname, pvalue, &pconst.pjsip_TOKEN_SPEC,
- &pconst.pjsip_TOKEN_SPEC_ESC, option);
+ // Token does not need to be unescaped.
+ // Refer to PR #2933.
+ // &pconst.pjsip_TOKEN_SPEC_ESC,
+ NULL, option);
}
@@ -2168,7 +2186,10 @@ static void int_parse_via_param( pjsip_v
pj_scan_get_char(scanner);
parse_param_imp(scanner, pool, &pname, &pvalue,
&pconst.pjsip_VIA_PARAM_SPEC,
- &pconst.pjsip_VIA_PARAM_SPEC_ESC,
+ // Token does not need to be unescaped.
+ // Refer to PR #2933.
+ // &pconst.pjsip_VIA_PARAM_SPEC_ESC,
+ NULL,
0);
if (!parser_stricmp(pname, pconst.pjsip_BRANCH_STR) && pvalue.slen) {
--- a/pjsip/src/test/msg_test.c
+++ b/pjsip/src/test/msg_test.c
@@ -953,7 +953,7 @@ static int hdr_test_subject_utf(pjsip_hd
#define GENERIC_PARAM "p0=a;p1=\"ab:;cd\";p2=ab%3acd;p3"
-#define GENERIC_PARAM_PARSED "p0=a;p1=\"ab:;cd\";p2=ab:cd;p3"
+#define GENERIC_PARAM_PARSED "p0=a;p1=\"ab:;cd\";p2=ab%3acd;p3"
#define PARAM_CHAR "][/:&+$"
#define SIMPLE_ADDR_SPEC "sip:host"
#define ADDR_SPEC SIMPLE_ADDR_SPEC ";"PARAM_CHAR"="PARAM_CHAR ";p1=\";\""
@@ -1401,7 +1401,7 @@ static int generic_param_test(pjsip_para
param = param->next;
if (pj_strcmp2(&param->name, "p2"))
return -956;
- if (pj_strcmp2(&param->value, "ab:cd"))
+ if (pj_strcmp2(&param->value, "ab%3acd"))
return -957;
param = param->next;
@@ -1621,7 +1621,7 @@ static int hdr_test_content_type(pjsip_h
prm = prm->next;
if (prm == &hdr->media.param) return -1960;
if (pj_strcmp2(&prm->name, "p2")) return -1961;
- if (pj_strcmp2(&prm->value, "ab:cd")) return -1962;
+ if (pj_strcmp2(&prm->value, "ab%3acd")) return -1962;
prm = prm->next;
if (prm == &hdr->media.param) return -1970;

View file

@ -0,0 +1,169 @@
From 7e3dfd8a15fd0f98dbf0e04d2d7a5bded90ee401 Mon Sep 17 00:00:00 2001
From: George Joseph <gjoseph@sangoma.com>
Date: Tue, 11 Jan 2022 09:27:23 -0700
Subject: [PATCH] Create generic pjsip_hdr_find functions
pjsip_msg_find_hdr(), pjsip_msg_find_hdr_by_name(), and
pjsip_msg_find_hdr_by_names() require a pjsip_msg to be passed in
so if you need to search a header list that's not in a pjsip_msg,
you have to do it yourself. This commit adds generic versions of
those 3 functions that take in the actual header list head instead
of a pjsip_msg so if you need to search a list of headers in
something like a pjsip_multipart_part, you can do so easily.
---
pjsip/include/pjsip/sip_msg.h | 53 +++++++++++++++++++++++++++++++++++
pjsip/src/pjsip/sip_msg.c | 51 +++++++++++++++++++++++----------
2 files changed, 89 insertions(+), 15 deletions(-)
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -363,6 +363,59 @@ PJ_DECL(void*) pjsip_hdr_shallow_clone(
PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len);
/**
+ * Find a header in a header list by the header type.
+ *
+ * @param hdr_list The "head" of the header list.
+ * @param type The header type to find.
+ * @param start The first header field where the search should begin.
+ * If NULL is specified, then the search will begin from the
+ * first header, otherwise the search will begin at the
+ * specified header.
+ *
+ * @return The header field, or NULL if no header with the specified
+ * type is found.
+ */
+PJ_DECL(void*) pjsip_hdr_find( const void *hdr_list,
+ pjsip_hdr_e type,
+ const void *start);
+
+/**
+ * Find a header in a header list by its name.
+ *
+ * @param hdr_list The "head" of the header list.
+ * @param name The header name to find.
+ * @param start The first header field where the search should begin.
+ * If NULL is specified, then the search will begin from the
+ * first header, otherwise the search will begin at the
+ * specified header.
+ *
+ * @return The header field, or NULL if no header with the specified
+ * type is found.
+ */
+PJ_DECL(void*) pjsip_hdr_find_by_name( const void *hdr_list,
+ const pj_str_t *name,
+ const void *start);
+
+/**
+ * Find a header in a header list by its name and short name version.
+ *
+ * @param hdr_list The "head" of the header list.
+ * @param name The header name to find.
+ * @param sname The short name version of the header name.
+ * @param start The first header field where the search should begin.
+ * If NULL is specified, then the search will begin from the
+ * first header, otherwise the search will begin at the
+ * specified header.
+ *
+ * @return The header field, or NULL if no header with the specified
+ * type is found.
+ */
+PJ_DECL(void*) pjsip_hdr_find_by_names( const void *hdr_list,
+ const pj_str_t *name,
+ const pj_str_t *sname,
+ const void *start);
+
+/**
* @}
*/
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -334,13 +334,13 @@ PJ_DEF(pjsip_msg*) pjsip_msg_clone( pj_p
return dst;
}
-PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg,
- pjsip_hdr_e hdr_type, const void *start)
+PJ_DEF(void*) pjsip_hdr_find( const void *hdr_list,
+ pjsip_hdr_e hdr_type, const void *start)
{
- const pjsip_hdr *hdr=(const pjsip_hdr*) start, *end=&msg->hdr;
+ const pjsip_hdr *hdr=(const pjsip_hdr*) start, *end=hdr_list;
if (hdr == NULL) {
- hdr = msg->hdr.next;
+ hdr = end->next;
}
for (; hdr!=end; hdr = hdr->next) {
if (hdr->type == hdr_type)
@@ -349,14 +349,14 @@ PJ_DEF(void*) pjsip_msg_find_hdr( const
return NULL;
}
-PJ_DEF(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg,
- const pj_str_t *name,
- const void *start)
+PJ_DEF(void*) pjsip_hdr_find_by_name( const void *hdr_list,
+ const pj_str_t *name,
+ const void *start)
{
- const pjsip_hdr *hdr=(const pjsip_hdr*)start, *end=&msg->hdr;
+ const pjsip_hdr *hdr=(const pjsip_hdr*) start, *end=hdr_list;
if (hdr == NULL) {
- hdr = msg->hdr.next;
+ hdr = end->next;
}
for (; hdr!=end; hdr = hdr->next) {
if (pj_stricmp(&hdr->name, name) == 0)
@@ -365,15 +365,15 @@ PJ_DEF(void*) pjsip_msg_find_hdr_by_nam
return NULL;
}
-PJ_DEF(void*) pjsip_msg_find_hdr_by_names( const pjsip_msg *msg,
- const pj_str_t *name,
- const pj_str_t *sname,
- const void *start)
+PJ_DEF(void*) pjsip_hdr_find_by_names( const void *hdr_list,
+ const pj_str_t *name,
+ const pj_str_t *sname,
+ const void *start)
{
- const pjsip_hdr *hdr=(const pjsip_hdr*)start, *end=&msg->hdr;
+ const pjsip_hdr *hdr=(const pjsip_hdr*) start, *end=hdr_list;
if (hdr == NULL) {
- hdr = msg->hdr.next;
+ hdr = end->next;
}
for (; hdr!=end; hdr = hdr->next) {
if (pj_stricmp(&hdr->name, name) == 0)
@@ -384,6 +384,27 @@ PJ_DEF(void*) pjsip_msg_find_hdr_by_nam
return NULL;
}
+PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, const void *start)
+{
+ return pjsip_hdr_find(&msg->hdr, hdr_type, start);
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg,
+ const pj_str_t *name,
+ const void *start)
+{
+ return pjsip_hdr_find_by_name(&msg->hdr, name, start);
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr_by_names( const pjsip_msg *msg,
+ const pj_str_t *name,
+ const pj_str_t *sname,
+ const void *start)
+{
+ return pjsip_hdr_find_by_names(&msg->hdr, name, sname, start);
+}
+
PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
pjsip_hdr_e hdr_type, void *start)
{

View file

@ -0,0 +1,635 @@
From b7ecff22e77887626fd8e8608c4dd73bc7b7366f Mon Sep 17 00:00:00 2001
From: George Joseph <gjoseph@sangoma.com>
Date: Tue, 18 Jan 2022 06:14:31 -0700
Subject: [PATCH] Additional multipart improvements
Added the following APIs:
pjsip_multipart_find_part_by_header()
pjsip_multipart_find_part_by_header_str()
pjsip_multipart_find_part_by_cid_str()
pjsip_multipart_find_part_by_cid_uri()
---
pjsip/include/pjsip/sip_multipart.h | 83 ++++++++++
pjsip/src/pjsip/sip_multipart.c | 223 +++++++++++++++++++++++++++
pjsip/src/test/multipart_test.c | 225 +++++++++++++++++++++++++++-
3 files changed, 530 insertions(+), 1 deletion(-)
--- a/pjsip/include/pjsip/sip_multipart.h
+++ b/pjsip/include/pjsip/sip_multipart.h
@@ -154,6 +154,89 @@ pjsip_multipart_find_part( const pjsip_m
const pjsip_multipart_part *start);
/**
+ * Find a body inside multipart bodies which has a header matching the
+ * supplied one. Most useful for finding a part with a specific Content-ID.
+ *
+ * @param pool Memory pool to use for temp space.
+ * @param mp The multipart body.
+ * @param search_hdr Header to search for.
+ * @param start If specified, the search will begin at
+ * start->next part. Otherwise it will begin at
+ * the first part in the multipart bodies.
+ *
+ * @return The first part which has a header matching the
+ * specified one, or NULL if not found.
+ */
+PJ_DECL(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_header(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ void *search_hdr,
+ const pjsip_multipart_part *start);
+
+/**
+ * Find a body inside multipart bodies which has a header matching the
+ * supplied name and value. Most useful for finding a part with a specific
+ * Content-ID.
+ *
+ * @param pool Memory pool to use for temp space.
+ * @param mp The multipart body.
+ * @param hdr_name Header name to search for.
+ * @param hdr_value Header value search for.
+ * @param start If specified, the search will begin at
+ * start->next part. Otherwise it will begin at
+ * the first part in the multipart bodies.
+ *
+ * @return The first part which has a header matching the
+ * specified one, or NULL if not found.
+ */
+PJ_DECL(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_header_str(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ const pj_str_t *hdr_name,
+ const pj_str_t *hdr_value,
+ const pjsip_multipart_part *start);
+
+
+
+/**
+ * Find a body inside multipart bodies which has a Content-ID value matching the
+ * supplied "cid" URI in pj_str form. The "cid:" scheme will be assumed if the
+ * URL doesn't start with it. Enclosing angle brackets will also be handled
+ * correctly if they exist.
+ *
+ * @see RFC2392 Content-ID and Message-ID Uniform Resource Locators
+ *
+ * @param pool Memory pool to use for temp space.
+ * @param mp The multipart body.
+ * @param cid The "cid" URI to search for in pj_str form.
+ *
+ * @return The first part which has a Content-ID header matching the
+ * specified "cid" URI. or NULL if not found.
+ */
+PJ_DECL(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_cid_str(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ pj_str_t *cid);
+
+/**
+ * Find a body inside multipart bodies which has a Content-ID value matching the
+ * supplied "cid" URI.
+ *
+ * @see RFC2392 Content-ID and Message-ID Uniform Resource Locators
+ *
+ * @param pool Memory pool to use for temp space.
+ * @param mp The multipart body.
+ * @param cid The "cid" URI to search for.
+ *
+ * @return The first part which had a Content-ID header matching the
+ * specified "cid" URI. or NULL if not found.
+ */
+PJ_DECL(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_cid_uri(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ pjsip_other_uri *cid_uri);
+
+/**
* Parse multipart message.
*
* @param pool Memory pool.
--- a/pjsip/src/pjsip/sip_multipart.c
+++ b/pjsip/src/pjsip/sip_multipart.c
@@ -19,6 +19,7 @@
#include <pjsip/sip_multipart.h>
#include <pjsip/sip_parser.h>
#include <pjlib-util/scanner.h>
+#include <pjlib-util/string.h>
#include <pj/assert.h>
#include <pj/ctype.h>
#include <pj/errno.h>
@@ -416,6 +417,220 @@ pjsip_multipart_find_part( const pjsip_m
return NULL;
}
+/*
+ * Find a body inside multipart bodies which has the header and value.
+ */
+PJ_DEF(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_header_str(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ const pj_str_t *hdr_name,
+ const pj_str_t *hdr_value,
+ const pjsip_multipart_part *start)
+{
+ struct multipart_data *m_data;
+ pjsip_multipart_part *part;
+ pjsip_hdr *found_hdr;
+ pj_str_t found_hdr_str;
+ pj_str_t found_hdr_value;
+ pj_size_t expected_hdr_slen;
+ pj_size_t buf_size;
+ int hdr_name_len;
+#define REASONABLE_PADDING 32
+#define SEPARATOR_LEN 2
+ /* Must specify mandatory params */
+ PJ_ASSERT_RETURN(mp && hdr_name && hdr_value, NULL);
+
+ /* mp must really point to an actual multipart msg body */
+ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
+
+ /*
+ * We'll need to "print" each header we find to test it but
+ * allocating a buffer of PJSIP_MAX_URL_SIZE is overkill.
+ * Instead, we'll allocate one large enough to hold the search
+ * header name, the ": " separator, the search hdr value, and
+ * the NULL terminator. If we can't print the found header
+ * into that buffer then it can't be a match.
+ *
+ * Some header print functions such as generic_int require enough
+ * space to print the maximum possible header length so we'll
+ * add a reasonable amount to the print buffer size.
+ */
+ expected_hdr_slen = hdr_name->slen + SEPARATOR_LEN + hdr_value->slen;
+ buf_size = expected_hdr_slen + REASONABLE_PADDING;
+ found_hdr_str.ptr = pj_pool_alloc(pool, buf_size);
+ found_hdr_str.slen = 0;
+ hdr_name_len = hdr_name->slen + SEPARATOR_LEN;
+
+ m_data = (struct multipart_data*)mp->data;
+
+ if (start)
+ part = start->next;
+ else
+ part = m_data->part_head.next;
+
+ while (part != &m_data->part_head) {
+ found_hdr = NULL;
+ while ((found_hdr = pjsip_hdr_find_by_name(&part->hdr, hdr_name,
+ (found_hdr ? found_hdr->next : NULL))) != NULL) {
+
+ found_hdr_str.slen = pjsip_hdr_print_on((void*) found_hdr, found_hdr_str.ptr, buf_size);
+ /*
+ * If the buffer was too small (slen = -1) or the result wasn't
+ * the same length as the search header, it can't be a match.
+ */
+ if (found_hdr_str.slen != expected_hdr_slen) {
+ continue;
+ }
+ /*
+ * Set the value overlay to start at the found header value...
+ */
+ found_hdr_value.ptr = found_hdr_str.ptr + hdr_name_len;
+ found_hdr_value.slen = found_hdr_str.slen - hdr_name_len;
+ /* ...and compare it to the supplied header value. */
+ if (pj_strcmp(hdr_value, &found_hdr_value) == 0) {
+ return part;
+ }
+ }
+ part = part->next;
+ }
+ return NULL;
+#undef SEPARATOR_LEN
+#undef REASONABLE_PADDING
+}
+
+PJ_DEF(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_header(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ void *search_for,
+ const pjsip_multipart_part *start)
+{
+ struct multipart_data *m_data;
+ pjsip_hdr *search_hdr = search_for;
+ pj_str_t search_buf;
+
+ /* Must specify mandatory params */
+ PJ_ASSERT_RETURN(mp && search_hdr, NULL);
+
+ /* mp must really point to an actual multipart msg body */
+ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
+
+ /*
+ * Unfortunately, there isn't enough information to determine
+ * the maximum printed size of search_hdr at this point so we
+ * have to allocate a reasonable max.
+ */
+ search_buf.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+ search_buf.slen = pjsip_hdr_print_on(search_hdr, search_buf.ptr, PJSIP_MAX_URL_SIZE - 1);
+ if (search_buf.slen <= 0) {
+ return NULL;
+ }
+ /*
+ * Set the header value to start after the header name plus the ":", then
+ * strip leading and trailing whitespace.
+ */
+ search_buf.ptr += (search_hdr->name.slen + 1);
+ search_buf.slen -= (search_hdr->name.slen + 1);
+ pj_strtrim(&search_buf);
+
+ return pjsip_multipart_find_part_by_header_str(pool, mp, &search_hdr->name, &search_buf, start);
+}
+
+/*
+ * Convert a Content-ID URI to it's corresponding header value.
+ * RFC2392 says...
+ * A "cid" URL is converted to the corresponding Content-ID message
+ * header by removing the "cid:" prefix, converting the % encoded
+ * character(s) to their equivalent US-ASCII characters, and enclosing
+ * the remaining parts with an angle bracket pair, "<" and ">".
+ *
+ * This implementation will accept URIs with or without the "cid:"
+ * scheme and optional angle brackets.
+ */
+static pj_str_t cid_uri_to_hdr_value(pj_pool_t *pool, pj_str_t *cid_uri)
+{
+ pj_size_t cid_len = pj_strlen(cid_uri);
+ pj_size_t alloc_len = cid_len + 2 /* for the leading and trailing angle brackets */;
+ pj_str_t uri_overlay;
+ pj_str_t cid_hdr;
+ pj_str_t hdr_overlay;
+
+ pj_strassign(&uri_overlay, cid_uri);
+ /* If the URI is already enclosed in angle brackets, remove them. */
+ if (uri_overlay.ptr[0] == '<') {
+ uri_overlay.ptr++;
+ uri_overlay.slen -= 2;
+ }
+ /* If the URI starts with the "cid:" scheme, skip over it. */
+ if (pj_strncmp2(&uri_overlay, "cid:", 4) == 0) {
+ uri_overlay.ptr += 4;
+ uri_overlay.slen -= 4;
+ }
+ /* Start building */
+ cid_hdr.ptr = pj_pool_alloc(pool, alloc_len);
+ cid_hdr.ptr[0] = '<';
+ cid_hdr.slen = 1;
+ hdr_overlay.ptr = cid_hdr.ptr + 1;
+ hdr_overlay.slen = 0;
+ pj_strcpy_unescape(&hdr_overlay, &uri_overlay);
+ cid_hdr.slen += hdr_overlay.slen;
+ cid_hdr.ptr[cid_hdr.slen] = '>';
+ cid_hdr.slen++;
+
+ return cid_hdr;
+}
+
+PJ_DEF(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_cid_str(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ pj_str_t *cid)
+{
+ struct multipart_data *m_data;
+ pjsip_multipart_part *part;
+ pjsip_generic_string_hdr *found_hdr;
+ pj_str_t found_hdr_value;
+ static pj_str_t hdr_name = { "Content-ID", 10};
+ pj_str_t hdr_value;
+
+ PJ_ASSERT_RETURN(pool && mp && cid && (pj_strlen(cid) > 0), NULL);
+
+ hdr_value = cid_uri_to_hdr_value(pool, cid);
+ if (pj_strlen(&hdr_value) == 0) {
+ return NULL;
+ }
+
+ m_data = (struct multipart_data*)mp->data;
+ part = m_data->part_head.next;
+
+ while (part != &m_data->part_head) {
+ found_hdr = NULL;
+ while ((found_hdr = pjsip_hdr_find_by_name(&part->hdr, &hdr_name,
+ (found_hdr ? found_hdr->next : NULL))) != NULL) {
+ if (pj_strcmp(&hdr_value, &found_hdr->hvalue) == 0) {
+ return part;
+ }
+ }
+ part = part->next;
+ }
+ return NULL;
+}
+
+PJ_DEF(pjsip_multipart_part*)
+pjsip_multipart_find_part_by_cid_uri(pj_pool_t *pool,
+ const pjsip_msg_body *mp,
+ pjsip_other_uri *cid_uri)
+{
+ PJ_ASSERT_RETURN(pool && mp && cid_uri, NULL);
+
+ if (pj_strcmp2(&cid_uri->scheme, "cid") != 0) {
+ return NULL;
+ }
+ /*
+ * We only need to pass the URI content so we
+ * can do that directly.
+ */
+ return pjsip_multipart_find_part_by_cid_str(pool, mp, &cid_uri->content);
+}
+
/* Parse a multipart part. "pct" is parent content-type */
static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool,
char *start,
@@ -584,6 +799,7 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_
(int)boundary.slen, boundary.ptr));
}
+
/* Build the delimiter:
* delimiter = "--" boundary
*/
@@ -630,6 +846,8 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_
if (*curptr=='\r') ++curptr;
if (*curptr!='\n') {
/* Expecting a newline here */
+ PJ_LOG(2, (THIS_FILE, "Failed to find newline"));
+
return NULL;
}
++curptr;
@@ -645,6 +863,7 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_
curptr = pj_strstr(&subbody, &delim);
if (!curptr) {
/* We're really expecting end delimiter to be found. */
+ PJ_LOG(2, (THIS_FILE, "Failed to find end-delimiter"));
return NULL;
}
}
@@ -670,9 +889,13 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_
part = parse_multipart_part(pool, start_body, end_body - start_body,
ctype);
if (part) {
+ TRACE_((THIS_FILE, "Adding part"));
pjsip_multipart_add_part(pool, body, part);
+ } else {
+ PJ_LOG(2, (THIS_FILE, "Failed to add part"));
}
}
+ TRACE_((THIS_FILE, "pjsip_multipart_parse finished: %p", body));
return body;
}
--- a/pjsip/src/test/multipart_test.c
+++ b/pjsip/src/test/multipart_test.c
@@ -28,6 +28,7 @@
typedef pj_status_t (*verify_ptr)(pj_pool_t*,pjsip_msg_body*);
static pj_status_t verify1(pj_pool_t *pool, pjsip_msg_body *body);
+static pj_status_t verify2(pj_pool_t *pool, pjsip_msg_body *body);
static struct test_t
{
@@ -68,7 +69,41 @@ static struct test_t
"This is epilogue, which should be ignored too",
&verify1
+ },
+ {
+ /* Content-type */
+ "multipart", "mixed", "12345",
+
+ /* Body: */
+ "This is the prolog, which should be ignored.\r\n"
+ "--12345\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-ID: <header1@example.org>\r\n"
+ "Content-ID: <\"header1\"@example.org>\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "has header1\r\n"
+ "--12345 \t\r\n"
+ "Content-Type: application/pidf+xml\r\n"
+ "Content-ID: <my header2@example.org>\r\n"
+ "Content-ID: <my\xffheader2@example.org>\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "has header2\r\n"
+ "--12345\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-ID: <my header3@example.org>\r\n"
+ "Content-ID: <header1@example.org>\r\n"
+ "Content-ID: <my header4@example.org>\r\n"
+ "Content-Length: 13\r\n"
+ "\r\n"
+ "has header4\r\n"
+ "--12345--\r\n"
+ "This is epilogue, which should be ignored too",
+
+ &verify2
}
+
};
static void init_media_type(pjsip_media_type *mt,
@@ -87,6 +122,192 @@ static void init_media_type(pjsip_media_
}
}
+static int verify_hdr(pj_pool_t *pool, pjsip_msg_body *multipart_body,
+ void *hdr, char *part_body)
+{
+ pjsip_media_type mt;
+ pjsip_multipart_part *part;
+ pj_str_t the_body;
+
+
+ part = pjsip_multipart_find_part_by_header(pool, multipart_body, hdr, NULL);
+ if (!part) {
+ return -1;
+ }
+
+ the_body.ptr = (char*)part->body->data;
+ the_body.slen = part->body->len;
+
+ if (pj_strcmp2(&the_body, part_body) != 0) {
+ return -2;
+ }
+
+ return 0;
+}
+
+static int verify_cid_str(pj_pool_t *pool, pjsip_msg_body *multipart_body,
+ pj_str_t cid_url, char *part_body)
+{
+ pjsip_media_type mt;
+ pjsip_multipart_part *part;
+ pj_str_t the_body;
+
+ part = pjsip_multipart_find_part_by_cid_str(pool, multipart_body, &cid_url);
+ if (!part) {
+ return -3;
+ }
+
+ the_body.ptr = (char*)part->body->data;
+ the_body.slen = part->body->len;
+
+ if (pj_strcmp2(&the_body, part_body) != 0) {
+ return -4;
+ }
+
+ return 0;
+}
+
+static int verify_cid_uri(pj_pool_t *pool, pjsip_msg_body *multipart_body,
+ pjsip_other_uri *cid_uri, char *part_body)
+{
+ pjsip_media_type mt;
+ pjsip_multipart_part *part;
+ pj_str_t the_body;
+
+ part = pjsip_multipart_find_part_by_cid_uri(pool, multipart_body, cid_uri);
+ if (!part) {
+ return -5;
+ }
+
+ the_body.ptr = (char*)part->body->data;
+ the_body.slen = part->body->len;
+
+ if (pj_strcmp2(&the_body, part_body) != 0) {
+ return -6;
+ }
+
+ return 0;
+}
+
+static pj_status_t verify2(pj_pool_t *pool, pjsip_msg_body *body)
+{
+ int rc = 0;
+ int rcbase = 300;
+ pjsip_other_uri *cid_uri;
+ pjsip_ctype_hdr *ctype_hdr = pjsip_ctype_hdr_create(pool);
+
+ ctype_hdr->media.type = pj_str("application");
+ ctype_hdr->media.subtype = pj_str("pidf+xml");
+
+ rc = verify_hdr(pool, body, ctype_hdr, "has header2");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("cid:header1@example.org"), "has header1");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("%22header1%22@example.org"), "has header1");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ cid_uri = pjsip_uri_get_uri(pjsip_parse_uri(pool, "<cid:%22header1%22@example.org>",
+ strlen("<cid:%22header1%22@example.org>"), 0));
+ rcbase += 10;
+ rc = verify_cid_uri(pool, body, cid_uri, "has header1");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("<cid:my%20header2@example.org>"), "has header2");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("cid:my%ffheader2@example.org"), "has header2");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ cid_uri = pjsip_uri_get_uri(pjsip_parse_uri(pool, "<cid:my%ffheader2@example.org>",
+ strlen("<cid:my%ffheader2@example.org>"), 0));
+ rcbase += 10;
+ rc = verify_cid_uri(pool, body, cid_uri, "has header2");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("cid:my%20header3@example.org"), "has header4");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("<cid:my%20header4@example.org>"), "has header4");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ cid_uri = pjsip_uri_get_uri(pjsip_parse_uri(pool, "<cid:my%20header4@example.org>",
+ strlen("<cid:my%20header4@example.org>"), 0));
+ rcbase += 10;
+ rc = verify_cid_uri(pool, body, cid_uri, "has header4");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("<my%20header3@example.org>"), "has header4");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ /* These should all fail for malformed or missing URI */
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("cid:"), "has header4");
+ if (!rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str(""), "has header4");
+ if (!rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("<>"), "has header4");
+ if (!rc) {
+ return (rc - rcbase);
+ }
+
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("<cid>"), "has header4");
+ if (!rc) {
+ return (rc - rcbase);
+ }
+
+ /*
+ * This is going to pass but the ' ' in the uri is un-encoded which is invalid
+ * so we should never see it.
+ */
+ rcbase += 10;
+ rc = verify_cid_str(pool, body, pj_str("cid:my header3@example.org"), "has header4");
+ if (rc) {
+ return (rc - rcbase);
+ }
+
+ return 0;
+}
+
static int verify_part(pjsip_multipart_part *part,
char *h_content_type,
char *h_content_subtype,
@@ -236,8 +457,10 @@ static int parse_test(void)
pj_strdup2_with_null(pool, &str, p_tests[i].msg);
body = pjsip_multipart_parse(pool, str.ptr, str.slen, &ctype, 0);
- if (!body)
+ if (!body) {
+ pj_pool_release(pool);
return -100;
+ }
if (p_tests[i].verify) {
rc = p_tests[i].verify(pool, body);

View file

@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=asterisk
PKG_VERSION:=18.5.1
PKG_RELEASE:=2
PKG_VERSION:=18.11.2
PKG_RELEASE:=1
PKG_SOURCE:=asterisk-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://downloads.asterisk.org/pub/telephony/asterisk/releases
PKG_HASH:=e7d78716a0deeadf24b7d537cd24c11c2d9a096265eefc9470565c4da0fc54c7
PKG_HASH:=b456d19610e53789446e097bb9773a82a04088922de0cfaff2b818326b67296b
PKG_BUILD_DEPENDS:=libxml2/host
@ -58,6 +58,7 @@ MODULES_AVAILABLE:= \
app-directed-pickup \
app-directory \
app-disa \
app-dtmfstore \
app-dumpchan \
app-exec \
app-externalivr \
@ -68,6 +69,7 @@ MODULES_AVAILABLE:= \
app-ices \
app-image \
app-ivrdemo \
app-mf \
app-milliwatt \
app-minivm \
app-mixmonitor \
@ -81,10 +83,12 @@ MODULES_AVAILABLE:= \
app-read \
app-readexten \
app-record \
app-reload \
app-saycounted \
app-sayunixtime \
app-senddtmf \
app-sendtext \
app-sf \
app-skel \
app-sms \
app-softhangup \
@ -100,6 +104,7 @@ MODULES_AVAILABLE:= \
app-url \
app-userevent \
app-verbose \
app-waitforcond \
app-voicemail \
app-voicemail-imap \
app-voicemail-odbc \
@ -180,6 +185,7 @@ MODULES_AVAILABLE:= \
func-enum \
func-env \
func-extstate \
func-frame-drop \
func-frame-trace \
func-global \
func-groupcount \
@ -187,6 +193,7 @@ MODULES_AVAILABLE:= \
func-holdintercept \
func-iconv \
func-jitterbuffer \
func-json \
func-lock \
func-math \
func-md5 \
@ -196,6 +203,8 @@ MODULES_AVAILABLE:= \
func-presencestate \
func-rand \
func-realtime \
func-sayfiles \
func-scramble \
func-sha1 \
func-shell \
func-sorcery \
@ -304,6 +313,7 @@ MODULES_AVAILABLE:= \
res-stun-monitor \
res-timing-dahdi \
res-timing-pthread \
res-tonedetect \
res-xmpp
UTILS_AVAILABLE:= \
@ -564,6 +574,7 @@ CONFIGURE_ARGS+= \
--without-pjproject-bundled \
--with-libedit="$(STAGING_DIR)/usr" \
--with-libxml2 \
--without-libxslt \
$(if $(CONFIG_PACKAGE_$(PKG_NAME)-res-snmp),--with-netsnmp="$(STAGING_DIR)/usr",--without-netsnmp) \
--without-newt \
--without-osptk \
@ -781,16 +792,18 @@ $(eval $(call BuildAsteriskModule,app-dictate,Virtual dictation machine,Virtual
$(eval $(call BuildAsteriskModule,app-directed-pickup,Directed call pickup,Directed call pickup application.,,,app_directed_pickup,,))
$(eval $(call BuildAsteriskModule,app-directory,Extension directory,Extension directory.,,,app_directory,,))
$(eval $(call BuildAsteriskModule,app-disa,Direct Inward System Access,Direct Inward System Access application.,,,app_disa,,))
$(eval $(call BuildAsteriskModule,app-dtmfstore,DTMF storage,Technology independent async DTMF storage.,,,app_dtmfstore,,))
$(eval $(call BuildAsteriskModule,app-dumpchan,Dump info about channel,Dump info about the calling channel.,,,app_dumpchan,,))
$(eval $(call BuildAsteriskModule,app-exec,Exec application,Executes dialplan applications.,,,app_exec,,))
$(eval $(call BuildAsteriskModule,app-externalivr,External IVR interface,External IVR interface application.,,,app_externalivr,,))
$(eval $(call BuildAsteriskModule,app-festival,Simple festival interface,Simple Festival interface.,,festival.conf,app_festival,,))
$(eval $(call BuildAsteriskModule,app-flash,Flash channel,Flash channel application.,+$(PKG_NAME)-chan-dahdi,,app_flash,,))
$(eval $(call BuildAsteriskModule,app-followme,Find-me/follow-me,Find-Me/Follow-Me application.,,followme.conf,app_followme,,))
$(eval $(call BuildAsteriskModule,app-getcpeid,Get ADSI CPE ID,Get ADSI CPE ID.,,,app_getcpeid,,))
$(eval $(call BuildAsteriskModule,app-getcpeid,Get ADSI CPE ID,Get ADSI CPE ID.,+asterisk-res-adsi,,app_getcpeid,,))
$(eval $(call BuildAsteriskModule,app-ices,Encode and stream,Encode and stream via Icecast and IceS.,,,app_ices,,))
$(eval $(call BuildAsteriskModule,app-image,Image transmission,Image transmission application.,,,app_image,,))
$(eval $(call BuildAsteriskModule,app-ivrdemo,IVR demo,IVR demo application.,,,app_ivrdemo,,))
$(eval $(call BuildAsteriskModule,app-mf,MF digits,Send MF digits Application.,,,app_mf,,))
$(eval $(call BuildAsteriskModule,app-milliwatt,Digital milliwatt [mu-law] test app,Digital milliwatt test application.,,,app_milliwatt,,))
$(eval $(call BuildAsteriskModule,app-minivm,Minimal voicemail system,A minimal voicemail e-mail system.,,extensions_minivm.conf minivm.conf,app_minivm,,))
$(eval $(call BuildAsteriskModule,app-mixmonitor,Record a call and mix the audio,Mixed audio monitoring application.,,,app_mixmonitor,,))
@ -804,10 +817,12 @@ $(eval $(call BuildAsteriskModule,app-queue,True Call Queueing,True call queuein
$(eval $(call BuildAsteriskModule,app-read,Variable read,Read variable application.,,,app_read,,))
$(eval $(call BuildAsteriskModule,app-readexten,Extension to variable,Read and evaluate extension validity.,,,app_readexten,,))
$(eval $(call BuildAsteriskModule,app-record,Record sound file,Trivial record application.,,,app_record,,))
$(eval $(call BuildAsteriskModule,app-reload,Reload,Reload module[s].,,,app_reload,,))
$(eval $(call BuildAsteriskModule,app-saycounted,Decline words,Decline words according to channel language.,,,app_saycounted,,))
$(eval $(call BuildAsteriskModule,app-sayunixtime,Say Unix time,Say time.,,,app_sayunixtime,,))
$(eval $(call BuildAsteriskModule,app-senddtmf,Send DTMF digits,Send DTMF digits application.,,,app_senddtmf,,))
$(eval $(call BuildAsteriskModule,app-sendtext,Send text,Send text applications.,,,app_sendtext,,))
$(eval $(call BuildAsteriskModule,app-sf,SF Sender and Receiver Applications,SF Sender and Receiver Applications.,,,app_sf,,))
$(eval $(call BuildAsteriskModule,app-skel,Skeleton [sample],Skeleton application.,,app_skel.conf,app_skel,,))
$(eval $(call BuildAsteriskModule,app-sms,SMS,SMS/PSTN handler.,,,app_sms,,))
$(eval $(call BuildAsteriskModule,app-softhangup,Hang up requested channel,Hangs up the requested channel.,,,app_softhangup,,))
@ -823,6 +838,7 @@ $(eval $(call BuildAsteriskModule,app-transfer,Transfers caller to other ext,Tra
$(eval $(call BuildAsteriskModule,app-url,Send URL,Send URL applications.,,,app_url,,))
$(eval $(call BuildAsteriskModule,app-userevent,Custom user event,Custom user event application.,,,app_userevent,,))
$(eval $(call BuildAsteriskModule,app-verbose,Verbose logging,Send verbose output.,,,app_verbose,,))
$(eval $(call BuildAsteriskModule,app-waitforcond,Wait for condition,Wait until condition is true.,,,app_waitforcond,,))
$(eval $(call BuildAsteriskModule,app-voicemail,Voicemail,Voicemail module.,,voicemail.conf,app_voicemail,vm-*,))
$(eval $(call BuildAsteriskModule,app-voicemail-imap,Voicemail IMAP,Voicemail module.,+uw-imap,,app_voicemail_imap,,))
$(eval $(call BuildAsteriskModule,app-voicemail-odbc,Voicemail ODBC,Voicemail module.,+unixodbc,,app_voicemail_odbc,,))
@ -903,6 +919,7 @@ $(eval $(call BuildAsteriskModule,func-dialplan,Dialplan context/extension/prior
$(eval $(call BuildAsteriskModule,func-enum,ENUM,ENUM related dialplan functions.,,enum.conf,func_enum,,))
$(eval $(call BuildAsteriskModule,func-env,Environment functions,Environment/filesystem dialplan functions.,,,func_env,,))
$(eval $(call BuildAsteriskModule,func-extstate,Hinted extension state,Gets the state of an extension in the dialplan.,,,func_extstate,,))
$(eval $(call BuildAsteriskModule,func-frame-drop,Frame drop,Function to drop frames on a channel.,,,func_frame_drop,,))
$(eval $(call BuildAsteriskModule,func-frame-trace,Frame trace for internal ast_frame debugging,Frame trace for internal ast_frame debugging.,,,func_frame_trace,,))
$(eval $(call BuildAsteriskModule,func-global,Global variable,Variable dialplan functions.,,,func_global,,))
$(eval $(call BuildAsteriskModule,func-groupcount,Group count,Channel group dialplan functions.,,,func_groupcount,,))
@ -910,6 +927,7 @@ $(eval $(call BuildAsteriskModule,func-hangupcause,HANGUPCAUSE related functions
$(eval $(call BuildAsteriskModule,func-holdintercept,Hold interception dialplan function,Hold interception dialplan function.,,,func_holdintercept,,))
$(eval $(call BuildAsteriskModule,func-iconv,Charset conversion,Charset conversions.,,,func_iconv,,,$(ICONV_DEPENDS)))
$(eval $(call BuildAsteriskModule,func-jitterbuffer,Jitter buffer for read side of channel,Jitter buffer for read side of channel.,,,func_jitterbuffer,,))
$(eval $(call BuildAsteriskModule,func-json,JSON decoding function,JSON decoding function.,,,func_json,,))
$(eval $(call BuildAsteriskModule,func-lock,Dialplan mutexes,Dialplan mutexes.,,,func_lock,,))
$(eval $(call BuildAsteriskModule,func-math,Math functions,Mathematical dialplan function.,,,func_math,,))
$(eval $(call BuildAsteriskModule,func-md5,MD5 digest dialplan functions,MD5 digest dialplan functions.,,,func_md5,,))
@ -919,6 +937,8 @@ $(eval $(call BuildAsteriskModule,func-pitchshift,Audio effects dialplan functio
$(eval $(call BuildAsteriskModule,func-presencestate,Hinted presence state,Gets or sets a presence state in the dialplan.,,,func_presencestate,,))
$(eval $(call BuildAsteriskModule,func-rand,RAND dialplan function,Random number dialplan function.,,,func_rand,,))
$(eval $(call BuildAsteriskModule,func-realtime,REALTIME dialplan function,Read/write/store/destroy values from a realtime repository.,,,func_realtime,,))
$(eval $(call BuildAsteriskModule,func-sayfiles,Say files,Say application files.,,,func_sayfiles,,))
$(eval $(call BuildAsteriskModule,func-scramble,Scramble,Frequency inverting voice scrambler.,,,func_scramble,,))
$(eval $(call BuildAsteriskModule,func-sha1,SHA-1 computation dialplan function,SHA-1 computation dialplan function.,,,func_sha1,,))
$(eval $(call BuildAsteriskModule,func-shell,Shell,Collects the output generated by a command executed by the system shell.,,,func_shell,,))
$(eval $(call BuildAsteriskModule,func-sorcery,Get a field from a sorcery object,Get a field from a sorcery object.,,,func_sorcery,,))
@ -1027,6 +1047,7 @@ $(eval $(call BuildAsteriskModule,res-stir-shaken,STIR/SHAKEN resource module,ST
$(eval $(call BuildAsteriskModule,res-stun-monitor,STUN monitoring,STUN network monitor.,,res_stun_monitor.conf,res_stun_monitor,,))
$(eval $(call BuildAsteriskModule,res-timing-dahdi,DAHDI Timing Interface,DAHDI timing interface.,+$(PKG_NAME)-chan-dahdi,,res_timing_dahdi,,))
$(eval $(call BuildAsteriskModule,res-timing-pthread,pthread Timing Interface,pthread timing interface.,,,res_timing_pthread,,))
$(eval $(call BuildAsteriskModule,res-tonedetect,Tone detection,Tone detection module.,,,res_tonedetect,,))
$(eval $(call BuildAsteriskModule,res-xmpp,XMPP client and component module,Asterisk XMPP interface.,+libiksemel +libopenssl,xmpp.conf,res_xmpp,,))
################################

View file

@ -1,6 +1,6 @@
--- a/configure.ac
+++ b/configure.ac
@@ -1040,15 +1040,18 @@ AC_LINK_IFELSE(
@@ -1090,15 +1090,18 @@ AC_LINK_IFELSE(
# Some platforms define sem_init(), but only support sem_open(). joyous.
AC_MSG_CHECKING(for working unnamed semaphores)

View file

@ -18,7 +18,7 @@ Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
--- a/configure.ac
+++ b/configure.ac
@@ -1434,7 +1434,11 @@ AC_LINK_IFELSE(
@@ -1484,7 +1484,11 @@ AC_LINK_IFELSE(
#include <arpa/nameser.h>
#endif
#include <resolv.h>],

View file

@ -1,147 +0,0 @@
From 1e5a2cfe3037823b17dd4ac47b071f02d6f9825f Mon Sep 17 00:00:00 2001
From: Naveen Albert <mail@interlinked.x10host.com>
Date: Fri, 21 May 2021 20:08:58 -0400
Subject: [PATCH] app_dial: Expanded A option to add caller announcement
Hitherto, the A option has made it possible to play
audio upon answer to the called party only. This option
is expanded to allow for playback of an audio file to
the caller instead of or in addition to the audio
played to the answerer.
ASTERISK-29442
Change-Id: If6eed3ff5c341dc8c588c8210987f2571e891e5e
---
apps/app_dial.c | 79 +++++++++++++++----
doc/CHANGES-staging/app_dial_announcement.txt | 6 ++
2 files changed, 68 insertions(+), 17 deletions(-)
create mode 100644 doc/CHANGES-staging/app_dial_announcement.txt
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -93,11 +93,17 @@
</parameter>
<parameter name="options" required="false">
<optionlist>
- <option name="A">
- <argument name="x" required="true">
+ <option name="A" argsep=":">
+ <argument name="x">
<para>The file to play to the called party</para>
</argument>
- <para>Play an announcement to the called party, where <replaceable>x</replaceable> is the prompt to be played</para>
+ <argument name="y">
+ <para>The file to play to the calling party</para>
+ </argument>
+ <para>Play an announcement to the called and/or calling parties, where <replaceable>x</replaceable>
+ is the prompt to be played to the called party and <replaceable>y</replaceable> is the prompt
+ to be played to the caller. The files may be different and will be played to each party
+ simultaneously.</para>
</option>
<option name="a">
<para>Immediately answer the calling channel when the called channel answers in
@@ -2941,33 +2947,71 @@ static int dial_exec_full(struct ast_cha
int digit = 0;
struct ast_channel *chans[2];
struct ast_channel *active_chan;
+ char *calledfile = NULL, *callerfile = NULL;
+ int calledstream = 0, callerstream = 0;
chans[0] = chan;
chans[1] = peer;
- /* we need to stream the announcement to the called party when the OPT_ARG_ANNOUNCE (-A) is setted */
-
- /* stream the file */
- res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], ast_channel_language(peer));
- if (res) {
- res = 0;
- ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", opt_args[OPT_ARG_ANNOUNCE]);
+ /* we need to stream the announcement(s) when the OPT_ARG_ANNOUNCE (-A) is set */
+ callerfile = opt_args[OPT_ARG_ANNOUNCE];
+ calledfile = strsep(&callerfile, ":");
+
+ /* stream the file(s) */
+ if (!ast_strlen_zero(calledfile)) {
+ res = ast_streamfile(peer, calledfile, ast_channel_language(peer));
+ if (res) {
+ res = 0;
+ ast_log(LOG_ERROR, "error streaming file '%s' to callee\n", calledfile);
+ } else {
+ calledstream = 1;
+ }
+ }
+ if (!ast_strlen_zero(callerfile)) {
+ res = ast_streamfile(chan, callerfile, ast_channel_language(chan));
+ if (res) {
+ res = 0;
+ ast_log(LOG_ERROR, "error streaming file '%s' to caller\n", callerfile);
+ } else {
+ callerstream = 1;
+ }
}
+ /* can't use ast_waitstream, because we're streaming two files at once, and can't block
+ We'll need to handle both channels at once. */
+
ast_channel_set_flag(peer, AST_FLAG_END_DTMF_ONLY);
- while (ast_channel_stream(peer)) {
- int ms;
+ while (ast_channel_stream(peer) || ast_channel_stream(chan)) {
+ int mspeer, mschan;
- ms = ast_sched_wait(ast_channel_sched(peer));
+ mspeer = ast_sched_wait(ast_channel_sched(peer));
+ mschan = ast_sched_wait(ast_channel_sched(chan));
- if (ms < 0 && !ast_channel_timingfunc(peer)) {
- ast_stopstream(peer);
+ if (calledstream) {
+ if (mspeer < 0 && !ast_channel_timingfunc(peer)) {
+ ast_stopstream(peer);
+ calledstream = 0;
+ }
+ }
+ if (callerstream) {
+ if (mschan < 0 && !ast_channel_timingfunc(chan)) {
+ ast_stopstream(chan);
+ callerstream = 0;
+ }
+ }
+
+ if (!calledstream && !callerstream) {
break;
}
- if (ms < 0)
- ms = 1000;
- active_chan = ast_waitfor_n(chans, 2, &ms);
+ if (mspeer < 0)
+ mspeer = 1000;
+
+ if (mschan < 0)
+ mschan = 1000;
+
+ /* wait for the lowest maximum of the two */
+ active_chan = ast_waitfor_n(chans, 2, (mspeer > mschan ? &mschan : &mspeer));
if (active_chan) {
struct ast_channel *other_chan;
struct ast_frame *fr = ast_read(active_chan);
@@ -3017,6 +3061,7 @@ static int dial_exec_full(struct ast_cha
ast_frfree(fr);
}
ast_sched_runq(ast_channel_sched(peer));
+ ast_sched_runq(ast_channel_sched(chan));
}
ast_channel_clear_flag(peer, AST_FLAG_END_DTMF_ONLY);
}
--- /dev/null
+++ b/doc/CHANGES-staging/app_dial_announcement.txt
@@ -0,0 +1,6 @@
+Subject: app_dial announcement option
+
+The A option for Dial now supports
+playing audio to the caller as well
+as the called party.
+

View file

@ -15,14 +15,14 @@
cat << END
/*
* build.h
--- a/Makefile
+++ b/Makefile
@@ -489,7 +489,7 @@ doc/core-en_US.xml: makeopts .lastclean
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
@for x in $(MOD_SUBDIRS); do \
printf "$$x " ; \
- for i in `find $$x -name '*.c'`; do \
+ for i in `find $$x -name '*.c' | LC_ALL=C sort`; do \
MODULEINFO=$$($(AWK) -f build_tools/get_moduleinfo $$i) ; \
if [ -n "$$MODULEINFO" ] ; \
then \
--- a/build_tools/make_xml_documentation
+++ b/build_tools/make_xml_documentation
@@ -187,7 +187,7 @@ printf "Building Documentation For: "
for subdir in ${mod_subdirs} ; do
printf "%s " "${subdir}"
subdir_path="${source_tree}/${subdir}"
- for i in $(${FIND} "${subdir_path}" -name '*.c' -or -name '*.cc'); do
+ for i in $(${FIND} "${subdir_path}" -name '*.c' -or -name '*.cc' | LC_ALL=C sort); do
if [ "${with_moduleinfo}" -eq "1" ] ; then
MODULEINFO=$(${AWK} -f "${source_tree}/build_tools/get_moduleinfo" "${i}")
if [ "${MODULEINFO}" != "" ] ; then

View file

@ -1,6 +1,6 @@
--- a/configure.ac
+++ b/configure.ac
@@ -1205,7 +1205,7 @@ if test "${ac_cv_have_variable_fdset}x"
@@ -1263,7 +1263,7 @@ if test "${ac_cv_have_variable_fdset}x"
fi
AC_MSG_CHECKING([if we have usable eventfd support])

View file

@ -1,6 +1,6 @@
--- a/configure.ac
+++ b/configure.ac
@@ -2555,7 +2555,7 @@ if test -z "$__opus_include" -o x"$__opu
@@ -2619,7 +2619,7 @@ if test -z "$__opus_include" -o x"$__opu
fi
AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])

View file

@ -0,0 +1,22 @@
--- a/autoconf/ast_ext_tool_check.m4
+++ b/autoconf/ast_ext_tool_check.m4
@@ -8,13 +8,16 @@ AC_DEFUN([AST_EXT_TOOL_CHECK],
AC_REQUIRE([AST_PROG_SED])dnl
if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
PBX_$1=0
- AC_PATH_TOOL(CONFIG_$1, $2, No, [${$1_DIR}/bin:$PATH])
+ if test "x${$1_DIR}" != "x"; then
+ AC_PATH_TOOL(CONFIG_$1, $2, No, [${$1_DIR}/bin:$PATH])
+ else
+ AC_PATH_TOOL(CONFIG_$1, $2, No, [$PATH])
+ fi
if test ! "x${CONFIG_$1}" = xNo; then
$1_INCLUDE=$(${CONFIG_$1} m4_default([$3],[--cflags]))
- $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-I|-I${$1_DIR}|g" -e "s|-std=c99||g")
+ $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-std=c99||g")
$1_LIB=$(${CONFIG_$1} m4_default([$4],[--libs]))
- $1_LIB=$(echo ${$1_LIB} | $SED -e "s|-L|-L${$1_DIR}|g")
m4_ifval([$5], [
saved_cppflags="${CPPFLAGS}"

View file

@ -0,0 +1,55 @@
From: https://issues.asterisk.org/jira/browse/ASTERISK-29905
From d27d75ad8058f6ed35197737b949bac57202dd54 Mon Sep 17 00:00:00 2001
From: "Sergey V. Lobanov" <sergey@lobanov.in>
Date: Wed, 9 Feb 2022 01:29:46 +0300
Subject: [PATCH] build: fix bininstall launchd issue on cross-platfrom build
configure script detects /sbin/launchd, but the result of this
check is not used in Makefile (bininstall). Makefile also detects
/sbin/launchd file to decide if it is required to install
safe_asterisk.
configure script correctly detects cross compile build and sets
PBX_LAUNCHD=0
In case of building asterisk on MacOS host for Linux target using
external toolchain (e.g. OpenWrt toolchain), bininstall does not
install safe_asterisk (due to /sbin/launchd detection in Makefile),
but it is required on target (Linux).
This patch adds HAVE_SBIN_LAUNCHD=@PBX_LAUNCHD@ to makeopts.in to
use the result of /sbin/launchd detection from configure script in
Makefile.
Also this patch uses HAVE_SBIN_LAUNCHD in Makefile (bininstall) to
decide if it is required to install safe_asterisk.
Signed-off-by: Sergey V. Lobanov <sergey@lobanov.in>
---
Makefile | 6 +++---
makeopts.in | 2 ++
2 files changed, 5 insertions(+), 3 deletions(-)
--- a/Makefile
+++ b/Makefile
@@ -558,9 +558,9 @@ bininstall: _all installdirs $(SUBDIRS_I
$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
- if [ ! -f /sbin/launchd ]; then \
- ./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk"; \
- fi
+ifneq ($(HAVE_SBIN_LAUNCHD),1)
+ ./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk";
+endif
ifneq ($(DISABLE_XMLDOC),yes)
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
--- a/makeopts.in
+++ b/makeopts.in
@@ -373,3 +373,5 @@ SNDFILE_LIB=@SNDFILE_LIB@
BEANSTALK_INCLUDE=@BEANSTALK_INCLUDE@
BEANSTALK_LIB=@BEANSTALK_LIB@
+
+HAVE_SBIN_LAUNCHD=@PBX_LAUNCHD@