summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--Makefile7
-rw-r--r--Makefile.moddir_rules12
-rw-r--r--apps/app_dial.c9
-rwxr-xr-xbuild_tools/download_externals180
-rwxr-xr-xbuild_tools/list_valid_installed_externals55
-rwxr-xr-xbuild_tools/make_version4
-rw-r--r--build_tools/menuselect-deps.in2
-rw-r--r--codecs/codecs.xml25
-rwxr-xr-xconfigure209
-rw-r--r--configure.ac18
-rw-r--r--include/asterisk/autoconfig.h.in3
-rw-r--r--makeopts.in2
-rw-r--r--res/res.xml6
-rw-r--r--res/res_pjsip_session.c71
-rw-r--r--third-party/pjproject/configure.m41
-rw-r--r--third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch212
17 files changed, 810 insertions, 17 deletions
diff --git a/CHANGES b/CHANGES
index 27643f28f..d36da1f99 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,17 @@
--- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
------------------------------------------------------------------------------
+Build System
+------------------
+ * The res_digium_phone, codec_g729a, codec_silk, codec_siren7 and
+ codec_siren14 binary modules hosted at downloads.digium.com can now be
+ automatically downloaded and installed during the Asterisk install
+ process. If selected in menuselect, when 'make install' is run, the
+ script will check the downloads site for a new version and download
+ and install it if needed. The '--with-externals-cache' option to
+ ./configure can be used to specify a location to cache the latest
+ tarballs so they don't have to be re-downloaded for every install.
+
app_voicemail
------------------
* Added "tps_queue_high" and "tps_queue_low" options.
diff --git a/Makefile b/Makefile
index 6385fac9f..938ecfe19 100644
--- a/Makefile
+++ b/Makefile
@@ -619,9 +619,10 @@ $(SUBDIRS_INSTALL):
NEWMODS:=$(foreach d,$(MOD_SUBDIRS),$(notdir $(wildcard $(d)/*.so)))
OLDMODS=$(filter-out $(NEWMODS) $(notdir $(DESTDIR)$(ASTMODDIR)),$(notdir $(wildcard $(DESTDIR)$(ASTMODDIR)/*.so)))
+BADMODS=$(strip $(filter-out $(shell ./build_tools/list_valid_installed_externals),$(OLDMODS)))
oldmodcheck:
- @if [ -n "$(OLDMODS)" ]; then \
+ @if [ -n "$(BADMODS)" ]; then \
echo " WARNING WARNING WARNING" ;\
echo "" ;\
echo " Your Asterisk modules directory, located at" ;\
@@ -631,7 +632,7 @@ oldmodcheck:
echo " modules are compatible with this version before" ;\
echo " attempting to run Asterisk." ;\
echo "" ;\
- for f in $(OLDMODS); do \
+ for f in $(BADMODS); do \
echo " $$f" ;\
done ;\
echo "" ;\
@@ -981,7 +982,7 @@ menuselect/nmenuselect: menuselect/makeopts .lastclean
menuselect/makeopts: makeopts .lastclean
+$(MAKE_MENUSELECT) makeopts
-menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml utils/utils.xml agi/agi.xml configure makeopts
+menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc) $(wildcard $(dir)/*.xml)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml utils/utils.xml agi/agi.xml configure makeopts
@echo "Generating input for menuselect ..."
@echo "<?xml version=\"1.0\"?>" > $@
@echo >> $@
diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules
index d508606db..22849a2fa 100644
--- a/Makefile.moddir_rules
+++ b/Makefile.moddir_rules
@@ -124,6 +124,18 @@ clean::
install:: all
@echo "Installing modules from `basename $(CURDIR)`..."
@for x in $(LOADABLE_MODS:%=%.so); do $(INSTALL) -m 755 $$x "$(DESTDIR)$(ASTMODDIR)" ; done
+ifneq ($(findstring :,$(XMLSTARLET)$(BASH)),:)
+ @if [ -f .moduleinfo ] ; then \
+ declare -A DISABLED_MODS ;\
+ for x in $(MENUSELECT_$(MENUSELECT_CATEGORY)) ; do DISABLED_MODS[$${x}]=1 ; done ;\
+ EXTERNAL_MODS=$$(xmlstarlet sel -t -m "/category/member[support_level = 'external']" -v "@name" -n .moduleinfo) ;\
+ for x in $${EXTERNAL_MODS} ; do \
+ if [ -z "$${DISABLED_MODS[$${x}]}" ] ; then \
+ $(ASTTOPDIR)/build_tools/download_externals $${x} ;\
+ fi ;\
+ done ;\
+ fi
+endif
uninstall::
diff --git a/apps/app_dial.c b/apps/app_dial.c
index c2484a2f3..7b7c70201 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1914,9 +1914,10 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
break;
case '5':
- /* XXX should we set status to DENY ? */
- if (ast_test_flag64(opts, OPT_PRIVACY))
+ if (ast_test_flag64(opts, OPT_PRIVACY)) {
+ ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
break;
+ }
/* if not privacy, then 5 is the same as "default" case */
default: /* bad input or -1 if failure to start autoservice */
/* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do? */
@@ -1941,8 +1942,6 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
}
return 0; /* the good exit path */
} else {
- /* hang up on the callee -- he didn't want to talk anyway! */
- ast_autoservice_chan_hangup_peer(chan, peer);
return -1;
}
}
@@ -2799,6 +2798,8 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
if ( (ast_test_flag64(&opts, OPT_PRIVACY) || ast_test_flag64(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
if (do_privacy(chan, peer, &opts, opt_args, &pa)) {
ast_channel_publish_dial(chan, peer, NULL, pa.status);
+ /* hang up on the callee -- he didn't want to talk anyway! */
+ ast_autoservice_chan_hangup_peer(chan, peer);
res = 0;
goto out;
}
diff --git a/build_tools/download_externals b/build_tools/download_externals
new file mode 100755
index 000000000..8d5872df7
--- /dev/null
+++ b/build_tools/download_externals
@@ -0,0 +1,180 @@
+#!/usr/bin/env bash
+
+if [[ ( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} > 1 ) || ${BASH_VERSINFO[0]} > 4 ]] ; then
+ shopt -s compat41
+fi
+set -e
+
+ASTTOPDIR=${ASTTOPDIR:-.}
+
+module_name=$1
+
+if [[ -z ${module_name} ]] ; then
+ echo "You must supply a module name."
+ exit 64
+fi
+
+tmpdir=$(mktemp -d)
+if [[ -z "${tmpdir}" ]] ; then
+ echo "${module_name}: Unable to create temporary directory."
+ exit 1
+fi
+trap "rm -rf ${tmpdir}" EXIT
+
+sed -r -e "s/^([^ =]+)\s*=\s*(.*)$/\1=\"\2\"/g" ${ASTTOPDIR}/makeopts >${tmpdir}/makeopts
+source ${tmpdir}/makeopts
+if [[ -z "${ASTMODDIR}" ]] ; then
+ echo "${module_name}: Unable to parse ${ASTTOPDIR}/makeopts."
+ exit 1
+fi
+
+XMLSTARLET=${XMLSTARLET:-xmlstarlet}
+if [[ "${XMLSTARLET}" = ":" ]] ; then
+ echo "${module_name}: The externals downloader requires xmlstarlet to be installed."
+ exit 1
+fi
+
+cache_dir="${EXTERNALS_CACHE_DIR}"
+if [[ -z ${cache_dir} ]] ; then
+ cache_dir=${tmpdir}
+fi
+
+version=$(${ASTTOPDIR}/build_tools/make_version ${ASTTOPDIR})
+if [[ ! ${version} =~ ^(GIT-)?([^.-]+)[.-].* ]] ; then
+ echo "${module_name}: Couldn't parse version ${version}"
+ exit 1
+fi
+major_version=${BASH_REMATCH[2]}
+
+if [[ "${major_version}" == "master" ]] ; then
+ echo "${module_name}: External module downloading is not available in the 'master' git branch. Please disable in menuselect and download manually."
+ exit 1
+fi
+
+major_version=${major_version}.0
+
+if [[ "${HOST_CPU}" = "x86_64" ]] ; then
+ host_bits=64
+else
+ host_bits=32
+fi
+
+remote_url=$(${XMLSTARLET} sel -t -v "/menu/category/member[@name = '${module_name}']/member_data/downloader/@remote_url" ${ASTTOPDIR}/menuselect-tree || :)
+if [[ -n "${remote_url}" ]] ; then
+ remote_url="${remote_url}/asterisk-${major_version}/x86-${host_bits}"
+else
+ directory_name=$(${XMLSTARLET} sel -t -v "/menu/category/member[@name = '${module_name}']/member_data/downloader/@directory_name" ${ASTTOPDIR}/menuselect-tree || :)
+ remote_url="http://downloads.digium.com/pub/telephony/${directory_name:-${module_name}}/asterisk-${major_version}/x86-${host_bits}"
+fi
+
+version_convert() {
+ local v=${1##*_}
+ if [[ ${v} =~ ([0-9]+)[.]([0-9]+)[.]([0-9]+) ]] ; then
+ v=$(( ${BASH_REMATCH[1]}<<18 | ${BASH_REMATCH[2]}<<9 | ${BASH_REMATCH[3]} ))
+ fi
+ echo ${v}
+}
+
+${WGET} -q -O ${tmpdir}/manifest.xml ${remote_url}/manifest.xml || {
+ echo "${module_name}: Unable to fetch ${remote_url}/manifest.xml"
+ exit 1
+}
+
+rpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${tmpdir}/manifest.xml)
+rpvi=$(version_convert ${rpv})
+echo "${module_name}: Remote package version ${rpv} (${rpvi})"
+
+module_dir=${module_name}-${rpv}-x86_${host_bits}
+tarball=${module_dir}.tar.gz
+export need_install=0
+
+if [[ -f ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml ]] ; then
+ package_arch=$(${XMLSTARLET} sel -t -v "/package/@arch" ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml)
+ ipv=$(${XMLSTARLET} sel -t -v "/package/@version" ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml)
+ ipvi=$(version_convert ${ipv})
+ ip_major=${ipv%_*}
+ echo "${module_name}: Installed package version ${ipv} (${ipvi})"
+ if [[ "${ip_major}" != "${major_version}" || "${package_arch}" != "x86_${host_bits}" ]] ; then
+ echo "${module_name}: The installed package is not for this version of Asterisk. Reinstalling."
+ need_install=1
+ elif [[ ${rpvi} > ${ipvi} ]] ; then
+ echo "${module_name}: A newer package is available"
+ need_install=1
+ else
+ sums=$(${XMLSTARLET} sel -t -m "//file" -v "@md5sum" -n ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml)
+ for sum in ${sums} ; do
+ install_path=$(${XMLSTARLET} sel -t -v "//file[@md5sum = '${sum}']/@install_path" ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml )
+ f=${DESTDIR}$(eval echo ${install_path})
+ if [[ ! -f ${f} ]] ; then
+ echo Not found: ${f}
+ need_install=1
+ else
+ cs=$(md5sum ${f} | cut -b1-32)
+ if [[ "${cs}" != "${sum}" ]] ; then
+ echo Checksum mismatch: ${f}
+ need_install=1
+ fi
+ fi
+ done
+ fi
+else
+ need_install=1
+fi
+
+if [[ ${need_install} == 1 ]] ; then
+ if [[ ( -n "${ipvi}" ) && ${ipvi} > ${rpvi} ]] ; then
+ echo "${module_name}: Installed package is newer than that available for download."
+ exit 0
+ fi
+else
+ echo "${module_name} is up to date."
+ exit 0;
+fi
+
+need_download=1
+if [[ -f ${cache_dir}/${module_name}.manifest.xml ]] ; then
+ cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${module_name}.manifest.xml)
+ cpvi=$(version_convert ${cpv})
+ echo "${module_name}: Cached package version ${cpv} (${cpvi})"
+ if [[ ${cpvi} == ${rpvi} && ( -f ${cache_dir}/${tarball} ) ]] ; then
+ echo "${module_name}: Cached version is available."
+ need_download=0
+ fi
+fi
+
+if [[ ${need_download} = 1 ]] ; then
+ echo "${module_name}: Downloading ${remote_url}/${tarball}"
+ ${WGET} -q -O ${cache_dir}/${tarball} ${remote_url}/${tarball} || {
+ echo "${module_name}: Unable to fetch ${remote_url}/${tarball}"
+ exit 1
+ }
+ cp ${tmpdir}/manifest.xml ${cache_dir}/${module_name}.manifest.xml
+fi
+
+tar -xzf ${cache_dir}/${tarball} -C ${cache_dir}
+trap "rm -rf ${cache_dir}/${module_dir} ; rm -rf ${tmpdir}" EXIT
+
+echo "${module_name}: Installing."
+
+if [[ $EUID == 0 ]] ; then
+ install_params="--group=0 --owner=0"
+fi
+
+names=$(${XMLSTARLET} sel -t -m "//file" -v "@name" -n ${cache_dir}/${module_dir}/manifest.xml)
+for name in ${names} ; do
+ source_path=${cache_dir}/${module_dir}/${name}
+ install_path=$(${XMLSTARLET} sel -t -v "//file[@name = '${name}']/@install_path" ${cache_dir}/${module_dir}/manifest.xml)
+ install_path=${DESTDIR}$(eval echo ${install_path})
+ executable=$(${XMLSTARLET} sel -t -v "//file[@name = '${name}']/@executable" ${cache_dir}/${module_dir}/manifest.xml || :)
+ if [[ "${executable}" = "yes" ]] ; then
+ mode=0755
+ else
+ mode=0644
+ fi
+
+ ${INSTALL} -Dp ${install_params} --mode=${mode} ${source_path} ${install_path}
+
+done
+${INSTALL} -Dp ${install_params} --mode=0644 ${cache_dir}/${module_dir}/manifest.xml ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml
+
+echo "${module_name}: Installed."
diff --git a/build_tools/list_valid_installed_externals b/build_tools/list_valid_installed_externals
new file mode 100755
index 000000000..12aff3f95
--- /dev/null
+++ b/build_tools/list_valid_installed_externals
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+if [[ ( ${BASH_VERSINFO[0]} == 4 && ${BASH_VERSINFO[1]} > 1 ) || ${BASH_VERSINFO[0]} > 4 ]] ; then
+ shopt -s compat41
+fi
+set -e
+
+ASTTOPDIR=${ASTTOPDIR:-.}
+
+tmpdir=$(mktemp -d)
+if [[ -z "${tmpdir}" ]] ; then
+ echo "${module_name}: Unable to create temporary directory."
+ exit 1
+fi
+trap "rm -rf ${tmpdir}" EXIT
+
+sed -r -e "s/^([^ =]+)\s*=\s*(.*)$/\1=\"\2\"/g" ${ASTTOPDIR}/makeopts >${tmpdir}/makeopts
+source ${tmpdir}/makeopts
+if [[ -z "${ASTMODDIR}" ]] ; then
+ echo "${module_name}: Unable to parse ${ASTTOPDIR}/makeopts."
+ exit 1
+fi
+
+XMLSTARLET=${XMLSTARLET:-xmlstarlet}
+if [[ "${XMLSTARLET}" = ":" ]] ; then
+ echo "${module_name}: The externals downloader requires xmlstarlet to be installed."
+ exit 1
+fi
+
+version=$(${ASTTOPDIR}/build_tools/make_version ${ASTTOPDIR})
+if [[ ! ${version} =~ ^(GIT-)?([^.-]+)[.-].* ]] ; then
+ echo "${module_name}: Couldn't parse version ${version}"
+ exit 1
+fi
+major_version=${BASH_REMATCH[2]}.0
+
+if [[ "${HOST_CPU}" = "x86_64" ]] ; then
+ host_bits=64
+else
+ host_bits=32
+fi
+
+names=""
+for manifest in ${DESTDIR}${ASTMODDIR}/*.manifest.xml ; do
+ if [ ! -f "$manifest" ] ; then
+ break
+ fi
+ package_version=$(${XMLSTARLET} sel -t -v "/package/@version" ${manifest})
+ package_major_version=${package_version%_*}
+ package_arch=$(${XMLSTARLET} sel -t -v "/package/@arch" ${manifest})
+ if [[ "$package_major_version" = "$major_version" && "${package_arch}" = "x86_${host_bits}" ]] ; then
+ names+=$(${XMLSTARLET} sel -t -m "//file[@executable = 'yes']" -v "concat(@name, ' ')" ${manifest})
+ fi
+done
+echo $names
diff --git a/build_tools/make_version b/build_tools/make_version
index 99d5dee16..a89d273d2 100755
--- a/build_tools/make_version
+++ b/build_tools/make_version
@@ -89,11 +89,13 @@ elif [ -d ${1}/.git ]; then
if [ -z ${GIT} ]; then
GIT="git"
fi
-
+
if ! command -v ${GIT} >/dev/null 2>&1; then
echo "UNKNOWN__and_probably_unsupported"
exit 1
fi
+ cd ${1}
+
# If the first log commit messages indicates that this is checked into
# subversion, we'll just use the SVN- form of the revision.
MODIFIED=""
diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in
index e5413f1b5..b2f73bc53 100644
--- a/build_tools/menuselect-deps.in
+++ b/build_tools/menuselect-deps.in
@@ -30,6 +30,8 @@ KQUEUE=@PBX_KQUEUE@
LDAP=@PBX_LDAP@
LIBEDIT=@PBX_LIBEDIT@
LIBXML2=@PBX_LIBXML2@
+XMLSTARLET=@PBX_XMLSTARLET@
+BASH=@PBX_BASH@
LTDL=@PBX_LTDL@
LUA=@PBX_LUA@
MISDN=@PBX_MISDN@
diff --git a/codecs/codecs.xml b/codecs/codecs.xml
new file mode 100644
index 000000000..ac3c6e638
--- /dev/null
+++ b/codecs/codecs.xml
@@ -0,0 +1,25 @@
+<member name="codec_silk" displayname="Download the SILK codec from Digium. See http://downloads.digium.com/pub/telephony/codec_silk/README.">
+ <support_level>external</support_level>
+ <depend>xmlstarlet</depend>
+ <depend>bash</depend>
+ <defaultenabled>no</defaultenabled>
+</member>
+<member name="codec_siren7" displayname="Download the Siren7 codec from Digium. See http://downloads.digium.com/pub/telephony/codec_siren7/README.">
+ <support_level>external</support_level>
+ <depend>xmlstarlet</depend>
+ <depend>bash</depend>
+ <defaultenabled>no</defaultenabled>
+</member>
+<member name="codec_siren14" displayname="Download the Siren14 codec from Digium. See http://downloads.digium.com/pub/telephony/codec_siren14/README.">
+ <support_level>external</support_level>
+ <depend>xmlstarlet</depend>
+ <depend>bash</depend>
+ <defaultenabled>no</defaultenabled>
+</member>
+<member name="codec_g729a" displayname="Download the g729a codec from Digium. A license must be purchased for this codec. See http://downloads.digium.com/pub/telephony/codec_g729/README.">
+ <support_level>external</support_level>
+ <depend>xmlstarlet</depend>
+ <depend>bash</depend>
+ <defaultenabled>no</defaultenabled>
+ <member_data><downloader directory_name="codec_g729"/></member_data>
+</member>
diff --git a/configure b/configure
index 7f6b5dddf..e3c537a16 100755
--- a/configure
+++ b/configure
@@ -802,6 +802,7 @@ PBX_SPANDSP
SPANDSP_DIR
SPANDSP_INCLUDE
SPANDSP_LIB
+EXTERNALS_CACHE_DIR
SOUNDS_CACHE_DIR
PBX_SDL_IMAGE
SDL_IMAGE_DIR
@@ -915,6 +916,10 @@ PBX_POPT
POPT_DIR
POPT_INCLUDE
POPT_LIB
+PBX_PJSIP_INV_SESSION_REF
+PJSIP_INV_SESSION_REF_DIR
+PJSIP_INV_SESSION_REF_INCLUDE
+PJSIP_INV_SESSION_REF_LIB
PBX_PJSIP_EVSUB_GRP_LOCK
PJSIP_EVSUB_GRP_LOCK_DIR
PJSIP_EVSUB_GRP_LOCK_INCLUDE
@@ -1178,6 +1183,8 @@ PTHREAD_CC
ax_pthread_config
MD5
SOXMIX
+PBX_BASH
+PBX_XMLSTARLET
PBX_FLEX
PBX_BISON
OPENSSL
@@ -1187,6 +1194,7 @@ DOWNLOAD
FETCH
ALEMBIC
GIT
+BASH
XMLSTARLET
XMLLINT
KPATHSEA
@@ -1373,6 +1381,7 @@ with_resample
with_sdl
with_SDL_image
with_sounds_cache
+with_externals_cache
with_spandsp
with_ss7
with_speex
@@ -2117,6 +2126,8 @@ Optional Packages:
--with-SDL_image=PATH use Sdl Image files in PATH
--with-sounds-cache=PATH
use cached sound tarfiles in PATH
+ --with-externals-cache=PATH
+ use cached external module tarfiles in PATH
--with-spandsp=PATH use SPANDSP files in PATH
--with-ss7=PATH use ISDN SS7 files in PATH
--with-speex=PATH use Speex files in PATH
@@ -7458,6 +7469,47 @@ $as_echo "no" >&6; }
fi
+# Extract the first word of "bash", so it can be a program name with args.
+set dummy bash; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_BASH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $BASH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_BASH="$BASH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_BASH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_BASH" && ac_cv_path_BASH=":"
+ ;;
+esac
+fi
+BASH=$ac_cv_path_BASH
+if test -n "$BASH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BASH" >&5
+$as_echo "$BASH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
# Extract the first word of "git", so it can be a program name with args.
set dummy git; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -7765,6 +7817,20 @@ else
fi
+if test "x${XMLSTARLET}" = "x:" ; then
+ PBX_XMLSTARLET=0
+else
+ PBX_XMLSTARLET=1
+fi
+
+
+if test "x${BASH}" = "x:" ; then
+ PBX_BASH=0
+else
+ PBX_BASH=1
+fi
+
+
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}soxmix", so it can be a program name with args.
set dummy ${ac_tool_prefix}soxmix; ac_word=$2
@@ -10938,6 +11004,18 @@ PBX_PJSIP_EVSUB_GRP_LOCK=0
+PJSIP_INV_SESSION_REF_DESCRIP="PJSIP INVITE Session Reference Count support"
+PJSIP_INV_SESSION_REF_OPTION=pjsip
+PJSIP_INV_SESSION_REF_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_INV_SESSION_REF=0
+
+
+
+
+
+
+
POPT_DESCRIP="popt"
POPT_OPTION="popt"
@@ -11463,6 +11541,30 @@ fi
+
+# Check whether --with-externals-cache was given.
+if test "${with_externals_cache+set}" = set; then :
+ withval=$with_externals_cache;
+ case ${withval} in
+ n|no)
+ unset EXTERNALS_CACHE_DIR
+ ;;
+ *)
+ if test "x${withval}" = "x"; then
+ :
+ else
+ EXTERNALS_CACHE_DIR="${withval}"
+ fi
+ ;;
+ esac
+
+else
+ :
+fi
+
+
+
+
SPANDSP_DESCRIP="SPANDSP"
SPANDSP_OPTION="spandsp"
PBX_SPANDSP=0
@@ -24863,6 +24965,9 @@ $as_echo "#define HAVE_PJSIP_TLS_TRANSPORT_PROTO 1" >>confdefs.h
$as_echo "#define HAVE_PJSIP_EVSUB_GRP_LOCK 1" >>confdefs.h
+$as_echo "#define HAVE_PJSIP_INV_SESSION_REF 1" >>confdefs.h
+
+
else
if test "x${PBX_PJPROJECT}" != "x1" -a "${USE_PJPROJECT}" != "no"; then
@@ -25683,6 +25788,110 @@ _ACEOF
fi
+
+if test "x${PBX_PJSIP_INV_SESSION_REF}" != "x1" -a "${USE_PJSIP_INV_SESSION_REF}" != "no"; then
+ pbxlibdir=""
+ # if --with-PJSIP_INV_SESSION_REF=DIR has been specified, use it.
+ if test "x${PJSIP_INV_SESSION_REF_DIR}" != "x"; then
+ if test -d ${PJSIP_INV_SESSION_REF_DIR}/lib; then
+ pbxlibdir="-L${PJSIP_INV_SESSION_REF_DIR}/lib"
+ else
+ pbxlibdir="-L${PJSIP_INV_SESSION_REF_DIR}"
+ fi
+ fi
+ pbxfuncname="pjsip_inv_add_ref"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_PJSIP_INV_SESSION_REF_FOUND=yes
+ else
+ ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
+ as_ac_Lib=`$as_echo "ac_cv_lib_pjsip_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpjsip" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpjsip... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ AST_PJSIP_INV_SESSION_REF_FOUND=yes
+else
+ AST_PJSIP_INV_SESSION_REF_FOUND=no
+fi
+
+ CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+ fi
+
+ # now check for the header.
+ if test "${AST_PJSIP_INV_SESSION_REF_FOUND}" = "yes"; then
+ PJSIP_INV_SESSION_REF_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
+ # if --with-PJSIP_INV_SESSION_REF=DIR has been specified, use it.
+ if test "x${PJSIP_INV_SESSION_REF_DIR}" != "x"; then
+ PJSIP_INV_SESSION_REF_INCLUDE="-I${PJSIP_INV_SESSION_REF_DIR}/include"
+ fi
+ PJSIP_INV_SESSION_REF_INCLUDE="${PJSIP_INV_SESSION_REF_INCLUDE} $PJPROJECT_CFLAGS"
+ if test "xpjsip.h" = "x" ; then # no header, assume found
+ PJSIP_INV_SESSION_REF_HEADER_FOUND="1"
+ else # check for the header
+ ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${PJSIP_INV_SESSION_REF_INCLUDE}"
+ ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
+if test "x$ac_cv_header_pjsip_h" = xyes; then :
+ PJSIP_INV_SESSION_REF_HEADER_FOUND=1
+else
+ PJSIP_INV_SESSION_REF_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+ fi
+ if test "x${PJSIP_INV_SESSION_REF_HEADER_FOUND}" = "x0" ; then
+ PJSIP_INV_SESSION_REF_LIB=""
+ PJSIP_INV_SESSION_REF_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ PJSIP_INV_SESSION_REF_LIB=""
+ fi
+ PBX_PJSIP_INV_SESSION_REF=1
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PJSIP_INV_SESSION_REF 1
+_ACEOF
+
+ fi
+ fi
+fi
+
+
fi
fi
diff --git a/configure.ac b/configure.ac
index 588a50075..1ba86bb7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -282,6 +282,7 @@ AC_PATH_PROG([CATDVI], [catdvi], :)
AC_PATH_PROG([KPATHSEA], [kpsewhich], :)
AC_PATH_PROG([XMLLINT], [xmllint], :)
AC_PATH_PROG([XMLSTARLET], [xmlstarlet], :)
+AC_PATH_PROG([BASH], [bash], :)
AC_PATH_PROG([GIT], [git], :)
AC_PATH_PROG([ALEMBIC], [alembic], :)
if test "${WGET}" != ":" ; then
@@ -341,6 +342,20 @@ else
fi
AC_SUBST(PBX_FLEX)
+if test "x${XMLSTARLET}" = "x:" ; then
+ PBX_XMLSTARLET=0
+else
+ PBX_XMLSTARLET=1
+fi
+AC_SUBST(PBX_XMLSTARLET)
+
+if test "x${BASH}" = "x:" ; then
+ PBX_BASH=0
+else
+ PBX_BASH=1
+fi
+AC_SUBST(PBX_BASH)
+
AC_CHECK_TOOL([SOXMIX], [soxmix], [:])
if test "${SOXMIX}" != ":" ; then
AC_DEFINE([HAVE_SOXMIX], 1, [Define to 1 if your system has soxmix application.])
@@ -487,6 +502,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_fro
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver Support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport proto field support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_SESSION_REF], [PJSIP INVITE Session Reference Count support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
@@ -521,6 +537,7 @@ AST_EXT_LIB_SETUP([RESAMPLE], [LIBRESAMPLE], [resample])
AST_EXT_LIB_SETUP([SDL], [Sdl], [sdl])
AST_EXT_LIB_SETUP([SDL_IMAGE], [Sdl Image], [SDL_image])
AST_OPTION_ONLY([sounds-cache], [SOUNDS_CACHE_DIR], [cached sound tarfiles], [])
+AST_OPTION_ONLY([externals-cache], [EXTERNALS_CACHE_DIR], [cached external module tarfiles], [])
AST_EXT_LIB_SETUP([SPANDSP], [SPANDSP], [spandsp])
AST_EXT_LIB_SETUP([SS7], [ISDN SS7], [ss7])
AST_EXT_LIB_SETUP([SPEEX], [Speex], [speex])
@@ -2190,6 +2207,7 @@ if test "$USE_PJPROJECT" != "no" ; then
CPPFLAGS="${saved_cppflags}"
AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
+ AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
fi
fi
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 5e53dbc70..337878c4d 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -596,6 +596,9 @@
/* Define if your system has pjsip_get_dest_info declared. */
#undef HAVE_PJSIP_GET_DEST_INFO
+/* Define if your system has PJSIP_INV_SESSION_REF */
+#undef HAVE_PJSIP_INV_SESSION_REF
+
/* Define if your system has the PJSIP_REPLACE_MEDIA_STREAM headers. */
#undef HAVE_PJSIP_REPLACE_MEDIA_STREAM
diff --git a/makeopts.in b/makeopts.in
index 9fa49bc04..9bd523dbc 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -28,11 +28,13 @@ WGET=@WGET@
FETCH=@FETCH@
DOWNLOAD=@DOWNLOAD@
SOUNDS_CACHE_DIR=@SOUNDS_CACHE_DIR@
+EXTERNALS_CACHE_DIR=@EXTERNALS_CACHE_DIR@
RUBBER=@RUBBER@
CATDVI=@CATDVI@
KPATHSEA=@KPATHSEA@
XMLLINT=@XMLLINT@
XMLSTARLET=@XMLSTARLET@
+BASH=@BASH@
MD5=@MD5@
SHA1SUM=@SHA1SUM@
OPENSSL=@OPENSSL@
diff --git a/res/res.xml b/res/res.xml
new file mode 100644
index 000000000..e9cb5f962
--- /dev/null
+++ b/res/res.xml
@@ -0,0 +1,6 @@
+<member name="res_digium_phone" displayname="Download the Digium Phone Module for Asterisk. See http://downloads.digium.com/pub/telephony/res_digium_phone/README.">
+ <support_level>external</support_level>
+ <depend>xmlstarlet</depend>
+ <depend>bash</depend>
+ <defaultenabled>no</defaultenabled>
+</member>
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 856626a02..050970998 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -213,6 +213,11 @@ static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sd
int i;
int handled = 0;
+ if (session->inv_session && session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ ast_log(LOG_ERROR, "Failed to handle incoming SDP. Session has been already disconnected\n");
+ return -1;
+ }
+
for (i = 0; i < sdp->media_count; ++i) {
/* See if there are registered handlers for this media stream type */
char media[20];
@@ -2087,6 +2092,16 @@ static int new_invite(void *data)
* so that we will be notified so we can destroy the session properly
*/
+ if (invite->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
+ invite->session->inv_session->cause,
+ pjsip_get_status_text(invite->session->inv_session->cause)->ptr);
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+ pjsip_inv_dec_ref(invite->session->inv_session);
+#endif
+ return -1;
+ }
+
switch (get_destination(invite->session, invite->rdata)) {
case SIP_GET_DEST_EXTEN_FOUND:
/* Things worked. Keep going */
@@ -2097,7 +2112,7 @@ static int new_invite(void *data)
} else {
pjsip_inv_terminate(invite->session->inv_session, 416, PJ_TRUE);
}
- return 0;
+ goto end;
case SIP_GET_DEST_EXTEN_NOT_FOUND:
case SIP_GET_DEST_EXTEN_PARTIAL:
default:
@@ -2110,7 +2125,7 @@ static int new_invite(void *data)
} else {
pjsip_inv_terminate(invite->session->inv_session, 404, PJ_TRUE);
}
- return 0;
+ goto end;
};
if ((sdp_info = pjsip_rdata_get_sdp_info(invite->rdata)) && (sdp_info->sdp_err == PJ_SUCCESS) && sdp_info->sdp) {
@@ -2120,7 +2135,7 @@ static int new_invite(void *data)
} else {
pjsip_inv_terminate(invite->session->inv_session, 488, PJ_TRUE);
}
- return 0;
+ goto end;
}
/* We are creating a local SDP which is an answer to their offer */
local = create_local_sdp(invite->session->inv_session, invite->session, sdp_info->sdp);
@@ -2136,7 +2151,7 @@ static int new_invite(void *data)
} else {
pjsip_inv_terminate(invite->session->inv_session, 500, PJ_TRUE);
}
- return 0;
+ goto end;
} else {
pjsip_inv_set_local_sdp(invite->session->inv_session, local);
pjmedia_sdp_neg_set_prefer_remote_codec_order(invite->session->inv_session->neg, PJ_FALSE);
@@ -2153,12 +2168,16 @@ static int new_invite(void *data)
/* At this point, we've verified what we can, so let's go ahead and send a 100 Trying out */
if (pjsip_inv_initial_answer(invite->session->inv_session, invite->rdata, 100, NULL, NULL, &tdata) != PJ_SUCCESS) {
pjsip_inv_terminate(invite->session->inv_session, 500, PJ_TRUE);
- return 0;
+ goto end;
}
ast_sip_session_send_response(invite->session, tdata);
handle_incoming_request(invite->session, invite->rdata);
+end:
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+ pjsip_inv_dec_ref(invite->session->inv_session);
+#endif
return 0;
}
@@ -2179,6 +2198,20 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
return;
}
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+ if (pjsip_inv_add_ref(inv_session) != PJ_SUCCESS) {
+ ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+ if (inv_session->state != PJSIP_INV_STATE_DISCONNECTED) {
+ if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
+ pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+ } else {
+ internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
+ }
+ }
+ return;
+ }
+#endif
+
session = ast_sip_session_alloc(endpoint, NULL, inv_session, rdata);
if (!session) {
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
@@ -2186,6 +2219,9 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
} else {
internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
}
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+ pjsip_inv_dec_ref(inv_session);
+#endif
return;
}
@@ -2196,6 +2232,9 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
} else {
internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
}
+#ifdef HAVE_PJSIP_INV_SESSION_REF
+ pjsip_inv_dec_ref(inv_session);
+#endif
ao2_cleanup(invite);
}
ao2_ref(session, -1);
@@ -2467,8 +2506,9 @@ static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdat
}
}
-static void session_end(struct ast_sip_session *session)
+static int session_end(void *vsession)
{
+ struct ast_sip_session *session = vsession;
struct ast_sip_session_supplement *iter;
/* Stop the scheduled termination */
@@ -2480,6 +2520,7 @@ static void session_end(struct ast_sip_session *session)
iter->session_end(session);
}
}
+ return 0;
}
/*!
@@ -2585,7 +2626,10 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
}
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- session_end(session);
+ if (ast_sip_push_task(session->serializer, session_end, session)) {
+ /* Do it anyway even though this is not the right thread. */
+ session_end(session);
+ }
}
}
@@ -2752,7 +2796,11 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* Pass the session ref held by session->inv_session to
* session_end_completion().
*/
- session_end_completion(session);
+ if (session
+ && ast_sip_push_task(session->serializer, session_end_completion, session)) {
+ /* Do it anyway even though this is not the right thread. */
+ session_end_completion(session);
+ }
return;
}
break;
@@ -2877,7 +2925,12 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
static const pj_str_t STR_IP6 = { "IP6", 3 };
pjmedia_sdp_session *local;
- if (!(local = PJ_POOL_ZALLOC_T(inv->pool_prov, pjmedia_sdp_session))) {
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ ast_log(LOG_ERROR, "Failed to create session SDP. Session has been already disconnected\n");
+ return NULL;
+ }
+
+ if (!inv->pool_prov || !(local = PJ_POOL_ZALLOC_T(inv->pool_prov, pjmedia_sdp_session))) {
return NULL;
}
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
index 67ac04d4d..7a079f657 100644
--- a/third-party/pjproject/configure.m4
+++ b/third-party/pjproject/configure.m4
@@ -45,4 +45,5 @@ AC_DEFUN([PJPROJECT_CONFIGURE],
PJPROJECT_SYMBOL_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip_endpt_set_ext_resolver], [pjsip.h])
AC_DEFINE([HAVE_PJSIP_TLS_TRANSPORT_PROTO], 1, [Define if your system has PJSIP_TLS_TRANSPORT_PROTO])
AC_DEFINE([HAVE_PJSIP_EVSUB_GRP_LOCK], 1, [Define if your system has PJSIP_EVSUB_GRP_LOCK])
+ AC_DEFINE([HAVE_PJSIP_INV_SESSION_REF], 1, [Define if your system has PJSIP_INV_SESSION_REF])
])
diff --git a/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch b/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch
new file mode 100644
index 000000000..12ae6a028
--- /dev/null
+++ b/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch
@@ -0,0 +1,212 @@
+When a transport error occured on an INVITE session
+the stack calls on_tsx_state_changed with new state
+PJSIP_INV_STATE_DISCONNECTED and immediately destroys
+the INVITE session.
+At the same time this INVITE session could being processed
+on another thread. This thread could use the session's
+memory pools which were already freed, so we get segfault.
+
+This patch adds a reference counter and new functions:
+pjsip_inv_add_ref and pjsip_inv_dec_ref.
+The INVITE session is destroyed only when the reference
+counter has reached zero.
+
+To avoid race condition an application should call
+pjsip_inv_add_ref/pjsip_inv_dec_ref.
+
+Index: pjsip/include/pjsip-ua/sip_inv.h
+===================================================================
+--- a/pjsip/include/pjsip-ua/sip_inv.h (revision 5434)
++++ b/pjsip/include/pjsip-ua/sip_inv.h (revision 5435)
+@@ -383,6 +383,11 @@
+ * Other applications that want to use these pools must understand
+ * that the flip-flop pool's lifetimes are synchronized to the
+ * SDP offer-answer negotiation.
++ *
++ * The lifetime of this session is controlled by the reference counter in this
++ * structure, which is manipulated by calling #pjsip_inv_add_ref and
++ * #pjsip_inv_dec_ref. When the reference counter has reached zero, then
++ * this session will be destroyed.
+ */
+ struct pjsip_inv_session
+ {
+@@ -412,6 +417,7 @@
+ struct pjsip_timer *timer; /**< Session Timers. */
+ pj_bool_t following_fork; /**< Internal, following
+ forked media? */
++ pj_atomic_t *ref_cnt; /**< Reference counter. */
+ };
+
+
+@@ -631,6 +637,30 @@
+
+
+ /**
++ * Add reference counter to the INVITE session. The reference counter controls
++ * the life time of the session, ie. when the counter reaches zero, then it
++ * will be destroyed.
++ *
++ * @param inv The INVITE session.
++ * @return PJ_SUCCESS if the INVITE session reference counter
++ * was increased.
++ */
++PJ_DECL(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv );
++
++/**
++ * Decrement reference counter of the INVITE session.
++ * When the session is no longer used, it will be destroyed and
++ * caller is informed with PJ_EGONE return status.
++ *
++ * @param inv The INVITE session.
++ * @return PJ_SUCCESS if the INVITE session reference counter
++ * was decreased. A status PJ_EGONE will be returned to
++ * inform that session is destroyed.
++ */
++PJ_DECL(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv );
++
++
++/**
+ * Forcefully terminate and destroy INVITE session, regardless of
+ * the state of the session. Note that this function should only be used
+ * when there is failure in the INVITE session creation. After the
+Index: pjsip/src/pjsip-ua/sip_inv.c
+===================================================================
+--- a/pjsip/src/pjsip-ua/sip_inv.c (revision 5434)
++++ b/pjsip/src/pjsip-ua/sip_inv.c (revision 5435)
+@@ -195,6 +195,65 @@
+ }
+
+ /*
++ * Add reference to INVITE session.
++ */
++PJ_DEF(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv )
++{
++ PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL);
++
++ pj_atomic_inc(inv->ref_cnt);
++
++ return PJ_SUCCESS;
++}
++
++static void inv_session_destroy(pjsip_inv_session *inv)
++{
++ if (inv->last_ack) {
++ pjsip_tx_data_dec_ref(inv->last_ack);
++ inv->last_ack = NULL;
++ }
++ if (inv->invite_req) {
++ pjsip_tx_data_dec_ref(inv->invite_req);
++ inv->invite_req = NULL;
++ }
++ if (inv->pending_bye) {
++ pjsip_tx_data_dec_ref(inv->pending_bye);
++ inv->pending_bye = NULL;
++ }
++ pjsip_100rel_end_session(inv);
++ pjsip_timer_end_session(inv);
++ pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod);
++
++ /* Release the flip-flop pools */
++ pj_pool_release(inv->pool_prov);
++ inv->pool_prov = NULL;
++ pj_pool_release(inv->pool_active);
++ inv->pool_active = NULL;
++
++ pj_atomic_destroy(inv->ref_cnt);
++ inv->ref_cnt = NULL;
++}
++
++/*
++ * Decrease INVITE session reference, destroy it when the reference count
++ * reaches zero.
++ */
++PJ_DEF(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv )
++{
++ pj_atomic_value_t ref_cnt;
++
++ PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL);
++
++ ref_cnt = pj_atomic_dec_and_get(inv->ref_cnt);
++ pj_assert( ref_cnt >= 0);
++ if (ref_cnt == 0) {
++ inv_session_destroy(inv);
++ return PJ_EGONE;
++ }
++ return PJ_SUCCESS;
++}
++
++/*
+ * Set session state.
+ */
+ static void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,
+@@ -261,27 +320,7 @@
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED &&
+ prev_state != PJSIP_INV_STATE_DISCONNECTED)
+ {
+- if (inv->last_ack) {
+- pjsip_tx_data_dec_ref(inv->last_ack);
+- inv->last_ack = NULL;
+- }
+- if (inv->invite_req) {
+- pjsip_tx_data_dec_ref(inv->invite_req);
+- inv->invite_req = NULL;
+- }
+- if (inv->pending_bye) {
+- pjsip_tx_data_dec_ref(inv->pending_bye);
+- inv->pending_bye = NULL;
+- }
+- pjsip_100rel_end_session(inv);
+- pjsip_timer_end_session(inv);
+- pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod);
+-
+- /* Release the flip-flop pools */
+- pj_pool_release(inv->pool_prov);
+- inv->pool_prov = NULL;
+- pj_pool_release(inv->pool_active);
+- inv->pool_active = NULL;
++ pjsip_inv_dec_ref(inv);
+ }
+ }
+
+@@ -838,6 +877,12 @@
+ inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session);
+ pj_assert(inv != NULL);
+
++ status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt);
++ if (status != PJ_SUCCESS) {
++ pjsip_dlg_dec_lock(dlg);
++ return status;
++ }
++
+ inv->pool = dlg->pool;
+ inv->role = PJSIP_ROLE_UAC;
+ inv->state = PJSIP_INV_STATE_NULL;
+@@ -881,6 +926,7 @@
+ pjsip_100rel_attach(inv);
+
+ /* Done */
++ pjsip_inv_add_ref(inv);
+ *p_inv = inv;
+
+ pjsip_dlg_dec_lock(dlg);
+@@ -1471,6 +1517,12 @@
+ inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session);
+ pj_assert(inv != NULL);
+
++ status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt);
++ if (status != PJ_SUCCESS) {
++ pjsip_dlg_dec_lock(dlg);
++ return status;
++ }
++
+ inv->pool = dlg->pool;
+ inv->role = PJSIP_ROLE_UAS;
+ inv->state = PJSIP_INV_STATE_NULL;
+@@ -1540,6 +1592,7 @@
+ }
+
+ /* Done */
++ pjsip_inv_add_ref(inv);
+ pjsip_dlg_dec_lock(dlg);
+ *p_inv = inv;
+