diff options
-rw-r--r-- | CHANGES | 11 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | Makefile.moddir_rules | 12 | ||||
-rw-r--r-- | apps/app_dial.c | 9 | ||||
-rwxr-xr-x | build_tools/download_externals | 180 | ||||
-rwxr-xr-x | build_tools/list_valid_installed_externals | 55 | ||||
-rwxr-xr-x | build_tools/make_version | 4 | ||||
-rw-r--r-- | build_tools/menuselect-deps.in | 2 | ||||
-rw-r--r-- | codecs/codecs.xml | 25 | ||||
-rwxr-xr-x | configure | 209 | ||||
-rw-r--r-- | configure.ac | 18 | ||||
-rw-r--r-- | include/asterisk/autoconfig.h.in | 3 | ||||
-rw-r--r-- | makeopts.in | 2 | ||||
-rw-r--r-- | res/res.xml | 6 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 71 | ||||
-rw-r--r-- | third-party/pjproject/configure.m4 | 1 | ||||
-rw-r--r-- | third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch | 212 |
17 files changed, 810 insertions, 17 deletions
@@ -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. @@ -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> @@ -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; + |