include $(TOPDIR)/rules.mk
PKG_NAME:=pjproject
-PKG_VERSION:=2.10
+PKG_VERSION:=2.12.1
PKG_RELEASE:=$(AUTORELEASE)
# download "vX.Y.tar.gz" as "pjproject-vX.Y.tar.gz"
PKG_SOURCE_URL_FILE:=$(PKG_VERSION).tar.gz
PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_URL_FILE)
PKG_SOURCE_URL:=https://github.com/pjsip/$(PKG_NAME)/archive
-PKG_HASH:=936a4c5b98601b52325463a397ddf11ab4106c6a7b04f8dc7cdd377efbb597de
+PKG_HASH:=d0feef6963b07934e821ba4328aecb4c36358515c1b3e507da5874555d713533
PKG_INSTALL:=1
PKG_LICENSE:=GPL-2.0
CONFIGURE_ARGS+= \
$(if $(CONFIG_SOFT_FLOAT),--disable-floating-point) \
+ --disable-android-mediacodec \
--disable-bcg729 \
--disable-darwin-ssl \
--disable-ext-sound \
TARGET_CFLAGS+=$(TARGET_CPPFLAGS)
define Build/Compile
- $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) dep
- $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)
+ $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) EXCLUDE_APP=1 dep
+ $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) EXCLUDE_APP=1
endef
PJPROJECT_LIBS:= \
$(foreach m,$(PJPROJECT_LIBS),$(CP) $(PKG_INSTALL_DIR)/usr/lib/$(m)* $(1)/usr/lib;)
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
- $(SED) 's|$(TARGET_CFLAGS)||g' $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libpjproject.pc
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/libpjproject.pc \
$(1)/usr/lib/pkgconfig
endef
+++ /dev/null
---- a/build/rules.mak
-+++ b/build/rules.mak
-@@ -13,7 +13,7 @@ SHLIB = $($(APP)_SHLIB)
- SONAME = $($(APP)_SONAME)
-
- ifeq ($(SHLIB_SUFFIX),so)
--SHLIB_OPT := -shared -Wl,-soname,$(SHLIB)
-+SHLIB_OPT := -shared
- else ifeq ($(SHLIB_SUFFIX),dylib)
- SHLIB_OPT := -dynamiclib -undefined dynamic_lookup -flat_namespace
- else ifeq ($(SHLIB_SUFFIX),dll)
+++ /dev/null
---- a/pjlib/src/pj/os_core_unix.c
-+++ b/pjlib/src/pj/os_core_unix.c
-@@ -1139,7 +1139,7 @@ static pj_status_t init_mutex(pj_mutex_t
- return PJ_RETURN_OS_ERROR(rc);
-
- if (type == PJ_MUTEX_SIMPLE) {
--#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
-+#if (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || \
- defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
- #elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
-@@ -1149,7 +1149,7 @@ static pj_status_t init_mutex(pj_mutex_t
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
- #endif
- } else {
--#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
-+#if (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || \
- defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
- rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
- #elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
--- /dev/null
+++ b/pjlib/include/pj/config_site.h
-@@ -0,0 +1,79 @@
+@@ -0,0 +1,83 @@
+/*
+ * Asterisk config_site.h
+ */
+/* Increase limits to allow more formats */
+#define PJMEDIA_MAX_SDP_FMT 64
+#define PJMEDIA_MAX_SDP_BANDW 4
-+#define PJMEDIA_MAX_SDP_ATTR (PJMEDIA_MAX_SDP_FMT*2 + 4)
++#define PJMEDIA_MAX_SDP_ATTR (PJMEDIA_MAX_SDP_FMT*3 + 4)
+#define PJMEDIA_MAX_SDP_MEDIA 16
+
+/*
+ */
+#define PJSIP_TCP_KEEP_ALIVE_INTERVAL 0
+#define PJSIP_TLS_KEEP_ALIVE_INTERVAL 0
++
++#define PJSIP_TSX_UAS_CONTINUE_ON_TP_ERROR 0
++#define PJ_SSL_SOCK_OSSL_USE_THREAD_CB 0
++#define PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER 1
+++ /dev/null
---- a/build.mak.in
-+++ b/build.mak.in
-@@ -296,7 +296,6 @@ export APP_LDLIBS := $(PJSUA_LIB_LDLIB)
- $(PJLIB_LDLIB) \
- @LIBS@
- export APP_LDXXLIBS := $(PJSUA2_LIB_LDLIB) \
-- -lstdc++ \
- $(APP_LDLIBS)
-
- # Here are the variabels to use if application is using the library
---- a/pjsip/build/Makefile
-+++ b/pjsip/build/Makefile
-@@ -152,8 +152,7 @@ export PJSUA2_LIB_LDFLAGS += $(PJSUA_LIB
- $(PJNATH_LDLIB) \
- $(PJLIB_UTIL_LDLIB) \
- $(PJLIB_LDLIB) \
-- $(_LDFLAGS) \
-- -lstdc++
-+ $(_LDFLAGS)
-
-
- ###############################################################################
Description: Multimedia communication library
--- a/build.mak.in
+++ b/build.mak.in
-@@ -314,8 +314,7 @@ export PJ_LIBXX_FILES := $(APP_LIBXX_FIL
- # And here are the variables to use if application is using the
- # library from the install location (i.e. --prefix)
+@@ -349,6 +349,6 @@ export PJ_LIBXX_FILES := $(APP_LIBXX_FIL
export PJ_INSTALL_DIR := @prefix@
--export PJ_INSTALL_INC_DIR := @includedir@
+ export PJ_INSTALL_INC_DIR := @includedir@
export PJ_INSTALL_LIB_DIR := @libdir@
--export PJ_INSTALL_CFLAGS := -I$(PJ_INSTALL_INC_DIR) -DPJ_AUTOCONF=1 @CFLAGS@
-+export PJ_INSTALL_CFLAGS := -DPJ_AUTOCONF=1 @CFLAGS@
- export PJ_INSTALL_CXXFLAGS := @CXXFLAGS@ $(PJ_INSTALL_CFLAGS)
- export PJ_INSTALL_LDFLAGS := -L$(PJ_INSTALL_LIB_DIR) $(APP_LDLIBS)
+-export PJ_INSTALL_CFLAGS := -I$(PJ_INSTALL_INC_DIR) -DPJ_AUTOCONF=1 @ac_cflags@
++export PJ_INSTALL_CFLAGS := -DPJ_AUTOCONF=1 @ac_cflags@
+ export PJ_INSTALL_LDFLAGS_PRIVATE := $(APP_THIRD_PARTY_LIBS) $(APP_THIRD_PARTY_EXT) @LIBS@
+-export PJ_INSTALL_LDFLAGS := -L$(PJ_INSTALL_LIB_DIR) $(filter-out $(PJ_INSTALL_LDFLAGS_PRIVATE),$(APP_LDXXLIBS))
++export PJ_INSTALL_LDFLAGS := $(filter-out $(PJ_INSTALL_LDFLAGS_PRIVATE),$(APP_LDXXLIBS))
--- /dev/null
+In upstream commit b236337 unit tests were extended to print stack traces when
+crashing, using GNU extensions. But this won't work with musl etc. (no
+"execinfo.h"), so we need to update the condition to make sure this is only
+done when glibc is used.
+
+--- a/pjlib-util/src/pjlib-util-test/main.c
++++ b/pjlib-util/src/pjlib-util-test/main.c
+@@ -33,7 +33,7 @@ static void init_signals()
+ sigaction(SIGALRM, &act, NULL);
+ }
+
+-#elif PJ_LINUX || PJ_DARWINOS
++#elif (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>\r
+ #include <signal.h>
+--- a/pjlib/src/pjlib-test/main.c
++++ b/pjlib/src/pjlib-test/main.c
+@@ -54,7 +54,7 @@ static void init_signals()
+ sigaction(SIGALRM, &act, NULL);
+ }
+
+-#elif PJ_LINUX || PJ_DARWINOS
++#elif (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>\r
+ #include <signal.h>
+--- a/pjmedia/src/test/main.c
++++ b/pjmedia/src/test/main.c
+@@ -32,7 +32,7 @@
+ #endif
+
+
+-#if PJ_LINUX || PJ_DARWINOS
++#if (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>\r
+ #include <signal.h>
+--- a/pjnath/src/pjnath-test/main.c
++++ b/pjnath/src/pjnath-test/main.c
+@@ -32,7 +32,7 @@ static void init_signals()
+ sigaction(SIGALRM, &act, NULL);
+ }
+
+-#elif PJ_LINUX || PJ_DARWINOS
++#elif (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>\r
+ #include <signal.h>
+--- a/pjsip-apps/src/pjsua/main.c
++++ b/pjsip-apps/src/pjsua/main.c
+@@ -80,7 +80,7 @@ static void setup_signal_handler(void)
+ SetConsoleCtrlHandler(&CtrlHandler, TRUE);
+ }
+
+-#elif PJ_LINUX || PJ_DARWINOS
++#elif (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>
+ #include <signal.h>
+--- a/pjsip/src/test/main.c
++++ b/pjsip/src/test/main.c
+@@ -36,7 +36,7 @@ static void usage(void)
+ list_tests();
+ }
+
+-#if PJ_LINUX || PJ_DARWINOS
++#if (defined(PJ_LINUX) && PJ_LINUX!=0 && defined(__GLIBC__)) || PJ_DARWINOS
+
+ #include <execinfo.h>\r
+ #include <signal.h>
+++ /dev/null
-commit c3c1bf45cae2a35003aa16c267d59f97027f9c5e
-Author: Kevin Harwell <kharwell@digium.com>
-Date: Thu Jun 11 11:11:13 2020 -0500
-
- sip_inv - fix invite session ref count crash
-
- Ensure the session's ref count is only decremented under proper conditons.
-
- For more details see the following issue report:
- https://github.com/pjsip/pjproject/issues/2443
-
- Patch supplied by sauwming
-
---- a/pjsip/src/pjsip-ua/sip_inv.c
-+++ b/pjsip/src/pjsip-ua/sip_inv.c
-@@ -323,9 +323,19 @@ static void inv_set_state(pjsip_inv_sess
- (*mod_inv.cb.on_state_changed)(inv, e);
- pjsip_inv_dec_ref(inv);
-
-- /* Only decrement when previous state is not already DISCONNECTED */
-+ /* The above callback may change the state, so we need to be careful here
-+ * and only decrement inv under the following conditions:
-+ * 1. If the state parameter is DISCONNECTED, and previous state is not
-+ * already DISCONNECTED.
-+ * This is to make sure that dec_ref() is not called more than once.
-+ * 2. If current state is PJSIP_INV_STATE_DISCONNECTED.
-+ * This is to make sure that dec_ref() is not called if user restarts
-+ * inv within the callback. Note that this check must be last since
-+ * inv may have already been destroyed.
-+ */
- if (state == PJSIP_INV_STATE_DISCONNECTED &&
-- prev_state != PJSIP_INV_STATE_DISCONNECTED)
-+ prev_state != PJSIP_INV_STATE_DISCONNECTED &&
-+ inv->state == PJSIP_INV_STATE_DISCONNECTED)
- {
- pjsip_inv_dec_ref(inv);
- }
+++ /dev/null
-commit 40dd48d10911f4ff9b8dfbf16428fbc9acc434ba
-Author: Riza Sulistyo <trengginas@users.noreply.github.com>
-Date: Thu Jul 9 17:47:24 2020 +0700
-
- Modify timer_id check on cancel() (#2463)
-
- * modify timer_id check on cancel().
-
- * modification based on comments.
-
---- a/pjlib/include/pj/timer.h
-+++ b/pjlib/include/pj/timer.h
-@@ -120,7 +120,10 @@ typedef struct pj_timer_entry
-
- /**
- * Internal unique timer ID, which is assigned by the timer heap.
-- * Application should not touch this ID.
-+ * Positive values indicate that the timer entry is running,
-+ * while -1 means that it's not. Any other value may indicate that it
-+ * hasn't been properly initialised or is in a bad state.
-+ * Application should not touch this ID.
- */
- pj_timer_id_t _timer_id;
-
---- a/pjlib/src/pj/timer.c
-+++ b/pjlib/src/pj/timer.c
-@@ -535,7 +535,7 @@ static int cancel( pj_timer_heap_t *ht,
- PJ_CHECK_STACK();
-
- // Check to see if the timer_id is out of range
-- if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) {
-+ if (entry->_timer_id < 1 || (pj_size_t)entry->_timer_id >= ht->max_size) {
- entry->_timer_id = -1;
- return 0;
- }
+++ /dev/null
-From 78683646c8bc670ec730a42494e075f671a08e28 Mon Sep 17 00:00:00 2001
-From: Guido Falsi <mad@madpilot.net>
-Date: Mon, 11 May 2020 08:50:39 +0200
-Subject: [PATCH] Fix race condition in parallel builds (#2426)
-
-* Some targets residing in `OBJDIRS` are missing a dependency on that directory, which results in a race condition, causing build to fail sometimes due to the directory not existing when running parallel builds.
-
-* The `PJSUA_LIB` variable is not defined anywhere, resulting in an empty value, and no correct dependency on the pjsua shared library for `pjsua2`. The correct variable seems to be `PJSUA_LIB_LIB`, defined at the start of this same `Makefile`.
----
- build/rules.mak | 12 ++++++------
- pjsip/build/Makefile | 2 +-
- 2 files changed, 7 insertions(+), 7 deletions(-)
-
---- a/build/rules.mak
-+++ b/build/rules.mak
-@@ -129,7 +129,7 @@ endif
- $(OBJDIR)/$(app).o: $(OBJDIRS) $(OBJS)
- $(CROSS_COMPILE)ld -r -o $@ $(OBJS)
-
--$(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o
-+$(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o | $(OBJDIRS)
- @echo Creating kbuild Makefile...
- @echo "# Our module name:" > $(OBJDIR)/Makefile
- @echo 'obj-m += $(app).o' >> $(OBJDIR)/Makefile
-@@ -154,27 +154,27 @@ $(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o
- ../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko
- cp $(OBJDIR)/$(app).ko ../lib
-
--$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.m
-+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.m | $(OBJDIRS)
- $(CC) $($(APP)_CFLAGS) \
- $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
- $(subst /,$(HOST_PSEP),$<)
-
--$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c
-+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c | $(OBJDIRS)
- $(CC) $($(APP)_CFLAGS) \
- $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
- $(subst /,$(HOST_PSEP),$<)
-
--$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S
-+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S | $(OBJDIRS)
- $(CC) $($(APP)_CFLAGS) \
- $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
- $(subst /,$(HOST_PSEP),$<)
-
--$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp
-+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp | $(OBJDIRS)
- $(CXX) $($(APP)_CXXFLAGS) \
- $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
- $(subst /,$(HOST_PSEP),$<)
-
--$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cc
-+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cc | $(OBJDIRS)
- $(CXX) $($(APP)_CXXFLAGS) \
- $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
- $(subst /,$(HOST_PSEP),$<)
---- a/pjsip/build/Makefile
-+++ b/pjsip/build/Makefile
-@@ -261,7 +261,7 @@ $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME): $(
-
- pjsua2-lib: $(PJSUA2_LIB_LIB)
- $(PJSUA2_LIB_SONAME): $(PJSUA2_LIB_LIB)
--$(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME): $(PJSUA_LIB) $(PSJUA_LIB_SONAME) $(PJSIP_LIB) $(PJSIP_SONAME) $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME) $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME)
-+$(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME): $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) $(PJSIP_LIB) $(PJSIP_SONAME) $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME) $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME)
- $(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
-
- pjsip-test: $(TEST_EXE)
+++ /dev/null
---- a/pjmedia/src/pjmedia/sdp_neg.c
-+++ b/pjmedia/src/pjmedia/sdp_neg.c
-@@ -906,7 +906,7 @@ static pj_status_t process_m_answer( pj_
- * after receiving remote answer.
- */
- static pj_status_t process_answer(pj_pool_t *pool,
-- pjmedia_sdp_session *offer,
-+ pjmedia_sdp_session *local_offer,
- pjmedia_sdp_session *answer,
- pj_bool_t allow_asym,
- pjmedia_sdp_session **p_active)
-@@ -914,10 +914,14 @@ static pj_status_t process_answer(pj_poo
- unsigned omi = 0; /* Offer media index */
- unsigned ami = 0; /* Answer media index */
- pj_bool_t has_active = PJ_FALSE;
-+ pjmedia_sdp_session *offer;
- pj_status_t status;
-
- /* Check arguments. */
-- PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL);
-+ PJ_ASSERT_RETURN(pool && local_offer && answer && p_active, PJ_EINVAL);
-+
-+ /* Duplicate local offer SDP. */
-+ offer = pjmedia_sdp_session_clone(pool, local_offer);
-
- /* Check that media count match between offer and answer */
- // Ticket #527, different media count is allowed for more interoperability,
+++ /dev/null
-From ce18018cc17bef8f80c08686e3a7b28384ef3ba5 Mon Sep 17 00:00:00 2001
-From: sauwming <ming@teluu.com>
-Date: Mon, 12 Oct 2020 13:31:25 +0800
-Subject: [PATCH] Fix incorrect copying of destination info when creating
- CANCEL (#2546)
-
----
- pjsip/src/pjsip/sip_util.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/pjsip/src/pjsip/sip_util.c
-+++ b/pjsip/src/pjsip/sip_util.c
-@@ -779,14 +779,14 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_c
- pjsip_hdr_clone(cancel_tdata->pool, req_tdata->saved_strict_route);
- }
-
-- /* Copy the destination host name from the original request */
-- pj_strdup(cancel_tdata->pool, &cancel_tdata->dest_info.name,
-- &req_tdata->dest_info.name);
--
-- /* Finally copy the destination info from the original request */
-+ /* Copy the destination info from the original request */
- pj_memcpy(&cancel_tdata->dest_info, &req_tdata->dest_info,
- sizeof(req_tdata->dest_info));
-
-+ /* Finally, copy the destination host name from the original request */
-+ pj_strdup(cancel_tdata->pool, &cancel_tdata->dest_info.name,
-+ &req_tdata->dest_info.name);
-+
- /* Done.
- * Return the transmit buffer containing the CANCEL request.
- */
+++ /dev/null
---- a/pjmedia/src/pjmedia/sdp_neg.c
-+++ b/pjmedia/src/pjmedia/sdp_neg.c
-@@ -304,7 +304,6 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modi
- {
- pjmedia_sdp_session *new_offer;
- pjmedia_sdp_session *old_offer;
-- char media_used[PJMEDIA_MAX_SDP_MEDIA];
- unsigned oi; /* old offer media index */
- pj_status_t status;
-
-@@ -323,8 +322,19 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modi
- /* Change state to STATE_LOCAL_OFFER */
- neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
-
-+ /* When there is no active local SDP in state PJMEDIA_SDP_NEG_STATE_DONE,
-+ * it means that the previous initial SDP nego must have been failed,
-+ * so we'll just set the local SDP offer here.
-+ */
-+ if (!neg->active_local_sdp) {
-+ neg->initial_sdp_tmp = NULL;
-+ neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
-+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
-+
-+ return PJ_SUCCESS;
-+ }
-+
- /* Init vars */
-- pj_bzero(media_used, sizeof(media_used));
- old_offer = neg->active_local_sdp;
- new_offer = pjmedia_sdp_session_clone(pool, local);
-
+++ /dev/null
-From bdbeb7c4b2b11efc2e59f5dee7aa4360a2bc9fff Mon Sep 17 00:00:00 2001
-From: sauwming <ming@teluu.com>
-Date: Thu, 22 Apr 2021 14:03:28 +0800
-Subject: [PATCH 90/90] Skip unsupported digest algorithm (#2408)
-
-Co-authored-by: Nanang Izzuddin <nanang@teluu.com>
----
- pjsip/src/pjsip/sip_auth_client.c | 32 +++++--
- tests/pjsua/scripts-sipp/uas-auth-two-algo.py | 7 ++
- .../pjsua/scripts-sipp/uas-auth-two-algo.xml | 83 +++++++++++++++++++
- 3 files changed, 117 insertions(+), 5 deletions(-)
- create mode 100644 tests/pjsua/scripts-sipp/uas-auth-two-algo.py
- create mode 100644 tests/pjsua/scripts-sipp/uas-auth-two-algo.xml
-
---- a/pjsip/src/pjsip/sip_auth_client.c
-+++ b/pjsip/src/pjsip/sip_auth_client.c
-@@ -1042,7 +1042,7 @@ static pj_status_t process_auth( pj_pool
- pjsip_hdr *hdr;
- pj_status_t status;
-
-- /* See if we have sent authorization header for this realm */
-+ /* See if we have sent authorization header for this realm (and scheme) */
- hdr = tdata->msg->hdr.next;
- while (hdr != &tdata->msg->hdr) {
- if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
-@@ -1052,7 +1052,8 @@ static pj_status_t process_auth( pj_pool
- {
- sent_auth = (pjsip_authorization_hdr*) hdr;
- if (pj_stricmp(&hchal->challenge.common.realm,
-- &sent_auth->credential.common.realm )==0)
-+ &sent_auth->credential.common.realm)==0 &&
-+ pj_stricmp(&hchal->scheme, &sent_auth->scheme)==0)
- {
- /* If this authorization has empty response, remove it. */
- if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 &&
-@@ -1062,6 +1063,14 @@ static pj_status_t process_auth( pj_pool
- hdr = hdr->next;
- pj_list_erase(sent_auth);
- continue;
-+ } else
-+ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 &&
-+ pj_stricmp(&sent_auth->credential.digest.algorithm,
-+ &hchal->challenge.digest.algorithm)!=0)
-+ {
-+ /* Same 'digest' scheme but different algo */
-+ hdr = hdr->next;
-+ continue;
- } else {
- /* Found previous authorization attempt */
- break;
-@@ -1155,9 +1164,10 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
- {
- pjsip_tx_data *tdata;
- const pjsip_hdr *hdr;
-- unsigned chal_cnt;
-+ unsigned chal_cnt, auth_cnt;
- pjsip_via_hdr *via;
- pj_status_t status;
-+ pj_status_t last_auth_err;
-
- PJ_ASSERT_RETURN(sess && rdata && old_request && new_request,
- PJ_EINVAL);
-@@ -1178,6 +1188,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
- */
- hdr = rdata->msg_info.msg->hdr.next;
- chal_cnt = 0;
-+ auth_cnt = 0;
-+ last_auth_err = PJSIP_EAUTHNOAUTH;
- while (hdr != &rdata->msg_info.msg->hdr) {
- pjsip_cached_auth *cached_auth;
- const pjsip_www_authenticate_hdr *hchal;
-@@ -1222,8 +1234,13 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
- */
- status = process_auth(tdata->pool, hchal, tdata->msg->line.req.uri,
- tdata, sess, cached_auth, &hauth);
-- if (status != PJ_SUCCESS)
-- return status;
-+ if (status != PJ_SUCCESS) {
-+ last_auth_err = status;
-+
-+ /* Process next header. */
-+ hdr = hdr->next;
-+ continue;
-+ }
-
- if (pj_pool_get_used_size(cached_auth->pool) >
- PJSIP_AUTH_CACHED_POOL_MAX_SIZE)
-@@ -1236,12 +1253,17 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
-
- /* Process next header. */
- hdr = hdr->next;
-+ auth_cnt++;
- }
-
- /* Check if challenge is present */
- if (chal_cnt == 0)
- return PJSIP_EAUTHNOCHAL;
-
-+ /* Check if any authorization header has been created */
-+ if (auth_cnt == 0)
-+ return last_auth_err;
-+
- /* Remove branch param in Via header. */
- via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
- via->branch_param.slen = 0;
---- /dev/null
-+++ b/tests/pjsua/scripts-sipp/uas-auth-two-algo.py
-@@ -0,0 +1,7 @@
-+# $Id$
-+#
-+import inc_const as const
-+
-+PJSUA = ["--null-audio --max-calls=1 --id=sip:a@localhost --username=a --realm=* --registrar=$SIPP_URI"]
-+
-+PJSUA_EXPECTS = [[0, "registration success", ""]]
---- /dev/null
-+++ b/tests/pjsua/scripts-sipp/uas-auth-two-algo.xml
-@@ -0,0 +1,83 @@
-+<?xml version="1.0" encoding="ISO-8859-1" ?>
-+<!DOCTYPE scenario SYSTEM "sipp.dtd">
-+
-+<scenario name="Basic UAS responder">
-+ <recv request="REGISTER" crlf="true">
-+ </recv>
-+
-+ <send>
-+ <![CDATA[
-+ SIP/2.0 100 Trying
-+ [last_Via:];received=1.1.1.1;rport=1111
-+ [last_From:]
-+ [last_To:];tag=[call_number]
-+ [last_Call-ID:]
-+ [last_CSeq:]
-+ Content-Length: 0
-+ ]]>
-+ </send>
-+
-+ <send>
-+ <![CDATA[
-+ SIP/2.0 401 Unauthorized
-+ [last_Via:];received=1.1.1.1;rport=1111
-+ [last_From:]
-+ [last_To:];tag=[call_number]
-+ [last_Call-ID:]
-+ [last_CSeq:]
-+ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=SHA-256, qop="auth"
-+ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=MD5, qop="auth"
-+ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=MD2, qop="auth"
-+ Content-Length: 0
-+ ]]>
-+ </send>
-+
-+ <recv request="REGISTER" crlf="true">
-+ <action>
-+ <ereg regexp=".*"
-+ search_in="hdr"
-+ header="Authorization:"
-+ assign_to="have_auth" />
-+ </action>
-+ </recv>
-+
-+ <nop next="resp_okay" test="have_auth" />
-+
-+ <send next="end">
-+ <![CDATA[
-+ SIP/2.0 403 no auth
-+ [last_Via:];received=1.1.1.1;rport=1111
-+ [last_From:]
-+ [last_To:];tag=[call_number]
-+ [last_Call-ID:]
-+ [last_CSeq:]
-+ [last_Contact:]
-+ Content-Length: 0
-+ ]]>
-+ </send>
-+
-+ <label id="resp_okay" />
-+
-+ <send>
-+ <![CDATA[
-+ SIP/2.0 200 OK
-+ [last_Via:];received=1.1.1.1;rport=1111
-+ [last_From:]
-+ [last_To:];tag=[call_number]
-+ [last_Call-ID:]
-+ [last_CSeq:]
-+ [last_Contact:]
-+ Content-Length: 0
-+ ]]>
-+ </send>
-+
-+ <label id="end" />
-+
-+ <!-- definition of the response time repartition table (unit is ms) -->
-+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
-+
-+ <!-- definition of the call length repartition table (unit is ms) -->
-+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
-+
-+</scenario>
-+
--- /dev/null
+commit 8e95490e37938f45d9d812905246036c3185b94f
+Author: Riza Sulistyo <trengginas@users.noreply.github.com>
+Date: Thu Mar 24 12:53:03 2022 +0700
+
+ Add compile time option to allow multiple Authorization header (#3010)
+
+--- a/pjsip/include/pjsip/sip_config.h
++++ b/pjsip/include/pjsip/sip_config.h
+@@ -1280,6 +1280,18 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
+ # define PJSIP_AUTH_CNONCE_USE_DIGITS_ONLY 1
+ #endif
+
++/**
++ * Allow client to send multiple Authorization header when receiving multiple
++ * WWW-Authenticate header fields. If this is disabled, the stack will send
++ * Authorization header field containing credentials that match the
++ * topmost header field.
++ *
++ * Default is 0
++ */
++#ifndef PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER
++# define PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER 0
++#endif
++
+ /*****************************************************************************
+ * SIP Event framework and presence settings.
+ */
+@@ -1458,6 +1470,11 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
+ # define PJSIP_INV_ACCEPT_UNKNOWN_BODY PJ_FALSE
+ #endif
+
++/**
++ * Dump configuration to log with verbosity equal to info(3).
++ */
++PJ_DECL(void) pjsip_dump_config(void);
++
+ PJ_END_DECL
+
+ /**
+--- a/pjsip/src/pjsip/sip_auth_client.c
++++ b/pjsip/src/pjsip/sip_auth_client.c
+@@ -1367,7 +1367,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
+ chal_cnt = 0;
+ auth_cnt = 0;
+ last_auth_err = PJSIP_EAUTHNOAUTH;
+- while (hdr != &rdata->msg_info.msg->hdr && auth_cnt == 0) {
++ while (hdr != &rdata->msg_info.msg->hdr) {
+ pjsip_cached_auth *cached_auth;
+ const pjsip_www_authenticate_hdr *hchal;
+ pjsip_authorization_hdr *hauth;
+@@ -1431,6 +1431,11 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reini
+ /* Process next header. */
+ hdr = hdr->next;
+ auth_cnt++;
++
++#if defined(PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER) && \
++ PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER==0
++ break;
++#endif
+ }
+
+ /* Check if challenge is present */
+--- a/pjsip/src/pjsip/sip_config.c
++++ b/pjsip/src/pjsip/sip_config.c
+@@ -19,6 +19,9 @@
+ */
+
+ #include <pjsip/sip_config.h>
++#include <pj/log.h>
++
++static const char *id = "sip_config.c";
+
+ /* pjsip configuration instance, initialized with default values */
+ pjsip_cfg_t pjsip_sip_cfg_var =
+@@ -65,6 +68,195 @@ pjsip_cfg_t pjsip_sip_cfg_var =
+ }
+ };
+
++PJ_DEF(void) pjsip_dump_config(void)
++{
++ PJ_LOG(3, (id, "Dumping PJSIP configurations:"));
++ PJ_LOG(3, (id, " PJSIP_MAX_DIALOG_COUNT : %d",
++ PJSIP_MAX_DIALOG_COUNT));
++ PJ_LOG(3, (id, " PJSIP_MAX_TRANSPORTS : %d",
++ PJSIP_MAX_TRANSPORTS));
++ PJ_LOG(3, (id, " PJSIP_TPMGR_HTABLE_SIZE : %d",
++ PJSIP_TPMGR_HTABLE_SIZE));
++ PJ_LOG(3, (id, " PJSIP_MAX_URL_SIZE : %d",
++ PJSIP_MAX_URL_SIZE));
++ PJ_LOG(3, (id, " PJSIP_MAX_MODULE : %d",
++ PJSIP_MAX_MODULE));
++ PJ_LOG(3, (id, " PJSIP_MAX_PKT_LEN : %d",
++ PJSIP_MAX_PKT_LEN));
++ PJ_LOG(3, (id, " PJSIP_HANDLE_EVENTS_HAS_SLEEP_ON_ERR : %d",
++ PJSIP_HANDLE_EVENTS_HAS_SLEEP_ON_ERR));
++ PJ_LOG(3, (id, " PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS : %d",
++ PJSIP_ACCEPT_MULTIPLE_SDP_ANSWERS));
++ PJ_LOG(3, (id, " PJSIP_UDP_SIZE_THRESHOLD : %d",
++ PJSIP_UDP_SIZE_THRESHOLD));
++ PJ_LOG(3, (id, " PJSIP_INCLUDE_ALLOW_HDR_IN_DLG : %d",
++ PJSIP_INCLUDE_ALLOW_HDR_IN_DLG));
++ PJ_LOG(3, (id, " PJSIP_SAFE_MODULE : %d",
++ PJSIP_SAFE_MODULE));
++ PJ_LOG(3, (id, " PJSIP_CHECK_VIA_SENT_BY : %d",
++ PJSIP_CHECK_VIA_SENT_BY));
++ PJ_LOG(3, (id, " PJSIP_UNESCAPE_IN_PLACE : %d",
++ PJSIP_UNESCAPE_IN_PLACE));
++ PJ_LOG(3, (id, " PJSIP_MAX_NET_EVENTS : %d",
++ PJSIP_MAX_NET_EVENTS));
++ PJ_LOG(3, (id, " PJSIP_MAX_TIMED_OUT_ENTRIES : %d",
++ PJSIP_MAX_TIMED_OUT_ENTRIES));
++ PJ_LOG(3, (id, " PJSIP_TRANSPORT_IDLE_TIME : %d",
++ PJSIP_TRANSPORT_IDLE_TIME));
++ PJ_LOG(3, (id, " PJSIP_TRANSPORT_SERVER_IDLE_TIME : %d",
++ PJSIP_TRANSPORT_SERVER_IDLE_TIME));
++ PJ_LOG(3, (id, " PJSIP_MAX_TRANSPORT_USAGE : %d",
++ PJSIP_MAX_TRANSPORT_USAGE));
++ PJ_LOG(3, (id, " PJSIP_TCP_TRANSPORT_BACKLOG : %d",
++ PJSIP_TCP_TRANSPORT_BACKLOG));
++ PJ_LOG(3, (id, " PJSIP_TCP_TRANSPORT_REUSEADDR : %d",
++ PJSIP_TCP_TRANSPORT_REUSEADDR));
++ PJ_LOG(3, (id, " PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER : %d",
++ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER));
++ PJ_LOG(3, (id, " PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER : %d",
++ PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER));
++ PJ_LOG(3, (id, " PJSIP_TCP_KEEP_ALIVE_INTERVAL : %d",
++ PJSIP_TCP_KEEP_ALIVE_INTERVAL));
++ PJ_LOG(3, (id, " PJSIP_POOL_INC_TRANSPORT : %d",
++ PJSIP_POOL_INC_TRANSPORT));
++ PJ_LOG(3, (id, " PJSIP_POOL_LEN_TDATA : %d",
++ PJSIP_POOL_LEN_TDATA));
++ PJ_LOG(3, (id, " PJSIP_POOL_INC_TDATA : %d",
++ PJSIP_POOL_INC_TDATA));
++ PJ_LOG(3, (id, " PJSIP_POOL_LEN_UA : %d",
++ PJSIP_POOL_LEN_UA));
++ PJ_LOG(3, (id, " PJSIP_POOL_INC_UA : %d",
++ PJSIP_POOL_INC_UA));
++ PJ_LOG(3, (id, " PJSIP_POOL_EVSUB_LEN : %d",
++ PJSIP_POOL_EVSUB_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_EVSUB_INC : %d",
++ PJSIP_POOL_EVSUB_INC));
++ PJ_LOG(3, (id, " PJSIP_MAX_FORWARDS_VALUE : %d",
++ PJSIP_MAX_FORWARDS_VALUE));
++ PJ_LOG(3, (id, " PJSIP_RFC3261_BRANCH_ID : %s",
++ PJSIP_RFC3261_BRANCH_ID));
++ PJ_LOG(3, (id, " PJSIP_RFC3261_BRANCH_LEN : %d",
++ PJSIP_RFC3261_BRANCH_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_TSX_LAYER_LEN : %d",
++ PJSIP_POOL_TSX_LAYER_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_TSX_LAYER_INC : %d",
++ PJSIP_POOL_TSX_LAYER_INC));
++ PJ_LOG(3, (id, " PJSIP_POOL_TSX_LEN : %d",
++ PJSIP_POOL_TSX_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_TSX_INC : %d",
++ PJSIP_POOL_TSX_INC));
++ PJ_LOG(3, (id, " PJSIP_TSX_1XX_RETRANS_DELAY : %d",
++ PJSIP_TSX_1XX_RETRANS_DELAY));
++ PJ_LOG(3, (id, " PJSIP_TSX_UAS_CONTINUE_ON_TP_ERROR : %d",
++ PJSIP_TSX_UAS_CONTINUE_ON_TP_ERROR));
++ PJ_LOG(3, (id, " PJSIP_MAX_TSX_KEY_LEN : %d",
++ PJSIP_MAX_TSX_KEY_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_LEN_USER_AGENT : %d",
++ PJSIP_POOL_LEN_USER_AGENT));
++ PJ_LOG(3, (id, " PJSIP_POOL_INC_USER_AGENT : %d",
++ PJSIP_POOL_INC_USER_AGENT));
++ PJ_LOG(3, (id, " PJSIP_MAX_BRANCH_LEN : %d",
++ PJSIP_MAX_HNAME_LEN));
++ PJ_LOG(3, (id, " PJSIP_POOL_LEN_DIALOG : %d",
++ PJSIP_POOL_LEN_DIALOG));
++ PJ_LOG(3, (id, " PJSIP_POOL_INC_DIALOG : %d",
++ PJSIP_POOL_INC_DIALOG));
++ PJ_LOG(3, (id, " PJSIP_MAX_HEADER_TYPES : %d",
++ PJSIP_MAX_HEADER_TYPES));
++ PJ_LOG(3, (id, " PJSIP_MAX_URI_TYPES : %d",
++ PJSIP_MAX_URI_TYPES));
++ PJ_LOG(3, (id, " PJSIP_AUTH_HEADER_CACHING : %d",
++ PJSIP_AUTH_HEADER_CACHING));
++ PJ_LOG(3, (id, " PJSIP_AUTH_AUTO_SEND_NEXT : %d",
++ PJSIP_AUTH_AUTO_SEND_NEXT));
++ PJ_LOG(3, (id, " PJSIP_AUTH_QOP_SUPPORT : %d",
++ PJSIP_AUTH_QOP_SUPPORT));
++ PJ_LOG(3, (id, " PJSIP_MAX_STALE_COUNT : %d",
++ PJSIP_MAX_STALE_COUNT));
++ PJ_LOG(3, (id, " PJSIP_HAS_DIGEST_AKA_AUTH : %d",
++ PJSIP_HAS_DIGEST_AKA_AUTH));
++ PJ_LOG(3, (id, " PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH : %d",
++ PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH));
++ PJ_LOG(3, (id, " PJSIP_REGISTER_ALLOW_EXP_REFRESH : %d",
++ PJSIP_REGISTER_ALLOW_EXP_REFRESH));
++ PJ_LOG(3, (id, " PJSIP_AUTH_CACHED_POOL_MAX_SIZE : %d",
++ PJSIP_AUTH_CACHED_POOL_MAX_SIZE));
++ PJ_LOG(3, (id, " PJSIP_AUTH_CNONCE_USE_DIGITS_ONLY : %d",
++ PJSIP_AUTH_CNONCE_USE_DIGITS_ONLY));
++ PJ_LOG(3, (id, " PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER : %d",
++ PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER));
++ PJ_LOG(3, (id, " PJSIP_EVSUB_TIME_UAC_REFRESH : %d",
++ PJSIP_EVSUB_TIME_UAC_REFRESH));
++ PJ_LOG(3, (id, " PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH : %d",
++ PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH));
++ PJ_LOG(3, (id, " PJSIP_EVSUB_TIME_UAC_TERMINATE : %d",
++ PJSIP_EVSUB_TIME_UAC_TERMINATE));
++ PJ_LOG(3, (id, " PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY : %d",
++ PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY));
++ PJ_LOG(3, (id, " PJSIP_PRES_DEFAULT_EXPIRES : %d",
++ PJSIP_PRES_DEFAULT_EXPIRES));
++ PJ_LOG(3, (id, " PJSIP_PRES_BAD_CONTENT_RESPONSE : %d",
++ PJSIP_PRES_BAD_CONTENT_RESPONSE));
++ PJ_LOG(3, (id, " PJSIP_PRES_PIDF_ADD_TIMESTAMP : %d",
++ PJSIP_PRES_PIDF_ADD_TIMESTAMP));
++ PJ_LOG(3, (id, " PJSIP_SESS_TIMER_DEF_SE : %d",
++ PJSIP_SESS_TIMER_DEF_SE));
++ PJ_LOG(3, (id, " PJSIP_SESS_TIMER_RETRY_DELAY : %d",
++ PJSIP_SESS_TIMER_RETRY_DELAY));
++ PJ_LOG(3, (id, " PJSIP_PUBLISHC_QUEUE_REQUEST : %d",
++ PJSIP_PUBLISHC_QUEUE_REQUEST));
++ PJ_LOG(3, (id, " PJSIP_MWI_DEFAULT_EXPIRES : %d",
++ PJSIP_MWI_DEFAULT_EXPIRES));
++ PJ_LOG(3, (id, " PJSIP_HAS_TX_DATA_LIST : %d",
++ PJSIP_HAS_TX_DATA_LIST));
++ PJ_LOG(3, (id, " PJSIP_INV_ACCEPT_UNKNOWN_BODY : %d",
++ PJSIP_INV_ACCEPT_UNKNOWN_BODY));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.allow_port_in_fromto_hdr : %d",
++ pjsip_cfg()->endpt.allow_port_in_fromto_hdr));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.accept_replace_in_early_state : %d",
++ pjsip_cfg()->endpt.accept_replace_in_early_state));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.allow_tx_hash_in_uri : %d",
++ pjsip_cfg()->endpt.allow_tx_hash_in_uri));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.disable_rport : %d",
++ pjsip_cfg()->endpt.disable_rport));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.disable_tcp_switch : %d",
++ pjsip_cfg()->endpt.disable_tcp_switch));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.disable_tls_switch : %d",
++ pjsip_cfg()->endpt.disable_tls_switch));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.follow_early_media_fork : %d",
++ pjsip_cfg()->endpt.follow_early_media_fork));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.req_has_via_alias : %d",
++ pjsip_cfg()->endpt.req_has_via_alias));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.resolve_hostname_to_get_interface:%d",
++ pjsip_cfg()->endpt.resolve_hostname_to_get_interface));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.disable_secure_dlg_check : %d",
++ pjsip_cfg()->endpt.disable_secure_dlg_check));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.use_compact_form : %d",
++ pjsip_cfg()->endpt.use_compact_form));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.accept_multiple_sdp_answers : %d",
++ pjsip_cfg()->endpt.accept_multiple_sdp_answers));
++ PJ_LOG(3, (id, " pjsip_cfg()->endpt.keep_inv_after_tsx_timeout : %d",
++ pjsip_cfg()->endpt.keep_inv_after_tsx_timeout));
++ PJ_LOG(3, (id, " pjsip_cfg()->tsx.max_count : %d",
++ pjsip_cfg()->tsx.max_count));
++ PJ_LOG(3, (id, " pjsip_cfg()->tsx.t1 : %d",
++ pjsip_cfg()->tsx.t1));
++ PJ_LOG(3, (id, " pjsip_cfg()->tsx.t2 : %d",
++ pjsip_cfg()->tsx.t2));
++ PJ_LOG(3, (id, " pjsip_cfg()->tsx.t4 : %d",
++ pjsip_cfg()->tsx.t4));
++ PJ_LOG(3, (id, " pjsip_cfg()->td : %d",
++ pjsip_cfg()->tsx.td));
++ PJ_LOG(3, (id, " pjsip_cfg()->regc.check_contact : %d",
++ pjsip_cfg()->regc.check_contact));
++ PJ_LOG(3, (id, " pjsip_cfg()->regc.add_xuid_param : %d",
++ pjsip_cfg()->regc.add_xuid_param));
++ PJ_LOG(3, (id, " pjsip_cfg()->tcp.keep_alive_interval : %d",
++ pjsip_cfg()->tcp.keep_alive_interval));
++ PJ_LOG(3, (id, " pjsip_cfg()->tls.keep_alive_interval : %d",
++ pjsip_cfg()->tls.keep_alive_interval));
++}
++
+
+ #ifdef PJ_DLL
+ PJ_DEF(pjsip_cfg_t*) pjsip_cfg(void)
+--- a/pjsip/src/pjsua-lib/pjsua_core.c
++++ b/pjsip/src/pjsua-lib/pjsua_core.c
+@@ -3443,8 +3443,10 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail
+ old_decor = pj_log_get_decor();
+ pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
+
+- if (detail)
++ if (detail) {
+ pj_dump_config();
++ pjsip_dump_config();
++ }
+
+ pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
+
+--- a/tests/pjsua/inc_sip.py
++++ b/tests/pjsua/inc_sip.py
+@@ -306,9 +306,11 @@ class RecvfromTransaction:
+ body = None
+ # Pattern to be expected on pjsua when receiving the response
+ expect = ""
++ # Required config
++ pj_config = ""
+
+ def __init__(self, title, resp_code, check_cseq=True,
+- include=[], exclude=[], cmds=[], resp_hdr=[], resp_body=None, expect=""):
++ include=[], exclude=[], cmds=[], resp_hdr=[], resp_body=None, expect="", pj_config=""):
+ self.title = title
+ self.cmds = cmds
+ self.include = include
+@@ -317,6 +319,7 @@ class RecvfromTransaction:
+ self.resp_hdr = resp_hdr
+ self.body = resp_body
+ self.expect = expect
++ self.pj_config=pj_config
+
+
+ class RecvfromCfg:
+@@ -328,15 +331,18 @@ class RecvfromCfg:
+ transaction = None
+ # Use TCP?
+ tcp = False
++ # Required config
++ pj_config = ""
+
+ # Note:
+ # Any "$PORT" string in the pjsua_args will be replaced
+ # by server port
+- def __init__(self, name, pjsua_args, transaction, tcp=False):
++ def __init__(self, name, pjsua_args, transaction, tcp=False, pj_config=""):
+ self.name = name
+ self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
+ self.transaction = transaction
+ self.tcp=tcp
++ self.pj_config=pj_config
+
+
+
+--- a/tests/pjsua/mod_recvfrom.py
++++ b/tests/pjsua/mod_recvfrom.py
+@@ -18,10 +18,20 @@ def test_func(test):
+ local_port=srv_port,
+ tcp=cfg_file.recvfrom_cfg.tcp)
+
++ config = pjsua.get_config(cfg_file.recvfrom_cfg.pj_config)
++ print "Config : " + config
++
+ last_cseq = 0
+ last_method = ""
+ last_call_id = ""
+ for t in cfg_file.recvfrom_cfg.transaction:
++ # Check if transaction requires configuration
++ if t.pj_config != "":
++ r = re.compile(t.pj_config, re.I)
++ if r.search(config) == None:
++ print "Configuration : " + t.pj_config + " not found, skipping"
++ continue
++
+ # Print transaction title
+ if t.title != "":
+ dlg.trace(t.title)
+--- a/tests/pjsua/run.py
++++ b/tests/pjsua/run.py
+@@ -249,6 +249,10 @@ class Expect(threading.Thread):
+ time.sleep(0.01)
+ return None
+
++ def get_config(self, key_config):
++ self.send("dd")
++ line = self.expect(key_config);
++ return line
+
+ def sync_stdout(self):
+ if not self.use_telnet:
+--- a/tests/pjsua/scripts-recvfrom/215_reg_good_multi_ok.py
++++ b/tests/pjsua/scripts-recvfrom/215_reg_good_multi_ok.py
+@@ -14,16 +14,27 @@ req1 = sip.RecvfromTransaction("Initial
+ expect="SIP/2.0 401"
+ )
+
+-req2 = sip.RecvfromTransaction("Registration retry with auth", 200,
++req2 = sip.RecvfromTransaction("Registration retry with auth (not allowed multiple auth)", 200,
+ include=["REGISTER sip",
+- # Must only have 1 Auth hdr since #2887
+ "Authorization:", # [\\s\\S]+Authorization:"
+ "realm=\"python1\"", # "realm=\"python2\"",
+ "username=\"theuser1\"", # "username=\"theuser2\"",
+ "nonce=\"1234\"", # "nonce=\"6789\"",
+ "response="],
+- expect="registration success"
++ expect="registration success",
++ pj_config="PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER.*: 0"
+ )
+
++req3 = sip.RecvfromTransaction("Registration retry with auth (allowed multiple auth)", 200,
++ include=["REGISTER sip",
++ "Authorization:[\\s\\S]+Authorization:", # Must have 2 Auth hdrs
++ "realm=\"python1\"", "realm=\"python2\"",
++ "username=\"theuser1\"", "username=\"theuser2\"",
++ "nonce=\"1234\"", "nonce=\"6789\"",
++ "response="],
++ expect="registration success",
++ pj_config="PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER.*: 1"
++ )
++
+ recvfrom_cfg = sip.RecvfromCfg("Multiple authentication challenges",
+- pjsua, [req1, req2])
++ pjsua, [req1, req2, req3], pj_config="PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER")
+++ /dev/null
-commit f0ff5817d0647bdecd1ec99488db9378e304cf83
-Author: sauwming <ming@teluu.com>
-Date: Mon May 17 09:56:27 2021 +0800
-
- Fix double free of stun session (#2709)
-
---- a/pjnath/include/pjnath/stun_session.h
-+++ b/pjnath/include/pjnath/stun_session.h
-@@ -341,6 +341,7 @@ struct pj_stun_tx_data
- pj_pool_t *pool; /**< Pool. */
- pj_stun_session *sess; /**< The STUN session. */
- pj_stun_msg *msg; /**< The STUN message. */
-+ pj_bool_t is_destroying; /**< Is destroying? */
-
- void *token; /**< The token. */
-
---- a/pjnath/src/pjnath/stun_session.c
-+++ b/pjnath/src/pjnath/stun_session.c
-@@ -167,16 +167,27 @@ static void tdata_on_destroy(void *arg)
- {
- pj_stun_tx_data *tdata = (pj_stun_tx_data*)arg;
-
-+ if (tdata->grp_lock) {
-+ pj_grp_lock_dec_ref(tdata->sess->grp_lock);
-+ }
-+
- pj_pool_safe_release(&tdata->pool);
- }
-
- static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
- {
-- TRACE_((THIS_FILE, "tdata %p destroy request, force=%d, tsx=%p", tdata,
-- force, tdata->client_tsx));
-+ TRACE_((THIS_FILE,
-+ "tdata %p destroy request, force=%d, tsx=%p, destroying=%d",
-+ tdata, force, tdata->client_tsx, tdata->is_destroying));
-+
-+ /* Just return if destroy has been requested before */
-+ if (tdata->is_destroying)
-+ return;
-
- /* STUN session may have been destroyed, except when tdata is cached. */
-
-+ tdata->is_destroying = PJ_TRUE;
-+
- if (tdata->res_timer.id != PJ_FALSE) {
- pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
- &tdata->res_timer, PJ_FALSE);
-@@ -189,7 +200,6 @@ static void destroy_tdata(pj_stun_tx_dat
- pj_stun_client_tsx_set_data(tdata->client_tsx, NULL);
- }
- if (tdata->grp_lock) {
-- pj_grp_lock_dec_ref(tdata->sess->grp_lock);
- pj_grp_lock_dec_ref(tdata->grp_lock);
- } else {
- tdata_on_destroy(tdata);
-@@ -200,11 +210,11 @@ static void destroy_tdata(pj_stun_tx_dat
- /* "Probably" this is to absorb retransmission */
- pj_time_val delay = {0, 300};
- pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
-+ tdata->is_destroying = PJ_FALSE;
-
- } else {
- pj_list_erase(tdata);
- if (tdata->grp_lock) {
-- pj_grp_lock_dec_ref(tdata->sess->grp_lock);
- pj_grp_lock_dec_ref(tdata->grp_lock);
- } else {
- tdata_on_destroy(tdata);
-@@ -238,7 +248,7 @@ static void on_cache_timeout(pj_timer_he
- sess = tdata->sess;
-
- pj_grp_lock_acquire(sess->grp_lock);
-- if (sess->is_destroying) {
-+ if (sess->is_destroying || tdata->is_destroying) {
- pj_grp_lock_release(sess->grp_lock);
- return;
- }
+++ /dev/null
-From bb92c97ea512aa0ef316c9b2335c7d57b84dfc9a Mon Sep 17 00:00:00 2001
-From: Nanang Izzuddin <nanang@teluu.com>
-Date: Wed, 16 Jun 2021 12:12:35 +0700
-Subject: [PATCH 1/2] - Avoid SSL socket parent/listener getting destroyed
- during handshake by increasing parent's reference count. - Add missing SSL
- socket close when the newly accepted SSL socket is discarded in SIP TLS
- transport.
-
----
- pjlib/src/pj/ssl_sock_imp_common.c | 44 +++++++++++++++++++++--------
- pjsip/src/pjsip/sip_transport_tls.c | 23 ++++++++++++++-
- 2 files changed, 55 insertions(+), 12 deletions(-)
-
---- a/pjlib/src/pj/ssl_sock_imp_common.c
-+++ b/pjlib/src/pj/ssl_sock_imp_common.c
-@@ -224,6 +224,8 @@ static pj_bool_t on_handshake_complete(p
-
- /* Accepting */
- if (ssock->is_server) {
-+ pj_bool_t ret = PJ_TRUE;
-+
- if (status != PJ_SUCCESS) {
- /* Handshake failed in accepting, destroy our self silently. */
-
-@@ -241,6 +243,12 @@ static pj_bool_t on_handshake_complete(p
- status);
- }
-
-+ /* Decrement ref count of parent */
-+ if (ssock->parent->param.grp_lock) {
-+ pj_grp_lock_dec_ref(ssock->parent->param.grp_lock);
-+ ssock->parent = NULL;
-+ }
-+
- /* Originally, this is a workaround for ticket #985. However,
- * a race condition may occur in multiple worker threads
- * environment when we are destroying SSL objects while other
-@@ -284,23 +292,29 @@ static pj_bool_t on_handshake_complete(p
-
- return PJ_FALSE;
- }
-+
- /* Notify application the newly accepted SSL socket */
- if (ssock->param.cb.on_accept_complete2) {
-- pj_bool_t ret;
- ret = (*ssock->param.cb.on_accept_complete2)
- (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
- pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr),
- status);
-- if (ret == PJ_FALSE)
-- return PJ_FALSE;
- } else if (ssock->param.cb.on_accept_complete) {
-- pj_bool_t ret;
- ret = (*ssock->param.cb.on_accept_complete)
- (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
- pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
-- if (ret == PJ_FALSE)
-- return PJ_FALSE;
- }
-+
-+ /* Decrement ref count of parent and reset parent (we don't need it
-+ * anymore, right?).
-+ */
-+ if (ssock->parent->param.grp_lock) {
-+ pj_grp_lock_dec_ref(ssock->parent->param.grp_lock);
-+ ssock->parent = NULL;
-+ }
-+
-+ if (ret == PJ_FALSE)
-+ return PJ_FALSE;
- }
-
- /* Connecting */
-@@ -864,9 +878,13 @@ static pj_bool_t asock_on_accept_complet
- if (status != PJ_SUCCESS)
- goto on_return;
-
-+ /* Set parent and add ref count (avoid parent destroy during handshake) */
-+ ssock->parent = ssock_parent;
-+ if (ssock->parent->param.grp_lock)
-+ pj_grp_lock_add_ref(ssock->parent->param.grp_lock);
-+
- /* Update new SSL socket attributes */
- ssock->sock = newsock;
-- ssock->parent = ssock_parent;
- ssock->is_server = PJ_TRUE;
- if (ssock_parent->cert) {
- status = pj_ssl_sock_set_certificate(ssock, ssock->pool,
-@@ -913,16 +931,20 @@ static pj_bool_t asock_on_accept_complet
- ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool,
- ssock->param.async_cnt,
- sizeof(void*));
-- if (!ssock->asock_rbuf)
-- return PJ_ENOMEM;
-+ if (!ssock->asock_rbuf) {
-+ status = PJ_ENOMEM;
-+ goto on_return;
-+ }
-
- for (i = 0; i<ssock->param.async_cnt; ++i) {
-- ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
-+ ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
- ssock->pool,
- ssock->param.read_buffer_size +
- sizeof(read_data_t*));
-- if (!ssock->asock_rbuf[i])
-- return PJ_ENOMEM;
-+ if (!ssock->asock_rbuf[i]) {
-+ status = PJ_ENOMEM;
-+ goto on_return;
-+ }
- }
-
- /* Create active socket */
---- a/pjsip/src/pjsip/sip_transport_tls.c
-+++ b/pjsip/src/pjsip/sip_transport_tls.c
-@@ -1325,9 +1325,26 @@ static pj_bool_t on_accept_complete2(pj_
- PJ_UNUSED_ARG(src_addr_len);
-
- listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock);
-+ if (!listener) {
-+ /* Listener already destroyed, e.g: after TCP accept but before SSL
-+ * handshake is completed.
-+ */
-+ if (new_ssock && accept_status == PJ_SUCCESS) {
-+ /* Close the SSL socket if the accept op is successful */
-+ PJ_LOG(4,(THIS_FILE,
-+ "Incoming TLS connection from %s (sock=%d) is discarded "
-+ "because listener is already destroyed",
-+ pj_sockaddr_print(src_addr, addr, sizeof(addr), 3),
-+ new_ssock));
-+
-+ pj_ssl_sock_close(new_ssock);
-+ }
-+
-+ return PJ_FALSE;
-+ }
-
- if (accept_status != PJ_SUCCESS) {
-- if (listener && listener->tls_setting.on_accept_fail_cb) {
-+ if (listener->tls_setting.on_accept_fail_cb) {
- pjsip_tls_on_accept_fail_param param;
- pj_ssl_sock_info ssi;
-
-@@ -1350,6 +1367,8 @@ static pj_bool_t on_accept_complete2(pj_
- PJ_ASSERT_RETURN(new_ssock, PJ_TRUE);
-
- if (!listener->is_registered) {
-+ pj_ssl_sock_close(new_ssock);
-+
- if (listener->tls_setting.on_accept_fail_cb) {
- pjsip_tls_on_accept_fail_param param;
- pj_bzero(¶m, sizeof(param));
-@@ -1401,6 +1420,8 @@ static pj_bool_t on_accept_complete2(pj_
- ssl_info.grp_lock, &tls);
-
- if (status != PJ_SUCCESS) {
-+ pj_ssl_sock_close(new_ssock);
-+
- if (listener->tls_setting.on_accept_fail_cb) {
- pjsip_tls_on_accept_fail_param param;
- pj_bzero(¶m, sizeof(param));
+++ /dev/null
-From 68c69f516f95df1faa42e5647e9ce7cfdc41ac38 Mon Sep 17 00:00:00 2001
-From: Nanang Izzuddin <nanang@teluu.com>
-Date: Wed, 16 Jun 2021 12:15:29 +0700
-Subject: [PATCH 2/2] - Fix silly mistake: accepted active socket created
- without group lock in SSL socket. - Replace assertion with normal validation
- check of SSL socket instance in OpenSSL verification callback (verify_cb())
- to avoid crash, e.g: if somehow race condition with SSL socket destroy
- happens or OpenSSL application data index somehow gets corrupted.
-
----
- pjlib/src/pj/ssl_sock_imp_common.c | 3 +-
- pjlib/src/pj/ssl_sock_ossl.c | 45 +++++++++++++++++++++++++-----
- 2 files changed, 40 insertions(+), 8 deletions(-)
-
---- a/pjlib/src/pj/ssl_sock_imp_common.c
-+++ b/pjlib/src/pj/ssl_sock_imp_common.c
-@@ -949,6 +949,7 @@ static pj_bool_t asock_on_accept_complet
-
- /* Create active socket */
- pj_activesock_cfg_default(&asock_cfg);
-+ asock_cfg.grp_lock = ssock->param.grp_lock;
- asock_cfg.async_cnt = ssock->param.async_cnt;
- asock_cfg.concurrency = ssock->param.concurrency;
- asock_cfg.whole_data = PJ_TRUE;
-@@ -964,7 +965,7 @@ static pj_bool_t asock_on_accept_complet
- goto on_return;
-
- pj_grp_lock_add_ref(glock);
-- asock_cfg.grp_lock = ssock->param.grp_lock = glock;
-+ ssock->param.grp_lock = glock;
- pj_grp_lock_add_handler(ssock->param.grp_lock, ssock->pool, ssock,
- ssl_on_destroy);
- }
---- a/pjlib/src/pj/ssl_sock_ossl.c
-+++ b/pjlib/src/pj/ssl_sock_ossl.c
-@@ -327,7 +327,8 @@ static pj_status_t STATUS_FROM_SSL_ERR(c
- ERROR_LOG("STATUS_FROM_SSL_ERR", err, ssock);
- }
-
-- ssock->last_err = err;
-+ if (ssock)
-+ ssock->last_err = err;
- return GET_STATUS_FROM_SSL_ERR(err);
- }
-
-@@ -344,7 +345,8 @@ static pj_status_t STATUS_FROM_SSL_ERR2(
- /* Dig for more from OpenSSL error queue */
- SSLLogErrors(action, ret, err, len, ssock);
-
-- ssock->last_err = ssl_err;
-+ if (ssock)
-+ ssock->last_err = ssl_err;
- return GET_STATUS_FROM_SSL_ERR(ssl_err);
- }
-
-@@ -587,6 +589,13 @@ static pj_status_t init_openssl(void)
-
- /* Create OpenSSL application data index for SSL socket */
- sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL);
-+ if (sslsock_idx == -1) {
-+ status = STATUS_FROM_SSL_ERR2("Init", NULL, -1, ERR_get_error(), 0);
-+ PJ_LOG(1,(THIS_FILE,
-+ "Fatal error: failed to get application data index for "
-+ "SSL socket"));
-+ return status;
-+ }
-
- return status;
- }
-@@ -614,21 +623,36 @@ static int password_cb(char *buf, int nu
- }
-
-
--/* SSL password callback. */
-+/* SSL certificate verification result callback.
-+ * Note that this callback seems to be always called from library worker
-+ * thread, e.g: active socket on_read_complete callback, which should have
-+ * already been equipped with race condition avoidance mechanism (should not
-+ * be destroyed while callback is being invoked).
-+ */
- static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
- {
-- pj_ssl_sock_t *ssock;
-- SSL *ossl_ssl;
-+ pj_ssl_sock_t *ssock = NULL;
-+ SSL *ossl_ssl = NULL;
- int err;
-
- /* Get SSL instance */
- ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
-- pj_assert(ossl_ssl);
-+ if (!ossl_ssl) {
-+ PJ_LOG(1,(THIS_FILE,
-+ "SSL verification callback failed to get SSL instance"));
-+ goto on_return;
-+ }
-
- /* Get SSL socket instance */
- ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx);
-- pj_assert(ssock);
-+ if (!ssock) {
-+ /* SSL socket may have been destroyed */
-+ PJ_LOG(1,(THIS_FILE,
-+ "SSL verification callback failed to get SSL socket "
-+ "instance (sslsock_idx=%d).", sslsock_idx));
-+ goto on_return;
-+ }
-
- /* Store verification status */
- err = X509_STORE_CTX_get_error(x509_ctx);
-@@ -706,6 +730,7 @@ static int verify_cb(int preverify_ok, X
- if (PJ_FALSE == ssock->param.verify_peer)
- preverify_ok = 1;
-
-+on_return:
- return preverify_ok;
- }
-
-@@ -1213,6 +1238,12 @@ static void ssl_destroy(pj_ssl_sock_t *s
- static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
- {
- ossl_sock_t *ossock = (ossl_sock_t *)ssock;
-+
-+ /* Detach from SSL instance */
-+ if (ossock->ossl_ssl) {
-+ SSL_set_ex_data(ossock->ossl_ssl, sslsock_idx, NULL);
-+ }
-+
- /**
- * Avoid calling SSL_shutdown() if handshake wasn't completed.
- * OpenSSL 1.0.2f complains if SSL_shutdown() is called during an
+++ /dev/null
-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;
- }
+++ /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
-+ },
-
- };
-
+++ /dev/null
-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(¶m->name, "p2"))
- return -956;
-- if (pj_strcmp2(¶m->value, "ab:cd"))
-+ if (pj_strcmp2(¶m->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;
+++ /dev/null
-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)
- {
+++ /dev/null
-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);
+++ /dev/null
-From 15663e3f37091069b8c98a7fce680dc04bc8e865 Mon Sep 17 00:00:00 2001
-From: sauwming <ming@teluu.com>
-Date: Tue, 10 Aug 2021 11:53:25 +0800
-Subject: [PATCH] Merge pull request from GHSA-2qpg-f6wf-w984
-
----
- pjnath/src/pjnath/stun_msg.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/pjnath/src/pjnath/stun_msg.c
-+++ b/pjnath/src/pjnath/stun_msg.c
-@@ -1763,6 +1763,9 @@ static pj_status_t decode_errcode_attr(p
- /* Get pointer to the string in the message */
- value.ptr = ((char*)buf + ATTR_HDR_LEN + 4);
- value.slen = attr->hdr.length - 4;
-+ /* Make sure the length is never negative */
-+ if (value.slen < 0)
-+ value.slen = 0;
-
- /* Copy the string to the attribute */
- pj_strdup(pool, &attr->reason, &value);
+++ /dev/null
-From db3235953baa56d2fb0e276ca510fefca751643f Mon Sep 17 00:00:00 2001
-From: Nanang Izzuddin <nanang@teluu.com>
-Date: Mon, 21 Feb 2022 06:24:52 +0700
-Subject: [PATCH] Merge pull request from GHSA-ffff-m5fm-qm62
-
-* Update pjsip_ua_unregister_dlg():
-- update the hash key if the dialog being unregistered is used as hash key.
-- add an assertion check to make sure that the dlg_set to be removed is valid (can be found in the hash table).
-
-* Change hash key string comparison method.
----
- pjsip/src/pjsip/sip_ua_layer.c | 48 +++++++++++++++++++++++++++++-----
- 1 file changed, 42 insertions(+), 6 deletions(-)
-
---- a/pjsip/src/pjsip/sip_ua_layer.c
-+++ b/pjsip/src/pjsip/sip_ua_layer.c
-@@ -65,6 +65,9 @@ struct dlg_set
- /* This is the buffer to store this entry in the hash table. */
- pj_hash_entry_buf ht_entry;
-
-+ /* Entry key in the hash table */
-+ pj_str_t ht_key;
-+
- /* List of dialog in this dialog set. */
- struct dlg_set_head dlg_list;
- };
-@@ -321,6 +324,7 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dl
- * Create the dialog set and add this dialog to it.
- */
- dlg_set = alloc_dlgset_node();
-+ dlg_set->ht_key = dlg->local.info->tag;
- pj_list_init(&dlg_set->dlg_list);
- pj_list_push_back(&dlg_set->dlg_list, dlg);
-
-@@ -328,8 +332,8 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dl
-
- /* Register the dialog set in the hash table. */
- pj_hash_set_np_lower(mod_ua.dlg_table,
-- dlg->local.info->tag.ptr,
-- (unsigned)dlg->local.info->tag.slen,
-+ dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
- dlg->local.tag_hval, dlg_set->ht_entry,
- dlg_set);
- }
-@@ -339,14 +343,15 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dl
- struct dlg_set *dlg_set;
-
- dlg_set = alloc_dlgset_node();
-+ dlg_set->ht_key = dlg->local.info->tag;
- pj_list_init(&dlg_set->dlg_list);
- pj_list_push_back(&dlg_set->dlg_list, dlg);
-
- dlg->dlg_set = dlg_set;
-
- pj_hash_set_np_lower(mod_ua.dlg_table,
-- dlg->local.info->tag.ptr,
-- (unsigned)dlg->local.info->tag.slen,
-+ dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
- dlg->local.tag_hval, dlg_set->ht_entry, dlg_set);
- }
-
-@@ -391,12 +396,43 @@ PJ_DEF(pj_status_t) pjsip_ua_unregister_
-
- /* If dialog list is empty, remove the dialog set from the hash table. */
- if (pj_list_empty(&dlg_set->dlg_list)) {
-- pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr,
-- (unsigned)dlg->local.info->tag.slen,
-+
-+ /* Verify that the dialog set is valid */
-+ pj_assert(pj_hash_get_lower(mod_ua.dlg_table, dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
-+ &dlg->local.tag_hval) == dlg_set);
-+
-+ pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
- dlg->local.tag_hval, NULL);
-
- /* Return dlg_set to free nodes. */
- pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set);
-+ } else {
-+ /* If the just unregistered dialog is being used as hash key,
-+ * reset the dlg_set entry with a new key (i.e: from the first dialog
-+ * in dlg_set).
-+ */
-+ if (dlg_set->ht_key.ptr == dlg->local.info->tag.ptr &&
-+ dlg_set->ht_key.slen == dlg->local.info->tag.slen)
-+ {
-+ pjsip_dialog* key_dlg = dlg_set->dlg_list.next;
-+
-+ /* Verify that the old & new keys share the hash value */
-+ pj_assert(key_dlg->local.tag_hval == dlg->local.tag_hval);
-+
-+ pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
-+ dlg->local.tag_hval, NULL);
-+
-+ dlg_set->ht_key = key_dlg->local.info->tag;
-+
-+ pj_hash_set_np_lower(mod_ua.dlg_table,
-+ dlg_set->ht_key.ptr,
-+ (unsigned)dlg_set->ht_key.slen,
-+ key_dlg->local.tag_hval, dlg_set->ht_entry,
-+ dlg_set);
-+ }
- }
-
- /* Unlock user agent. */
+++ /dev/null
-From 077b465c33f0aec05a49cd2ca456f9a1b112e896 Mon Sep 17 00:00:00 2001
-From: sauwming <ming@teluu.com>
-Date: Wed, 26 Jan 2022 13:28:57 +0800
-Subject: [PATCH] Merge pull request from GHSA-7fw8-54cv-r7pm
-
----
- pjlib-util/src/pjlib-util/scanner.c | 13 +++++++++----
- 1 file changed, 9 insertions(+), 4 deletions(-)
-
---- a/pjlib-util/src/pjlib-util/scanner.c
-+++ b/pjlib-util/src/pjlib-util/scanner.c
-@@ -444,16 +444,21 @@ PJ_DEF(void) pj_scan_get_n( pj_scanner *
-
- PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
- {
-- int chr = *scanner->curptr;
-+ register char *s = scanner->curptr;
-+ int chr;
-
-- if (!chr) {
-+ if (s >= scanner->end || !*s) {
- pj_scan_syntax_err(scanner);
- return 0;
- }
-
-- ++scanner->curptr;
-+ chr = *s;
-
-- if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) {
-+ ++s;
-+ scanner->curptr = s;
-+ if (PJ_SCAN_CHECK_EOF(s) && PJ_SCAN_IS_PROBABLY_SPACE(*s) &&
-+ scanner->skip_ws)
-+ {
- pj_scan_skip_whitespace(scanner);
- }
- return chr;