diff options
-rw-r--r-- | channels/chan_unistim.c | 27 | ||||
-rwxr-xr-x | configure | 125 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | include/asterisk/autoconfig.h.in | 4 | ||||
-rw-r--r-- | include/asterisk/channel.h | 34 | ||||
-rw-r--r-- | include/asterisk/channel_internal.h | 4 | ||||
-rw-r--r-- | include/asterisk/stream.h | 23 | ||||
-rw-r--r-- | main/Makefile | 4 | ||||
-rw-r--r-- | main/channel.c | 9 | ||||
-rw-r--r-- | main/channel_internal_api.c | 103 | ||||
-rw-r--r-- | main/http.c | 17 | ||||
-rw-r--r-- | main/manager.c | 2 | ||||
-rw-r--r-- | main/stream.c | 51 | ||||
-rw-r--r-- | res/res_config_sqlite3.c | 2 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_distributor.c | 4 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_scheduler.c | 32 | ||||
-rw-r--r-- | res/res_pjsip_exten_state.c | 5 | ||||
-rw-r--r-- | res/res_pjsip_pubsub.c | 231 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 2 | ||||
-rw-r--r-- | tests/test_stream.c | 318 | ||||
-rw-r--r-- | tests/test_voicemail_api.c | 4 | ||||
-rw-r--r-- | third-party/pjproject/configure.m4 | 1 | ||||
-rw-r--r-- | third-party/pjproject/patches/0010-evsub-Add-pjsip_evsub_set_uas_timeout.patch | 84 |
23 files changed, 939 insertions, 151 deletions
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c index b8ccdbb2a..f82522111 100644 --- a/channels/chan_unistim.c +++ b/channels/chan_unistim.c @@ -161,6 +161,7 @@ enum autoprov_extn { #define LED_HEADPHONE_ON 0x011 #define LED_MUTE_OFF 0x018 #define LED_MUTE_ON 0x019 +#define LED_MUTE_BLINK 0x1A #define SIZE_HEADER 6 #define SIZE_MAC_ADDR 17 @@ -357,8 +358,8 @@ struct unistim_subchannel { int softkey; /*! Softkey assigned */ pthread_t ss_thread; /*! unistim_ss thread handle */ int alreadygone; - char ringvolume; - char ringstyle; + signed char ringvolume; + signed char ringstyle; int moh; /*!< Music on hold in progress */ AST_LIST_ENTRY(unistim_subchannel) list; }; @@ -413,13 +414,13 @@ static struct unistim_device { char maintext2[25]; /*!< when the phone is idle, display this string on line 2 */ char titledefault[13]; /*!< title (text before date/time) */ char datetimeformat; /*!< format used for displaying time/date */ - char contrast; /*!< contrast */ + signed char contrast; /*!< contrast */ char country[3]; /*!< country used for dial tone frequency */ struct ast_tone_zone *tz; /*!< Tone zone for res_indications (ring, busy, congestion) */ - char ringvolume; /*!< Ring volume */ - char ringstyle; /*!< Ring melody */ - char cwvolume; /*!< Ring volume on call waiting */ - char cwstyle; /*!< Ring melody on call waiting */ + signed char ringvolume; /*!< Ring volume */ + signed char ringstyle; /*!< Ring melody */ + signed char cwvolume; /*!< Ring volume on call waiting */ + signed char cwstyle; /*!< Ring melody on call waiting */ int interdigit_timer; /*!< Interdigit timer for dialing number by timeout */ int dtmfduration; /*!< DTMF playback duration */ time_t nextdial; /*!< Timer used for dial by timeout */ @@ -443,7 +444,7 @@ static struct unistim_device { int nat; /*!< Used by the obscure ast_rtp_setnat */ enum autoprov_extn extension; /*!< See ifdef EXTENSION for valid values */ char extension_number[11]; /*!< Extension number entered by the user */ - char to_delete; /*!< Used in reload */ + signed char to_delete; /*!< Used in reload */ struct ast_silence_generator *silence_generator; AST_LIST_HEAD(,unistim_subchannel) subs; /*!< pointer to our current connection, channel... */ AST_LIST_HEAD(,unistim_line) lines; @@ -1701,7 +1702,7 @@ send_select_output(struct unistimsession *pte, unsigned char output, unsigned ch } pte->device->output = output; } -static void send_ring(struct unistimsession *pte, char volume, char style) +static void send_ring(struct unistimsession *pte, signed char volume, signed char style) { BUFFSEND; if (unistimdebug) { @@ -4835,7 +4836,7 @@ static int unistim_call(struct ast_channel *ast, const char *dest, int timeout) int res = 0, i; struct unistim_subchannel *sub, *sub_real; struct unistimsession *session; - char ringstyle, ringvolume; + signed char ringstyle, ringvolume; session = channel_to_session(ast); if (!session) { @@ -5438,8 +5439,8 @@ static struct unistim_subchannel *find_subchannel_by_name(const char *dest) if ((*at < '0') || (*at > '7')) { /* ring style */ ast_log(LOG_WARNING, "Invalid ring selection (%s)", at); } else { - char ring_volume = -1; - char ring_style = *at - '0'; + signed char ring_volume = -1; + signed char ring_style = *at - '0'; at++; if ((*at >= '0') && (*at <= '3')) { /* ring volume */ ring_volume = *at - '0'; @@ -6483,7 +6484,7 @@ static struct unistim_device *build_device(const char *cat, const struct ast_var int create = 1; int nbsoftkey, dateformat, timeformat, callhistory, sharpdial, linecnt; char linelabel[AST_MAX_EXTENSION]; - char ringvolume, ringstyle, cwvolume, cwstyle; + signed char ringvolume, ringstyle, cwvolume, cwstyle; /* First, we need to know if we already have this name in our list */ /* Get a lock for the device chained list */ @@ -947,6 +947,10 @@ PBX_POPT POPT_DIR POPT_INCLUDE POPT_LIB +PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT +PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR +PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE +PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB PBX_PJSIP_AUTH_CLT_DEINIT PJSIP_AUTH_CLT_DEINIT_DIR PJSIP_AUTH_CLT_DEINIT_INCLUDE @@ -7914,7 +7918,7 @@ if test "${WGET}" != ":" ; then DOWNLOAD_TIMEOUT='--timeout=$1' else if test "${CURL}" != ":" ; then DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\"" - DOWNLOAD_TO_STDOUT="${CURL} -L --progress-bar -w \"%{url_effective}\n\"" + DOWNLOAD_TO_STDOUT="${CURL} -L --progress-bar" DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)' else # Extract the first word of "fetch", so it can be a program name with args. @@ -9351,6 +9355,9 @@ $as_echo "#define HAVE_PJSIP_INV_SESSION_REF 1" >>confdefs.h $as_echo "#define HAVE_PJSIP_AUTH_CLT_DEINIT 1" >>confdefs.h +$as_echo "#define HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT 1" >>confdefs.h + + @@ -11545,6 +11552,18 @@ PBX_PJSIP_AUTH_CLT_DEINIT=0 + +PJSIP_EVSUB_SET_UAS_TIMEOUT_DESCRIP="PJSIP EVSUB Set UAS Timeout support" +PJSIP_EVSUB_SET_UAS_TIMEOUT_OPTION=pjsip +PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR=${PJPROJECT_DIR} + +PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT=0 + + + + + + fi @@ -26674,6 +26693,110 @@ _ACEOF fi + +if test "x${PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "x1" -a "${USE_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "no"; then + pbxlibdir="" + # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it. + if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then + if test -d ${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib; then + pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib" + else + pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" + fi + fi + pbxfuncname="pjsip_evsub_set_uas_timeout" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_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_EVSUB_SET_UAS_TIMEOUT_FOUND=yes +else + AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND}" = "yes"; then + PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB" + # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it. + if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then + PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="-I${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/include" + fi + PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE} $PJPROJECT_CFLAGS" + if test "xpjsip.h" = "x" ; then # no header, assume found + PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_SET_UAS_TIMEOUT_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_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=1 +else + PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND}" = "x0" ; then + PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="" + PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="" + fi + PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT 1 +_ACEOF + + fi + fi +fi + + fi fi diff --git a/configure.ac b/configure.ac index 982412ece..fff8ecf7e 100644 --- a/configure.ac +++ b/configure.ac @@ -297,7 +297,7 @@ if test "${WGET}" != ":" ; then DOWNLOAD_TIMEOUT='--timeout=$1' else if test "${CURL}" != ":" ; then DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\"" - DOWNLOAD_TO_STDOUT="${CURL} -L --progress-bar -w \"%{url_effective}\n\"" + DOWNLOAD_TO_STDOUT="${CURL} -L --progress-bar" DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)' else AC_PATH_PROG([FETCH], [fetch], [:]) @@ -518,6 +518,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport pro 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_OPTIONAL([PJSIP_AUTH_CLT_DEINIT], [pjsip_auth_clt_deinit support], [PJPROJECT], [pjsip]) +AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_SET_UAS_TIMEOUT], [PJSIP EVSUB Set UAS Timeout support], [PJPROJECT], [pjsip]) fi AST_EXT_LIB_SETUP([POPT], [popt], [popt]) @@ -2242,6 +2243,7 @@ if test "$USE_PJPROJECT" != "no" ; then 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]) AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) + AST_EXT_LIB_CHECK([PJSIP_EVSUB_SET_UAS_TIMEOUT], [pjsip], [pjsip_evsub_set_uas_timeout], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) fi fi diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index 34684920d..eecd957fa 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -596,6 +596,10 @@ /* Define to 1 if PJPROJECT has the PJSIP EVSUB Group Lock support feature. */ #undef HAVE_PJSIP_EVSUB_GRP_LOCK +/* Define to 1 if PJPROJECT has the PJSIP EVSUB Set UAS Timeout support + feature. */ +#undef HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT + /* Define to 1 if PJPROJECT has the PJSIP External Resolver Support feature. */ #undef HAVE_PJSIP_EXTERNAL_RESOLVER diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index e5f792f1f..4170a8af4 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -884,6 +884,10 @@ enum { * world */ AST_CHAN_TP_INTERNAL = (1 << 2), + /*! + * \brief Channels with this particular technology support multiple simultaneous streams + */ + AST_CHAN_TP_MULTISTREAM = (1 << 3), }; /*! \brief ast_channel flags */ @@ -4734,4 +4738,34 @@ enum ast_channel_error ast_channel_errno(void); */ int ast_channel_get_intercept_mode(void); +/*! + * \brief Retrieve the topology of streams on a channel + * + * \param chan The channel to get the stream topology of + * + * \pre chan is locked + * + * \retval non-NULL success + * \retval NULL failure + */ +struct ast_stream_topology *ast_channel_get_stream_topology( + const struct ast_channel *chan); + +/*! + * \brief Set the topology of streams on a channel + * + * \param chan The channel to set the stream topology on + * \param topology The stream topology to set + * + * \pre chan is locked + * + * \note If topology is NULL a new empty topology will be created + * and returned. + * + * \retval non-NULL Success + * \retval NULL failure + */ +struct ast_stream_topology *ast_channel_set_stream_topology( + struct ast_channel *chan, struct ast_stream_topology *topology); + #endif /* _ASTERISK_CHANNEL_H */ diff --git a/include/asterisk/channel_internal.h b/include/asterisk/channel_internal.h index 2316e2f24..3de2b14aa 100644 --- a/include/asterisk/channel_internal.h +++ b/include/asterisk/channel_internal.h @@ -27,3 +27,7 @@ int ast_channel_internal_setup_topics(struct ast_channel *chan); void ast_channel_internal_errno_set(enum ast_channel_error error); enum ast_channel_error ast_channel_internal_errno(void); +void ast_channel_internal_set_stream_topology(struct ast_channel *chan, + struct ast_stream_topology *topology); +void ast_channel_internal_swap_stream_topology(struct ast_channel *chan1, + struct ast_channel *chan2); diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h index cffe6ea4c..edb00b9eb 100644 --- a/include/asterisk/stream.h +++ b/include/asterisk/stream.h @@ -84,7 +84,7 @@ enum ast_stream_state { * * \since 15 */ -struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type); +struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type); /*! * \brief Destroy a media stream representation @@ -93,7 +93,7 @@ struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) * * \since 15 */ -void ast_stream_destroy(struct ast_stream *stream); +void ast_stream_free(struct ast_stream *stream); /*! * \brief Create a deep clone of an existing stream @@ -209,7 +209,7 @@ int ast_stream_get_position(const struct ast_stream *stream); * * \since 15 */ -struct ast_stream_topology *ast_stream_topology_create(void); +struct ast_stream_topology *ast_stream_topology_alloc(void); /*! * \brief Create a deep clone of an existing stream topology @@ -233,7 +233,7 @@ struct ast_stream_topology *ast_stream_topology_clone( * * \since 15 */ -void ast_stream_topology_destroy(struct ast_stream_topology *topology); +void ast_stream_topology_free(struct ast_stream_topology *topology); /*! * \brief Append a stream to the topology @@ -316,4 +316,19 @@ int ast_stream_topology_set_stream(struct ast_stream_topology *topology, struct ast_stream_topology *ast_stream_topology_create_from_format_cap( struct ast_format_cap *cap); +/*! + * \brief Gets the first stream of a specific type from the topology + * + * \param topology The topology of streams + * \param type The media type + * + * \retval non-NULL success + * \retval NULL failure + * + * \since 15 + */ +struct ast_stream *ast_stream_topology_get_first_stream_by_type( + const struct ast_stream_topology *topology, + enum ast_media_type type); + #endif /* _AST_STREAM_H */ diff --git a/main/Makefile b/main/Makefile index 4d1b2c41b..331da845c 100644 --- a/main/Makefile +++ b/main/Makefile @@ -355,7 +355,7 @@ else # Darwin endif endif ifneq ($(LDCONFIG),) - $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/" + $(LDCONFIG) endif $(LN) -sf asterisk "$(DESTDIR)$(ASTSBINDIR)/rasterisk" @@ -373,7 +373,7 @@ ifneq ($(ASTPJ_LIB).$(ASTPJ_SO_VERSION),.) rm -f "$(DESTDIR)$(ASTLIBDIR)/$(ASTPJ_LIB)" endif ifneq ($(LDCONFIG),) - $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/" + $(LDCONFIG) endif clean:: diff --git a/main/channel.c b/main/channel.c index 54db47351..1e7bc563e 100644 --- a/main/channel.c +++ b/main/channel.c @@ -73,6 +73,7 @@ #include "asterisk/test.h" #include "asterisk/stasis_channels.h" #include "asterisk/max_forwards.h" +#include "asterisk/stream.h" /*** DOCUMENTATION ***/ @@ -806,6 +807,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char struct ast_timer *timer; struct timeval now; const struct ast_channel_tech *channel_tech; + struct ast_stream_topology *topology; /* If shutting down, don't allocate any new channels */ if (ast_shutting_down()) { @@ -895,6 +897,11 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char return ast_channel_unref(tmp); } + if (!(topology = ast_stream_topology_alloc())) { + return ast_channel_unref(tmp); + } + ast_channel_internal_set_stream_topology(tmp, topology); + /* Always watch the alertpipe */ ast_channel_set_fd(tmp, AST_ALERT_FD, ast_channel_internal_alert_readfd(tmp)); /* And timing pipe */ @@ -7083,6 +7090,8 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann ast_channel_tech(clonechan)->type, ast_channel_name(clonechan)); } + ast_channel_internal_swap_stream_topology(original, clonechan); + /* * Now, at this point, the "clone" channel is totally F'd up. * We mark it as a zombie so nothing tries to touch it. diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index a0cbe8643..1934eb9a4 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -46,6 +46,7 @@ #include "asterisk/stasis_channels.h" #include "asterisk/stasis_endpoints.h" #include "asterisk/stringfields.h" +#include "asterisk/stream.h" #include "asterisk/test.h" /*! @@ -221,6 +222,8 @@ struct ast_channel { struct stasis_cp_single *topics; /*!< Topic for all channel's events */ struct stasis_forward *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */ struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */ + struct ast_stream_topology *stream_topology; /*!< Stream topology */ + struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */ }; /*! \brief The monotonically increasing integer counter for channel uniqueids */ @@ -825,10 +828,57 @@ struct ast_format_cap *ast_channel_nativeformats(const struct ast_channel *chan) { return chan->nativeformats; } -void ast_channel_nativeformats_set(struct ast_channel *chan, struct ast_format_cap *value) + +static void channel_set_default_streams(struct ast_channel *chan) +{ + enum ast_media_type type; + + ast_assert(chan != NULL); + + for (type = AST_MEDIA_TYPE_UNKNOWN; type < AST_MEDIA_TYPE_END; type++) { + if (chan->stream_topology) { + chan->default_streams[type] = + ast_stream_topology_get_first_stream_by_type(chan->stream_topology, type); + } else { + chan->default_streams[type] = NULL; + } + } +} + +void ast_channel_internal_set_stream_topology(struct ast_channel *chan, + struct ast_stream_topology *topology) +{ + ast_stream_topology_free(chan->stream_topology); + chan->stream_topology = topology; + channel_set_default_streams(chan); +} + +void ast_channel_nativeformats_set(struct ast_channel *chan, + struct ast_format_cap *value) { + ast_assert(chan != NULL); + ao2_replace(chan->nativeformats, value); + + /* If chan->stream_topology is NULL, the channel is being destroyed + * and topology is destroyed. + */ + if (!chan->stream_topology) { + return; + } + + if (!chan->tech || !(chan->tech->properties & AST_CHAN_TP_MULTISTREAM) || !value) { + struct ast_stream_topology *new_topology; + + if (!value) { + new_topology = ast_stream_topology_alloc(); + } else { + new_topology = ast_stream_topology_create_from_format_cap(value); + } + ast_channel_internal_set_stream_topology(chan, new_topology); + } } + struct ast_framehook_list *ast_channel_framehooks(const struct ast_channel *chan) { return chan->framehooks; @@ -1637,6 +1687,8 @@ void ast_channel_internal_cleanup(struct ast_channel *chan) stasis_cp_single_unsubscribe(chan->topics); chan->topics = NULL; + + ast_channel_internal_set_stream_topology(chan, NULL); } void ast_channel_internal_finalize(struct ast_channel *chan) @@ -1729,3 +1781,52 @@ enum ast_channel_error ast_channel_internal_errno(void) return *error_code; } + +struct ast_stream_topology *ast_channel_get_stream_topology( + const struct ast_channel *chan) +{ + ast_assert(chan != NULL); + + return chan->stream_topology; +} + +struct ast_stream_topology *ast_channel_set_stream_topology(struct ast_channel *chan, + struct ast_stream_topology *topology) +{ + struct ast_stream_topology *new_topology; + + ast_assert(chan != NULL); + + /* A non-MULTISTREAM channel can't manipulate topology directly */ + ast_assert(chan->tech != NULL && (chan->tech->properties & AST_CHAN_TP_MULTISTREAM)); + + /* Unless the channel is being destroyed, we always want a topology on + * it even if its empty. + */ + if (!topology) { + new_topology = ast_stream_topology_alloc(); + } else { + new_topology = topology; + } + + if (new_topology) { + ast_channel_internal_set_stream_topology(chan, new_topology); + } + + return new_topology; +} + +void ast_channel_internal_swap_stream_topology(struct ast_channel *chan1, + struct ast_channel *chan2) +{ + struct ast_stream_topology *tmp_topology; + + ast_assert(chan1 != NULL && chan2 != NULL); + + tmp_topology = chan1->stream_topology; + chan1->stream_topology = chan2->stream_topology; + chan2->stream_topology = tmp_topology; + + channel_set_default_streams(chan1); + channel_set_default_streams(chan2); +} diff --git a/main/http.c b/main/http.c index 5f57b1eb0..0db6ee7b6 100644 --- a/main/http.c +++ b/main/http.c @@ -2056,22 +2056,20 @@ static int __ast_http_load(int reload) http_tls_was_enabled = (reload && http_tls_cfg.enabled); http_tls_cfg.enabled = 0; - if (http_tls_cfg.certfile) { - ast_free(http_tls_cfg.certfile); - } + + ast_free(http_tls_cfg.certfile); http_tls_cfg.certfile = ast_strdup(AST_CERTFILE); - if (http_tls_cfg.pvtfile) { - ast_free(http_tls_cfg.pvtfile); - } + ast_free(http_tls_cfg.capath); + http_tls_cfg.capath = ast_strdup(""); + + ast_free(http_tls_cfg.pvtfile); http_tls_cfg.pvtfile = ast_strdup(""); /* Apply modern intermediate settings according to the Mozilla OpSec team as of July 30th, 2015 but disable TLSv1 */ ast_set_flag(&http_tls_cfg.flags, AST_SSL_DISABLE_TLSV1 | AST_SSL_SERVER_CIPHER_ORDER); - if (http_tls_cfg.cipher) { - ast_free(http_tls_cfg.cipher); - } + ast_free(http_tls_cfg.cipher); http_tls_cfg.cipher = ast_strdup("ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"); AST_RWLIST_WRLOCK(&uri_redirects); @@ -2285,6 +2283,7 @@ static void http_shutdown(void) ast_tcptls_server_stop(&https_desc); } ast_free(http_tls_cfg.certfile); + ast_free(http_tls_cfg.capath); ast_free(http_tls_cfg.pvtfile); ast_free(http_tls_cfg.cipher); diff --git a/main/manager.c b/main/manager.c index a25497fd3..f11c8dca4 100644 --- a/main/manager.c +++ b/main/manager.c @@ -6629,7 +6629,6 @@ static void *session_do(void *data) struct ast_sockaddr ser_remote_address_tmp; if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) { - ast_iostream_close(ser->stream); ast_atomic_fetchadd_int(&unauth_sessions, -1); goto done; } @@ -6638,7 +6637,6 @@ static void *session_do(void *data) session = build_mansession(&ser_remote_address_tmp); if (session == NULL) { - ast_iostream_close(ser->stream); ast_atomic_fetchadd_int(&unauth_sessions, -1); goto done; } diff --git a/main/stream.c b/main/stream.c index 24844c4ab..aacd33f17 100644 --- a/main/stream.c +++ b/main/stream.c @@ -69,7 +69,7 @@ struct ast_stream_topology { AST_VECTOR(, struct ast_stream *) streams; }; -struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) +struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type) { struct ast_stream *stream; @@ -108,7 +108,7 @@ struct ast_stream *ast_stream_clone(const struct ast_stream *stream) return new_stream; } -void ast_stream_destroy(struct ast_stream *stream) +void ast_stream_free(struct ast_stream *stream) { if (!stream) { return; @@ -176,7 +176,7 @@ int ast_stream_get_position(const struct ast_stream *stream) } #define TOPOLOGY_INITIAL_STREAM_COUNT 2 -struct ast_stream_topology *ast_stream_topology_create(void) +struct ast_stream_topology *ast_stream_topology_alloc(void) { struct ast_stream_topology *topology; @@ -201,7 +201,7 @@ struct ast_stream_topology *ast_stream_topology_clone( ast_assert(topology != NULL); - new_topology = ast_stream_topology_create(); + new_topology = ast_stream_topology_alloc(); if (!new_topology) { return NULL; } @@ -211,8 +211,8 @@ struct ast_stream_topology *ast_stream_topology_clone( ast_stream_clone(AST_VECTOR_GET(&topology->streams, i)); if (!stream || AST_VECTOR_APPEND(&new_topology->streams, stream)) { - ast_stream_destroy(stream); - ast_stream_topology_destroy(new_topology); + ast_stream_free(stream); + ast_stream_topology_free(new_topology); return NULL; } } @@ -220,13 +220,13 @@ struct ast_stream_topology *ast_stream_topology_clone( return new_topology; } -void ast_stream_topology_destroy(struct ast_stream_topology *topology) +void ast_stream_topology_free(struct ast_stream_topology *topology) { if (!topology) { return; } - AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_destroy); + AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_free); AST_VECTOR_FREE(&topology->streams); ast_free(topology); } @@ -272,7 +272,7 @@ int ast_stream_topology_set_stream(struct ast_stream_topology *topology, if (position < AST_VECTOR_SIZE(&topology->streams)) { existing_stream = AST_VECTOR_GET(&topology->streams, position); - ast_stream_destroy(existing_stream); + ast_stream_free(existing_stream); } stream->position = position; @@ -293,7 +293,7 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap( ast_assert(cap != NULL); - topology = ast_stream_topology_create(); + topology = ast_stream_topology_alloc(); if (!topology) { return NULL; } @@ -308,32 +308,51 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap( new_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); if (!new_cap) { - ast_stream_topology_destroy(topology); + ast_stream_topology_free(topology); return NULL; } ast_format_cap_set_framing(new_cap, ast_format_cap_get_framing(cap)); if (ast_format_cap_append_from_cap(new_cap, cap, type)) { ao2_cleanup(new_cap); - ast_stream_topology_destroy(topology); + ast_stream_topology_free(topology); return NULL; } - stream = ast_stream_create(ast_codec_media_type2str(type), type); + stream = ast_stream_alloc(ast_codec_media_type2str(type), type); if (!stream) { ao2_cleanup(new_cap); - ast_stream_topology_destroy(topology); + ast_stream_topology_free(topology); return NULL; } /* We're transferring the initial ref so no bump needed */ stream->formats = new_cap; stream->state = AST_STREAM_STATE_SENDRECV; if (ast_stream_topology_append_stream(topology, stream) == -1) { - ast_stream_destroy(stream); - ast_stream_topology_destroy(topology); + ast_stream_free(stream); + ast_stream_topology_free(topology); return NULL; } } return topology; } + +struct ast_stream *ast_stream_topology_get_first_stream_by_type( + const struct ast_stream_topology *topology, + enum ast_media_type type) +{ + int i; + + ast_assert(topology != NULL); + + for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) { + struct ast_stream *stream = AST_VECTOR_GET(&topology->streams, i); + + if (stream->type == type) { + return stream; + } + } + + return NULL; +} diff --git a/res/res_config_sqlite3.c b/res/res_config_sqlite3.c index b5c70ec2d..f2a6b00db 100644 --- a/res/res_config_sqlite3.c +++ b/res/res_config_sqlite3.c @@ -1125,6 +1125,8 @@ static int parse_config(int reload) if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) { ast_log(LOG_ERROR, "%s config file '%s'\n", config == CONFIG_STATUS_FILEMISSING ? "Missing" : "Invalid", config_filename); + ast_mutex_unlock(&config_lock); + return 0; } else { const char *cat; struct realtime_sqlite3_db *db; diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c index 741244586..eabfa4ba9 100644 --- a/res/res_pjsip/pjsip_distributor.c +++ b/res/res_pjsip/pjsip_distributor.c @@ -729,8 +729,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) ao2_ref(unid, -1); } ast_sip_report_auth_success(endpoint, rdata); - pjsip_tx_data_dec_ref(tdata); - return PJ_FALSE; + break; case AST_SIP_AUTHENTICATION_FAILED: log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); @@ -743,6 +742,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } + pjsip_tx_data_dec_ref(tdata); } return PJ_FALSE; diff --git a/res/res_pjsip/pjsip_scheduler.c b/res/res_pjsip/pjsip_scheduler.c index 27202c602..e4459da66 100644 --- a/res/res_pjsip/pjsip_scheduler.c +++ b/res/res_pjsip/pjsip_scheduler.c @@ -373,7 +373,7 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg struct ast_tm tm; char queued[32]; char last_start[32]; - char last_end[32]; + char next_start[32]; int datelen; struct timeval now = ast_tvnow(); const char *separator = "======================================"; @@ -397,19 +397,21 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n"); - ast_cli(a->fd, " %1$-24s %2$-8s %3$-9s %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", + ast_cli(a->fd, " %1$-24s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n", "Task Name", "Interval", "Times Run", "State", - datelen, "Queued", "Last Started", "Last Ended"); + datelen, "Queued", "Last Started", "Next Start", "( secs)"); - ast_cli(a->fd, " %1$-24.24s %2$-8.8s %3$-9.9s %4$-7.7s %6$-*5$.*5$s %7$-*5$.*5$s %8$-*5$.*5$s\n", + ast_cli(a->fd, " %1$-24.24s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n", separator, separator, separator, separator, - datelen, separator, separator, separator); + datelen, separator, separator, datelen + 8, separator); ao2_ref(tasks, +1); ao2_rdlock(tasks); i = ao2_iterator_init(tasks, 0); while ((schtd = ao2_iterator_next(&i))) { + int next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000; + struct timeval next = ast_tvadd(now, (struct timeval) {next_run_sec, 0}); ast_localtime(&schtd->when_queued, &tm, NULL); ast_strftime(queued, sizeof(queued), log_format, &tm); @@ -421,23 +423,17 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg ast_strftime(last_start, sizeof(last_start), log_format, &tm); } - if (ast_tvzero(schtd->last_end)) { - if (ast_tvzero(schtd->last_start)) { - strcpy(last_end, "not yet started"); - } else { - strcpy(last_end, "running"); - } - } else { - ast_localtime(&schtd->last_end, &tm, NULL); - ast_strftime(last_end, sizeof(last_end), log_format, &tm); - } + ast_localtime(&next, &tm, NULL); + ast_strftime(next_start, sizeof(next_start), log_format, &tm); - ast_cli(a->fd, " %1$-24.24s %2$-8.3f %3$-9d %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", + ast_cli(a->fd, " %1$-24.24s %2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n", schtd->name, schtd->interval / 1000.0, schtd->run_count, - schtd->is_running ? "running" : "waiting", - datelen, queued, last_start, last_end); + schtd->is_running ? "run" : "wait", + datelen, queued, last_start, + next_start, + next_run_sec); ao2_cleanup(schtd); } ao2_iterator_destroy(&i); diff --git a/res/res_pjsip_exten_state.c b/res/res_pjsip_exten_state.c index 9bb53bfe3..95a40829e 100644 --- a/res/res_pjsip_exten_state.c +++ b/res/res_pjsip_exten_state.c @@ -415,8 +415,9 @@ static int new_subscribe(struct ast_sip_endpoint *endpoint, const char *context = S_OR(endpoint->subscription.context, endpoint->context); if (!ast_exists_extension(NULL, context, resource, PRIORITY_HINT, NULL)) { - ast_log(LOG_NOTICE, "Extension state subscription failed: Extension %s does not exist in context '%s' or has no associated hint\n", - resource, context); + ast_log(LOG_NOTICE, "Endpoint '%s' state subscription failed: " + "Extension '%s' does not exist in context '%s' or has no associated hint\n", + ast_sorcery_object_get_id(endpoint), resource, context); return 404; } diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index 42f0dc11e..709dc6640 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -392,6 +392,13 @@ enum sip_subscription_tree_state { SIP_SUB_TREE_TERMINATED, }; +static char *sub_tree_state_description[] = { + "Normal", + "TerminatePending", + "TerminateInProgress", + "Terminated" +}; + /*! * \brief A tree of SIP subscriptions * @@ -428,6 +435,11 @@ struct sip_subscription_tree { AST_LIST_ENTRY(sip_subscription_tree) next; /*! Subscription tree state */ enum sip_subscription_tree_state state; + /*! On asterisk restart, this is the task data used + * to restart the expiration timer if pjproject isn't + * capable of restarting the timer. + */ + struct ast_sip_sched_task *expiration_task; }; /*! @@ -482,6 +494,17 @@ static const char *sip_subscription_roles_map[] = { [AST_SIP_NOTIFIER] = "Notifier" }; +enum sip_persistence_update_type { + /*! Called from send request */ + SUBSCRIPTION_PERSISTENCE_SEND_REQUEST = 0, + /*! Subscription created from initial client request */ + SUBSCRIPTION_PERSISTENCE_CREATED, + /*! Subscription recreated by asterisk on startup */ + SUBSCRIPTION_PERSISTENCE_RECREATED, + /*! Subscription created from client refresh */ + SUBSCRIPTION_PERSISTENCE_REFRESHED, +}; + AST_RWLIST_HEAD_STATIC(subscriptions, sip_subscription_tree); AST_RWLIST_HEAD_STATIC(body_generators, ast_sip_pubsub_body_generator); @@ -560,7 +583,7 @@ static struct subscription_persistence *subscription_persistence_create(struct s /*! \brief Function which updates persistence information of a subscription in sorcery */ static void subscription_persistence_update(struct sip_subscription_tree *sub_tree, - pjsip_rx_data *rdata) + pjsip_rx_data *rdata, enum sip_persistence_update_type type) { pjsip_dialog *dlg; @@ -568,6 +591,9 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr return; } + ast_debug(3, "Updating persistence for '%s->%s'\n", + ast_sorcery_object_get_id(sub_tree->endpoint), sub_tree->root->resource); + dlg = sub_tree->dlg; sub_tree->persistence->cseq = dlg->local.cseq; @@ -584,12 +610,15 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will * only ever have a single SIP message on it, and so we base persistence on that. */ - if (rdata->msg_info.msg_buf) { - ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf, - MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len)); - } else { - ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet, - sizeof(sub_tree->persistence->packet)); + if (type == SUBSCRIPTION_PERSISTENCE_CREATED + || type == SUBSCRIPTION_PERSISTENCE_RECREATED) { + if (rdata->msg_info.msg_buf) { + ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf, + MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len)); + } else { + ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet, + sizeof(sub_tree->persistence->packet)); + } } ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name, sizeof(sub_tree->persistence->src_name)); @@ -986,7 +1015,8 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a struct resources visited; if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) { - ast_debug(2, "Subscription to resource %s is not to a list\n", resource); + ast_debug(2, "Subscription '%s->%s' is not to a list\n", + ast_sorcery_object_get_id(endpoint), resource); tree->root = tree_node_alloc(resource, NULL, 0); if (!tree->root) { return 500; @@ -994,7 +1024,8 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a return handler->notifier->new_subscribe(endpoint, resource); } - ast_debug(2, "Subscription to resource %s is a list\n", resource); + ast_debug(2, "Subscription '%s->%s' is a list\n", + ast_sorcery_object_get_id(endpoint), resource); if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) { return 500; } @@ -1033,8 +1064,8 @@ static void remove_subscription(struct sip_subscription_tree *obj) if (i == obj) { AST_RWLIST_REMOVE_CURRENT(next); if (i->root) { - ast_debug(2, "Removing subscription to resource %s from list of subscriptions\n", - ast_sip_subscription_get_resource_name(i->root)); + ast_debug(2, "Removing subscription '%s->%s' from list of subscriptions\n", + ast_sorcery_object_get_id(i->endpoint), ast_sip_subscription_get_resource_name(i->root)); } break; } @@ -1045,7 +1076,8 @@ static void remove_subscription(struct sip_subscription_tree *obj) static void destroy_subscription(struct ast_sip_subscription *sub) { - ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource); + ast_debug(3, "Destroying SIP subscription from '%s->%s'\n", + ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource); ast_free(sub->body_text); AST_VECTOR_FREE(&sub->children); @@ -1197,7 +1229,10 @@ static void subscription_tree_destructor(void *obj) { struct sip_subscription_tree *sub_tree = obj; - ast_debug(3, "Destroying subscription tree %p\n", sub_tree); + ast_debug(3, "Destroying subscription tree %p '%s->%s'\n", + sub_tree, + sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown", + sub_tree->root ? sub_tree->root->resource : "Unknown"); ao2_cleanup(sub_tree->endpoint); @@ -1213,7 +1248,8 @@ static void subscription_tree_destructor(void *obj) void ast_sip_subscription_destroy(struct ast_sip_subscription *sub) { - ast_debug(3, "Removing subscription %p reference to subscription tree %p\n", sub, sub->tree); + ast_debug(3, "Removing subscription %p '%s->%s' reference to subscription tree %p\n", + sub, ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource, sub->tree); ao2_cleanup(sub->tree); } @@ -1320,7 +1356,6 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag); pjsip_ua_register_dlg(pjsip_ua_instance(), dlg); dlg->local.cseq = persistence->cseq; - dlg->remote.cseq = persistence->cseq; } pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub); @@ -1345,6 +1380,12 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s return sub_tree; } +/*! Wrapper structure for initial_notify_task */ +struct initial_notify_data { + struct sip_subscription_tree *sub_tree; + int expires; +}; + static int initial_notify_task(void *obj); static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state); @@ -1433,9 +1474,12 @@ static int sub_persistence_recreate(void *obj) } pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header); } + expires_header->ivalue = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000); if (expires_header->ivalue <= 0) { /* The subscription expired since we started recreating the subscription. */ + ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n", + persistence->endpoint, persistence->tag); ast_sorcery_delete(ast_sip_get_sorcery(), persistence); ao2_ref(endpoint, -1); return 0; @@ -1456,18 +1500,30 @@ static int sub_persistence_recreate(void *obj) ast_sorcery_delete(ast_sip_get_sorcery(), persistence); } } else { + struct initial_notify_data *ind = ast_malloc(sizeof(*ind)); + + if (!ind) { + pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE); + goto error; + } + + ind->sub_tree = ao2_bump(sub_tree); + ind->expires = expires_header->ivalue; + sub_tree->persistence = ao2_bump(persistence); - subscription_persistence_update(sub_tree, rdata); - if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, - ao2_bump(sub_tree))) { + subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_RECREATED); + if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) { /* Could not send initial subscribe NOTIFY */ pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE); ao2_ref(sub_tree, -1); + ast_free(ind); } } } else { ast_sorcery_delete(ast_sip_get_sorcery(), persistence); } + +error: resource_tree_destroy(&tree); ao2_ref(endpoint, -1); @@ -1485,6 +1541,8 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags) /* If this subscription has already expired remove it */ if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) { + ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n", + persistence->endpoint, persistence->tag); ast_sorcery_delete(ast_sip_get_sorcery(), persistence); return 0; } @@ -1814,7 +1872,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, res = internal_pjsip_evsub_send_request(sub_tree, tdata); - subscription_persistence_update(sub_tree, NULL); + subscription_persistence_update(sub_tree, NULL, SUBSCRIPTION_PERSISTENCE_SEND_REQUEST); ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET", "StateText: %s\r\n" @@ -2713,21 +2771,45 @@ static int generate_initial_notify(struct ast_sip_subscription *sub) return res; } +static int pubsub_on_refresh_timeout(void *userdata); + static int initial_notify_task(void * obj) { - struct sip_subscription_tree *sub_tree; + struct initial_notify_data *ind = obj; - sub_tree = obj; - if (generate_initial_notify(sub_tree->root)) { - pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE); + if (generate_initial_notify(ind->sub_tree->root)) { + pjsip_evsub_terminate(ind->sub_tree->evsub, PJ_TRUE); } else { - send_notify(sub_tree, 1); + send_notify(ind->sub_tree, 1); ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED", "Resource: %s", - sub_tree->root->resource); + ind->sub_tree->root->resource); + } + + if (ind->expires > -1) { + char *name = ast_alloca(strlen("->/ ") + + strlen(ind->sub_tree->persistence->endpoint) + + strlen(ind->sub_tree->root->resource) + + strlen(ind->sub_tree->root->handler->event_name) + + ind->sub_tree->dlg->call_id->id.slen + 1); + + sprintf(name, "%s->%s/%s %.*s", ind->sub_tree->persistence->endpoint, + ind->sub_tree->root->resource, ind->sub_tree->root->handler->event_name, + (int)ind->sub_tree->dlg->call_id->id.slen, ind->sub_tree->dlg->call_id->id.ptr); + + ast_debug(3, "Scheduling timer: %s\n", name); + ind->sub_tree->expiration_task = ast_sip_schedule_task(ind->sub_tree->serializer, + ind->expires * 1000, pubsub_on_refresh_timeout, name, + ind->sub_tree, AST_SIP_SCHED_TASK_FIXED | AST_SIP_SCHED_TASK_DATA_AO2); + if (!ind->sub_tree->expiration_task) { + ast_log(LOG_ERROR, "Unable to create expiration timer of %d seconds for %s\n", + ind->expires, name); + } } - ao2_ref(sub_tree, -1); + ao2_ref(ind->sub_tree, -1); + ast_free(ind); + return 0; } @@ -2820,12 +2902,25 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata) pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); } } else { + struct initial_notify_data *ind = ast_malloc(sizeof(*ind)); + + if (!ind) { + pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE); + resource_tree_destroy(&tree); + return PJ_TRUE; + } + + ind->sub_tree = ao2_bump(sub_tree); + /* Since this is a normal subscribe, pjproject takes care of the timer */ + ind->expires = -1; + sub_tree->persistence = subscription_persistence_create(sub_tree); - subscription_persistence_update(sub_tree, rdata); + subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_CREATED); sip_subscription_accept(sub_tree, rdata, resp); - if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) { + if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) { pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE); ao2_ref(sub_tree, -1); + ast_free(ind); } } @@ -3360,7 +3455,7 @@ static void set_state_terminated(struct ast_sip_subscription *sub) * send_notify ultimately calls pjsip_evsub_send_request * pjsip_evsub_send_request calls evsub's set_state * set_state calls pubsub_evsub_set_state - * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS + * pubsub_on_evsub_state checks state == TERMINATE_IN_PROGRESS * removes the subscriptions * cleans up references to evsub * sets state = TERMINATED @@ -3378,6 +3473,15 @@ static void set_state_terminated(struct ast_sip_subscription *sub) * serialized_pubsub_on_refresh_timeout starts * See (1) Above * + * * Transmission failure sending NOTIFY or error response from client + * pjproject transaction timer expires or non OK response + * pjproject locks dialog + * calls pubsub_on_evsub_state with event TSX_STATE + * pubsub_on_evsub_state checks event == TSX_STATE + * removes the subscriptions + * cleans up references to evsub + * sets state = TERMINATED + * pjproject unlocks dialog * * * ast_sip_subscription_notify is called * checks state == NORMAL @@ -3403,25 +3507,41 @@ static void set_state_terminated(struct ast_sip_subscription *sub) * * Although this function is called for every state change, we only care * about the TERMINATED state, and only when we're actually processing the final - * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS). In this case, we do all - * the subscription tree cleanup tasks and decrement the evsub reference. + * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS) OR when a transmission failure + * occurs (PJSIP_EVENT_TSX_STATE). In this case, we do all the subscription tree + * cleanup tasks and decrement the evsub reference. */ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event) { - struct sip_subscription_tree *sub_tree; + struct sip_subscription_tree *sub_tree = + pjsip_evsub_get_mod_data(evsub, pubsub_module.id); - ast_debug(3, "on_evsub_state called with state %s\n", pjsip_evsub_get_state_name(evsub)); + ast_debug(3, "evsub %p state %s event %s sub_tree %p sub_tree state %s\n", evsub, + pjsip_evsub_get_state_name(evsub), pjsip_event_str(event->type), sub_tree, + (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN")); - if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) { + if (!sub_tree || pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) { return; } - sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); - if (!sub_tree || sub_tree->state != SIP_SUB_TREE_TERMINATE_IN_PROGRESS) { - ast_debug(1, "Possible terminate race prevented %p\n", sub_tree); + /* It's easier to write this as what we WANT to process, then negate it. */ + if (!(sub_tree->state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS + || (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->state == SIP_SUB_TREE_NORMAL) + )) { + ast_debug(3, "Do nothing.\n"); return; } + if (sub_tree->expiration_task) { + char task_name[256]; + + ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name)); + ast_debug(3, "Cancelling timer: %s\n", task_name); + ast_sip_sched_task_cancel(sub_tree->expiration_task); + ao2_cleanup(sub_tree->expiration_task); + sub_tree->expiration_task = NULL; + } + remove_subscription(sub_tree); pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL); @@ -3443,16 +3563,17 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event) ao2_ref(sub_tree, -1); } -static int serialized_pubsub_on_refresh_timeout(void *userdata) +static int pubsub_on_refresh_timeout(void *userdata) { struct sip_subscription_tree *sub_tree = userdata; pjsip_dialog *dlg = sub_tree->dlg; + ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree, + (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN")); + pjsip_dlg_inc_lock(dlg); if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) { - ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree->evsub, sub_tree->state); pjsip_dlg_dec_lock(dlg); - ao2_cleanup(sub_tree); return 0; } @@ -3468,7 +3589,20 @@ static int serialized_pubsub_on_refresh_timeout(void *userdata) "Resource: %s", sub_tree->root->resource); pjsip_dlg_dec_lock(dlg); + + return 0; +} + +static int serialized_pubsub_on_refresh_timeout(void *userdata) +{ + struct sip_subscription_tree *sub_tree = userdata; + + ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree, + (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN")); + + pubsub_on_refresh_timeout(userdata); ao2_cleanup(sub_tree); + return 0; } @@ -3487,11 +3621,23 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata, struct sip_subscription_tree *sub_tree; sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); + ast_debug(3, "evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree, + (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN")); + if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) { - ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 ); return; } + if (sub_tree->expiration_task) { + char task_name[256]; + + ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name)); + ast_debug(3, "Cancelling timer: %s\n", task_name); + ast_sip_sched_task_cancel(sub_tree->expiration_task); + ao2_cleanup(sub_tree->expiration_task); + sub_tree->expiration_task = NULL; + } + /* PJSIP will set the evsub's state to terminated before calling into this function * if the Expires value of the incoming SUBSCRIBE is 0. */ @@ -3500,6 +3646,8 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata, sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING; } + subscription_persistence_update(sub_tree, rdata, SUBSCRIPTION_PERSISTENCE_REFRESHED); + if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_refresh_timeout, ao2_bump(sub_tree))) { /* If we can't push the NOTIFY refreshing task...we'll just go with it. */ ast_log(LOG_ERROR, "Failed to push task to send NOTIFY.\n"); @@ -3577,7 +3725,6 @@ static void pubsub_on_server_timeout(pjsip_evsub *evsub) sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id); if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) { - ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 ); return; } diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 3d124000a..5d7adcd43 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -2479,7 +2479,7 @@ static int rtp_address_is_ice_blacklisted(const pj_sockaddr_t *address) static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *addr, int port, int component, int transport) { - pj_sockaddr address[16]; + pj_sockaddr address[PJ_ICE_MAX_CAND]; unsigned int count = PJ_ARRAY_SIZE(address), pos = 0; int basepos = -1; diff --git a/tests/test_stream.c b/tests/test_stream.c index 110e4e4b8..5134cfb50 100644 --- a/tests/test_stream.c +++ b/tests/test_stream.c @@ -37,10 +37,11 @@ #include "asterisk/format.h" #include "asterisk/format_cap.h" #include "asterisk/format_cache.h" +#include "asterisk/channel.h" AST_TEST_DEFINE(stream_create) { - RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); switch (cmd) { case TEST_INIT: @@ -54,7 +55,7 @@ AST_TEST_DEFINE(stream_create) break; } - stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO); if (!stream) { ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); return AST_TEST_FAIL; @@ -80,7 +81,7 @@ AST_TEST_DEFINE(stream_create) AST_TEST_DEFINE(stream_create_no_name) { - RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); switch (cmd) { case TEST_INIT: @@ -94,7 +95,7 @@ AST_TEST_DEFINE(stream_create_no_name) break; } - stream = ast_stream_create(NULL, AST_MEDIA_TYPE_AUDIO); + stream = ast_stream_alloc(NULL, AST_MEDIA_TYPE_AUDIO); if (!stream) { ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); return AST_TEST_FAIL; @@ -105,7 +106,7 @@ AST_TEST_DEFINE(stream_create_no_name) AST_TEST_DEFINE(stream_set_type) { - RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); switch (cmd) { case TEST_INIT: @@ -119,7 +120,7 @@ AST_TEST_DEFINE(stream_set_type) break; } - stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO); if (!stream) { ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); return AST_TEST_FAIL; @@ -142,7 +143,7 @@ AST_TEST_DEFINE(stream_set_type) AST_TEST_DEFINE(stream_set_formats) { - RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); switch (cmd) { @@ -163,7 +164,7 @@ AST_TEST_DEFINE(stream_set_formats) return AST_TEST_FAIL; } - stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO); if (!stream) { ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); return AST_TEST_FAIL; @@ -188,7 +189,7 @@ AST_TEST_DEFINE(stream_set_formats) AST_TEST_DEFINE(stream_set_state) { - RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_free); switch (cmd) { case TEST_INIT: @@ -202,7 +203,7 @@ AST_TEST_DEFINE(stream_set_state) break; } - stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + stream = ast_stream_alloc("test", AST_MEDIA_TYPE_AUDIO); if (!stream) { ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); return AST_TEST_FAIL; @@ -225,7 +226,7 @@ AST_TEST_DEFINE(stream_set_state) AST_TEST_DEFINE(stream_topology_create) { - RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_destroy); + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); switch (cmd) { case TEST_INIT: @@ -239,7 +240,7 @@ AST_TEST_DEFINE(stream_topology_create) break; } - topology = ast_stream_topology_create(); + topology = ast_stream_topology_alloc(); if (!topology) { ast_test_status_update(test, "Failed to create media stream topology\n"); return AST_TEST_FAIL; @@ -250,8 +251,8 @@ AST_TEST_DEFINE(stream_topology_create) AST_TEST_DEFINE(stream_topology_clone) { - RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_destroy); - RAII_VAR(struct ast_stream_topology *, cloned, NULL, ast_stream_topology_destroy); + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); + RAII_VAR(struct ast_stream_topology *, cloned, NULL, ast_stream_topology_free); struct ast_stream *audio_stream, *video_stream; switch (cmd) { @@ -266,13 +267,13 @@ AST_TEST_DEFINE(stream_topology_clone) break; } - topology = ast_stream_topology_create(); + topology = ast_stream_topology_alloc(); if (!topology) { ast_test_status_update(test, "Failed to create media stream topology\n"); return AST_TEST_FAIL; } - audio_stream = ast_stream_create("audio", AST_MEDIA_TYPE_AUDIO); + audio_stream = ast_stream_alloc("audio", AST_MEDIA_TYPE_AUDIO); if (!audio_stream) { ast_test_status_update(test, "Failed to create an audio stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -280,11 +281,11 @@ AST_TEST_DEFINE(stream_topology_clone) if (ast_stream_topology_append_stream(topology, audio_stream) == -1) { ast_test_status_update(test, "Failed to append valid audio stream to stream topology\n"); - ast_stream_destroy(audio_stream); + ast_stream_free(audio_stream); return AST_TEST_FAIL; } - video_stream = ast_stream_create("video", AST_MEDIA_TYPE_VIDEO); + video_stream = ast_stream_alloc("video", AST_MEDIA_TYPE_VIDEO); if (!video_stream) { ast_test_status_update(test, "Failed to create a video stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -292,7 +293,7 @@ AST_TEST_DEFINE(stream_topology_clone) if (ast_stream_topology_append_stream(topology, video_stream) == -1) { ast_test_status_update(test, "Failed to append valid video stream to stream topology\n"); - ast_stream_destroy(video_stream); + ast_stream_free(video_stream); return AST_TEST_FAIL; } @@ -322,7 +323,7 @@ AST_TEST_DEFINE(stream_topology_clone) AST_TEST_DEFINE(stream_topology_append_stream) { - RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_destroy); + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); struct ast_stream *audio_stream, *video_stream; int position; @@ -338,13 +339,13 @@ AST_TEST_DEFINE(stream_topology_append_stream) break; } - topology = ast_stream_topology_create(); + topology = ast_stream_topology_alloc(); if (!topology) { ast_test_status_update(test, "Failed to create media stream topology\n"); return AST_TEST_FAIL; } - audio_stream = ast_stream_create("audio", AST_MEDIA_TYPE_AUDIO); + audio_stream = ast_stream_alloc("audio", AST_MEDIA_TYPE_AUDIO); if (!audio_stream) { ast_test_status_update(test, "Failed to create an audio stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -353,7 +354,7 @@ AST_TEST_DEFINE(stream_topology_append_stream) position = ast_stream_topology_append_stream(topology, audio_stream); if (position == -1) { ast_test_status_update(test, "Failed to append valid audio stream to stream topology\n"); - ast_stream_destroy(audio_stream); + ast_stream_free(audio_stream); return AST_TEST_FAIL; } else if (position != 0) { ast_test_status_update(test, "Appended audio stream to stream topology but position is '%d' instead of 0\n", @@ -378,7 +379,7 @@ AST_TEST_DEFINE(stream_topology_append_stream) return AST_TEST_FAIL; } - video_stream = ast_stream_create("video", AST_MEDIA_TYPE_VIDEO); + video_stream = ast_stream_alloc("video", AST_MEDIA_TYPE_VIDEO); if (!video_stream) { ast_test_status_update(test, "Failed to create a video stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -387,7 +388,7 @@ AST_TEST_DEFINE(stream_topology_append_stream) position = ast_stream_topology_append_stream(topology, video_stream); if (position == -1) { ast_test_status_update(test, "Failed to append valid video stream to stream topology\n"); - ast_stream_destroy(video_stream); + ast_stream_free(video_stream); return AST_TEST_FAIL; } else if (position != 1) { ast_test_status_update(test, "Appended video stream to stream topology but position is '%d' instead of 1\n", @@ -417,7 +418,7 @@ AST_TEST_DEFINE(stream_topology_append_stream) AST_TEST_DEFINE(stream_topology_set_stream) { - RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_destroy); + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); struct ast_stream *audio_stream, *video_stream; switch (cmd) { @@ -432,13 +433,13 @@ AST_TEST_DEFINE(stream_topology_set_stream) break; } - topology = ast_stream_topology_create(); + topology = ast_stream_topology_alloc(); if (!topology) { ast_test_status_update(test, "Failed to create media stream topology\n"); return AST_TEST_FAIL; } - audio_stream = ast_stream_create("audio", AST_MEDIA_TYPE_AUDIO); + audio_stream = ast_stream_alloc("audio", AST_MEDIA_TYPE_AUDIO); if (!audio_stream) { ast_test_status_update(test, "Failed to create an audio stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -446,7 +447,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) if (ast_stream_topology_set_stream(topology, 0, audio_stream)) { ast_test_status_update(test, "Failed to set an audio stream to a position where it is permitted\n"); - ast_stream_destroy(audio_stream); + ast_stream_free(audio_stream); return AST_TEST_FAIL; } @@ -467,7 +468,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) return AST_TEST_FAIL; } - video_stream = ast_stream_create("video", AST_MEDIA_TYPE_VIDEO); + video_stream = ast_stream_alloc("video", AST_MEDIA_TYPE_VIDEO); if (!video_stream) { ast_test_status_update(test, "Failed to create a video stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -475,7 +476,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) if (ast_stream_topology_set_stream(topology, 0, video_stream)) { ast_test_status_update(test, "Failed to set a video stream to a position where it is permitted\n"); - ast_stream_destroy(video_stream); + ast_stream_free(video_stream); return AST_TEST_FAIL; } @@ -496,7 +497,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) return AST_TEST_FAIL; } - audio_stream = ast_stream_create("audio", AST_MEDIA_TYPE_AUDIO); + audio_stream = ast_stream_alloc("audio", AST_MEDIA_TYPE_AUDIO); if (!audio_stream) { ast_test_status_update(test, "Failed to create an audio stream for testing stream topology\n"); return AST_TEST_FAIL; @@ -504,7 +505,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) if (ast_stream_topology_set_stream(topology, 1, audio_stream)) { ast_test_status_update(test, "Failed to set an audio stream to a position where it is permitted\n"); - ast_stream_destroy(audio_stream); + ast_stream_free(audio_stream); return AST_TEST_FAIL; } @@ -530,7 +531,7 @@ AST_TEST_DEFINE(stream_topology_set_stream) AST_TEST_DEFINE(stream_topology_create_from_format_cap) { - RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_destroy); + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); switch (cmd) { @@ -579,7 +580,7 @@ AST_TEST_DEFINE(stream_topology_create_from_format_cap) return AST_TEST_FAIL; } - ast_stream_topology_destroy(topology); + ast_stream_topology_free(topology); topology = NULL; ast_format_cap_append(caps, ast_format_h264, 0); @@ -611,6 +612,247 @@ AST_TEST_DEFINE(stream_topology_create_from_format_cap) return AST_TEST_PASS; } +AST_TEST_DEFINE(stream_topology_get_first_stream_by_type) +{ + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); + struct ast_stream *first_stream, *second_stream, *third_stream, *fourth_stream; + + switch (cmd) { + case TEST_INIT: + info->name = "stream_topology_get_first_stream_by_type"; + info->category = "/main/stream/"; + info->summary = "stream topology getting first stream by type unit test"; + info->description = + "Test that getting the first stream by type from a topology actually returns the first stream"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + topology = ast_stream_topology_alloc(); + if (!topology) { + ast_test_status_update(test, "Failed to create media stream topology\n"); + return AST_TEST_FAIL; + } + + first_stream = ast_stream_alloc("audio", AST_MEDIA_TYPE_AUDIO); + if (!first_stream) { + ast_test_status_update(test, "Failed to create an audio stream for testing stream topology\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_append_stream(topology, first_stream) == -1) { + ast_test_status_update(test, "Failed to append a perfectly good stream to a topology\n"); + ast_stream_free(first_stream); + return AST_TEST_FAIL; + } + + second_stream = ast_stream_alloc("audio2", AST_MEDIA_TYPE_AUDIO); + if (!second_stream) { + ast_test_status_update(test, "Failed to create a second audio stream for testing stream topology\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_append_stream(topology, second_stream) == -1) { + ast_test_status_update(test, "Failed to append a perfectly good stream to a topology\n"); + ast_stream_free(second_stream); + return AST_TEST_FAIL; + } + + third_stream = ast_stream_alloc("video", AST_MEDIA_TYPE_VIDEO); + if (!third_stream) { + ast_test_status_update(test, "Failed to create a video stream for testing stream topology\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_append_stream(topology, third_stream) == -1) { + ast_test_status_update(test, "Failed to append a perfectly good stream to a topology\n"); + ast_stream_free(third_stream); + return AST_TEST_FAIL; + } + + fourth_stream = ast_stream_alloc("video2", AST_MEDIA_TYPE_VIDEO); + if (!fourth_stream) { + ast_test_status_update(test, "Failed to create a second video stream for testing stream topology\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_append_stream(topology, fourth_stream) == -1) { + ast_test_status_update(test, "Failed to append a perfectly good stream to a topology\n"); + ast_stream_free(fourth_stream); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_get_first_stream_by_type(topology, AST_MEDIA_TYPE_AUDIO) != first_stream) { + ast_test_status_update(test, "Retrieved first audio stream from topology but it is not the correct one\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_topology_get_first_stream_by_type(topology, AST_MEDIA_TYPE_VIDEO) != third_stream) { + ast_test_status_update(test, "Retrieved first video stream from topology but it is not the correct one\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +static const struct ast_channel_tech mock_channel_tech = { +}; + +AST_TEST_DEFINE(stream_topology_create_from_channel_nativeformats) +{ + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); + RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); + struct ast_channel *mock_channel; + enum ast_test_result_state res = AST_TEST_FAIL; + struct ast_str *codec_have_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN); + struct ast_str *codec_wanted_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_topology_create_from_channel_nativeformats"; + info->category = "/main/stream/"; + info->summary = "stream topology creation from channel native formats unit test"; + info->description = + "Test that creating a stream topology from the setting of channel nativeformats results in the expected streams"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!caps) { + ast_test_status_update(test, "Could not allocate an empty format capabilities structure\n"); + return AST_TEST_FAIL; + } + + if (ast_format_cap_append(caps, ast_format_ulaw, 0)) { + ast_test_status_update(test, "Failed to append a ulaw format to capabilities for channel nativeformats\n"); + return AST_TEST_FAIL; + } + + if (ast_format_cap_append(caps, ast_format_alaw, 0)) { + ast_test_status_update(test, "Failed to append an alaw format to capabilities for channel nativeformats\n"); + return AST_TEST_FAIL; + } + + if (ast_format_cap_append(caps, ast_format_h264, 0)) { + ast_test_status_update(test, "Failed to append an h264 format to capabilities for channel nativeformats\n"); + return AST_TEST_FAIL; + } + + mock_channel = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, "TestChannel"); + if (!mock_channel) { + ast_test_status_update(test, "Failed to create a mock channel for testing\n"); + return AST_TEST_FAIL; + } + + ast_channel_tech_set(mock_channel, &mock_channel_tech); + ast_channel_nativeformats_set(mock_channel, caps); + + if (!ast_channel_get_stream_topology(mock_channel)) { + ast_test_status_update(test, "Set nativeformats with ulaw, alaw, and h264 on channel but it did not create a topology\n"); + goto end; + } + + if (ast_stream_topology_get_count(ast_channel_get_stream_topology(mock_channel)) != 2) { + ast_test_status_update(test, "Set nativeformats on a channel to ulaw, alaw, and h264 and received '%d' streams instead of expected 2\n", + ast_stream_topology_get_count(ast_channel_get_stream_topology(mock_channel))); + goto end; + } + + if (ast_stream_get_type(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 0)) != AST_MEDIA_TYPE_AUDIO) { + ast_test_status_update(test, "First stream on channel is of %s when it should be audio\n", + ast_codec_media_type2str(ast_stream_get_type(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 0)))); + goto end; + } + + ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_VIDEO); + if (!ast_format_cap_identical(ast_stream_get_formats(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 0)), caps)) { + ast_test_status_update(test, "Formats on audio stream of channel are '%s' when they should be '%s'\n", + ast_format_cap_get_names(ast_stream_get_formats(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 0)), &codec_have_buf), + ast_format_cap_get_names(caps, &codec_wanted_buf)); + goto end; + } + + if (ast_stream_get_type(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 1)) != AST_MEDIA_TYPE_VIDEO) { + ast_test_status_update(test, "Second stream on channel is of type %s when it should be video\n", + ast_codec_media_type2str(ast_stream_get_type(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 1)))); + goto end; + } + + ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_AUDIO); + + if (ast_format_cap_append(caps, ast_format_h264, 0)) { + ast_test_status_update(test, "Failed to append h264 video codec to capabilities for capabilities comparison\n"); + goto end; + } + + if (!ast_format_cap_identical(ast_stream_get_formats(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 1)), caps)) { + ast_test_status_update(test, "Formats on video stream of channel are '%s' when they should be '%s'\n", + ast_format_cap_get_names(ast_stream_get_formats(ast_stream_topology_get_stream(ast_channel_get_stream_topology(mock_channel), 1)), &codec_wanted_buf), + ast_format_cap_get_names(caps, &codec_wanted_buf)); + goto end; + } + + res = AST_TEST_PASS; + +end: + ast_channel_unlock(mock_channel); + ast_hangup(mock_channel); + + return res; +} + +static const struct ast_channel_tech mock_stream_channel_tech = { + .properties = AST_CHAN_TP_MULTISTREAM, +}; + +AST_TEST_DEFINE(stream_topology_channel_set) +{ + RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free); + struct ast_channel *mock_channel; + enum ast_test_result_state res = AST_TEST_PASS; + + switch (cmd) { + case TEST_INIT: + info->name = "stream_topology_channel_set"; + info->category = "/main/stream/"; + info->summary = "stream topology setting on a channel unit test"; + info->description = + "Test that setting a stream topology on a channel works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + topology = ast_stream_topology_alloc(); + if (!topology) { + ast_test_status_update(test, "Failed to create media stream topology\n"); + return AST_TEST_FAIL; + } + + mock_channel = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, "TestChannel"); + if (!mock_channel) { + ast_test_status_update(test, "Failed to create a mock channel for testing\n"); + return AST_TEST_FAIL; + } + + ast_channel_tech_set(mock_channel, &mock_stream_channel_tech); + ast_channel_set_stream_topology(mock_channel, topology); + + if (ast_channel_get_stream_topology(mock_channel) != topology) { + ast_test_status_update(test, "Set an explicit stream topology on a channel but the returned one did not match it\n"); + res = AST_TEST_FAIL; + } + + topology = NULL; + ast_channel_unlock(mock_channel); + ast_hangup(mock_channel); + + return res; +} + static int unload_module(void) { AST_TEST_UNREGISTER(stream_create); @@ -624,6 +866,9 @@ static int unload_module(void) AST_TEST_UNREGISTER(stream_topology_append_stream); AST_TEST_UNREGISTER(stream_topology_set_stream); AST_TEST_UNREGISTER(stream_topology_create_from_format_cap); + AST_TEST_UNREGISTER(stream_topology_get_first_stream_by_type); + AST_TEST_UNREGISTER(stream_topology_create_from_channel_nativeformats); + AST_TEST_UNREGISTER(stream_topology_channel_set); return 0; } @@ -639,6 +884,9 @@ static int load_module(void) AST_TEST_REGISTER(stream_topology_append_stream); AST_TEST_REGISTER(stream_topology_set_stream); AST_TEST_REGISTER(stream_topology_create_from_format_cap); + AST_TEST_REGISTER(stream_topology_get_first_stream_by_type); + AST_TEST_REGISTER(stream_topology_create_from_channel_nativeformats); + AST_TEST_REGISTER(stream_topology_channel_set); return AST_MODULE_LOAD_SUCCESS; } diff --git a/tests/test_voicemail_api.c b/tests/test_voicemail_api.c index 802b6bf1b..e46757263 100644 --- a/tests/test_voicemail_api.c +++ b/tests/test_voicemail_api.c @@ -825,12 +825,12 @@ static struct ast_channel *test_vm_api_create_mock_channel(void) } ast_channel_set_writeformat(mock_channel, ast_format_gsm); - native_formats = ast_channel_nativeformats(mock_channel); - ast_format_cap_append(native_formats, ast_channel_writeformat(mock_channel), 0); ast_channel_set_rawwriteformat(mock_channel, ast_format_gsm); ast_channel_set_readformat(mock_channel, ast_format_gsm); ast_channel_set_rawreadformat(mock_channel, ast_format_gsm); ast_channel_tech_set(mock_channel, &mock_channel_tech); + native_formats = ast_channel_nativeformats(mock_channel); + ast_format_cap_append(native_formats, ast_channel_writeformat(mock_channel), 0); ast_channel_unlock(mock_channel); diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4 index 8294d8ef9..d5c85317d 100644 --- a/third-party/pjproject/configure.m4 +++ b/third-party/pjproject/configure.m4 @@ -62,6 +62,7 @@ AC_DEFUN([_PJPROJECT_CONFIGURE], 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]) AC_DEFINE([HAVE_PJSIP_AUTH_CLT_DEINIT], 1, [Define if your system has pjsip_auth_clt_deinit declared.]) + AC_DEFINE([HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT], 1, [Define if your system has pjsip_evsub_set_uas_timeout declared.]) AC_SUBST([PJPROJECT_BUNDLED]) AC_SUBST([PJPROJECT_DIR]) diff --git a/third-party/pjproject/patches/0010-evsub-Add-pjsip_evsub_set_uas_timeout.patch b/third-party/pjproject/patches/0010-evsub-Add-pjsip_evsub_set_uas_timeout.patch new file mode 100644 index 000000000..a55aa0013 --- /dev/null +++ b/third-party/pjproject/patches/0010-evsub-Add-pjsip_evsub_set_uas_timeout.patch @@ -0,0 +1,84 @@ +From b7af9e6639f29feb4db6d0866c98e552b025ec96 Mon Sep 17 00:00:00 2001 +From: George Joseph <gjoseph@digium.com> +Date: Mon, 6 Feb 2017 15:39:29 -0700 +Subject: [PATCH] evsub: Add pjsip_evsub_set_uas_timeout. + +A UAS which needs to recreate incoming subscriptions from a persistent +store can call pjsip_dlg_create_uas_and_inc_lock and +pjsip_evsub_create_uas as long as they've persisted the +correct data but since the timer is triggered by an incoming subscribe, +it's never set and the subscription never expires. + +* Add pjsip_evsub_set_uas_timeout which is just a wrapper around + evsub.c:set_timeout(sub, TIMER_TYPE_UAS_TIMEOUT, seconds) + +* Also, fixed copy-paste error in pjsip_sub_state_hdr_print when + printing retry-after parameter. +--- + pjsip/include/pjsip-simple/evsub.h | 14 ++++++++++++++ + pjsip/src/pjsip-simple/evsub.c | 10 ++++++++++ + pjsip/src/pjsip-simple/evsub_msg.c | 2 +- + 3 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h +index 82e0a7c..45e6411 100644 +--- a/pjsip/include/pjsip-simple/evsub.h ++++ b/pjsip/include/pjsip-simple/evsub.h +@@ -511,6 +511,20 @@ PJ_DEF(pj_status_t) pjsip_evsub_add_ref(pjsip_evsub *sub); + PJ_DEF(pj_status_t) pjsip_evsub_dec_ref(pjsip_evsub *sub); + + ++/** ++ * Sets, resets or cancels the UAS subscription timeout. ++ * ++ * If there is an existing timer, it is cancelled before any ++ * other action. ++ * ++ * A timeout of 0 is ignored except that any existing timer ++ * is cancelled. ++ * ++ * @param sub The server subscription instance. ++ * @param seconds The new timeout. ++ */ ++PJ_DEF(void) pjsip_evsub_set_uas_timeout(pjsip_evsub *sub, pj_int32_t seconds); ++ + + PJ_END_DECL + +diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c +index 3fe4b49..6918a8c 100644 +--- a/pjsip/src/pjsip-simple/evsub.c ++++ b/pjsip/src/pjsip-simple/evsub.c +@@ -530,6 +530,16 @@ static void set_timer( pjsip_evsub *sub, int timer_id, + + + /* ++ * Set event subscription UAS timout. ++ */ ++PJ_DEF(void) pjsip_evsub_set_uas_timeout(pjsip_evsub *sub, pj_int32_t seconds) ++{ ++ PJ_ASSERT_RETURN(sub != NULL, PJ_EINVAL); ++ set_timer(sub, TIMER_TYPE_UAS_TIMEOUT, seconds); ++} ++ ++ ++/* + * Destructor. + */ + static void evsub_on_destroy(void *obj) +diff --git a/pjsip/src/pjsip-simple/evsub_msg.c b/pjsip/src/pjsip-simple/evsub_msg.c +index b44a715..b37db1c 100644 +--- a/pjsip/src/pjsip-simple/evsub_msg.c ++++ b/pjsip/src/pjsip-simple/evsub_msg.c +@@ -179,7 +179,7 @@ static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, + } + if (hdr->retry_after >= 0) { + pj_memcpy(p, ";retry-after=", 13); +- p += 9; ++ p += 13; + printed = pj_utoa(hdr->retry_after, p); + p += printed; + } +-- +2.9.3 + |