aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES43
-rw-r--r--UPGRADE.txt4
-rw-r--r--apps/app_confbridge.c234
-rw-r--r--apps/app_dial.c4
-rw-r--r--apps/app_meetme.c2
-rw-r--r--apps/app_queue.c377
-rw-r--r--apps/app_stack.c2
-rw-r--r--apps/app_voicemail.c6
-rw-r--r--apps/confbridge/conf_config_parser.c24
-rw-r--r--apps/confbridge/include/confbridge.h4
-rw-r--r--bridges/bridge_softmix.c76
-rwxr-xr-xbuild_tools/prep_tarball1
-rw-r--r--channels/chan_bridge.c3
-rw-r--r--channels/chan_dahdi.c83
-rw-r--r--channels/chan_gtalk.c2
-rw-r--r--channels/chan_iax2.c22
-rw-r--r--channels/chan_jingle.c2
-rw-r--r--channels/chan_mgcp.c2
-rw-r--r--channels/chan_sip.c821
-rw-r--r--channels/sip/include/sip.h13
-rw-r--r--channels/sip/reqresp_parser.c16
-rw-r--r--codecs/codec_speex.c2
-rw-r--r--configs/confbridge.conf.sample22
-rw-r--r--configs/queuerules.conf.sample2
-rw-r--r--configs/queues.conf.sample11
-rw-r--r--configs/sip.conf.sample2
-rwxr-xr-xcontrib/scripts/file.convert.sh20
-rw-r--r--funcs/func_aes.c2
-rw-r--r--funcs/func_speex.c2
-rw-r--r--include/asterisk/bridging.h61
-rw-r--r--include/asterisk/dsp.h5
-rw-r--r--include/asterisk/frame.h66
-rw-r--r--include/asterisk/logger.h2
-rw-r--r--include/asterisk/netsock2.h75
-rw-r--r--include/asterisk/pbx.h2
-rw-r--r--include/asterisk/res_fax.h7
-rw-r--r--main/asterisk.c6
-rw-r--r--main/bridging.c117
-rw-r--r--main/channel.c1
-rw-r--r--main/dsp.c29
-rw-r--r--main/features.c17
-rw-r--r--main/file.c11
-rw-r--r--main/frame.c4
-rw-r--r--main/manager.c162
-rw-r--r--main/netsock2.c15
-rw-r--r--main/pbx.c111
-rw-r--r--main/rtp_engine.c7
-rw-r--r--pbx/pbx_config.c100
-rw-r--r--pbx/pbx_dundi.c2
-rw-r--r--res/res_fax.c888
-rw-r--r--res/res_fax_spandsp.c309
-rw-r--r--res/res_jabber.c2
-rw-r--r--res/res_musiconhold.c13
-rw-r--r--res/res_timing_timerfd.c28
-rw-r--r--tests/test_netsock2.c125
55 files changed, 3252 insertions, 717 deletions
diff --git a/CHANGES b/CHANGES
index 1dfadcbf7..53587b598 100644
--- a/CHANGES
+++ b/CHANGES
@@ -41,6 +41,9 @@ Asterisk Manager Interface
Description field that is set by 'description' in the channel configuration
file.
* Added Uniqueid header to UserEvent.
+ * Added new action FilterAdd to control event filters for the current session.
+ This requires the system permission and uses the same filter syntax as
+ filters that can be defined in manager.conf
Asterisk HTTP Server
--------------------------
@@ -82,6 +85,8 @@ ConfBridge
* CONFBRIDGE_INFO dialplan function capable of retreiving information
about a conference such as locked status and number of parties, admins,
and marked users.
+ * Addition of video_mode option in confbridge.conf for adding video support
+ into a bridge profile.
Dialplan Variables
------------------
@@ -150,6 +155,39 @@ pbx_lua
stopped and restarted using the autoservice_stop() and autoservice_start()
functions.
+res_fax
+--------------------------
+ * The ReceiveFAXStatus and SendFAXStatus manager events have been consolidated
+ into a FAXStatus event with an 'Operation' header that will be either
+ 'send', 'receive', and 'gateway'.
+ * T.38 gateway functionality has been added to res_fax (and res_fax_spandsp).
+ Set FAXOPT(gateway)=yes to enable this functionality on a channel. This
+ feature will handle converting a fax call between an audio T.30 fax terminal
+ and an IFP T.38 fax terminal.
+
+SIP Changes
+-----------
+ * Add T38 support for REJECTED state where T.38 Negotiation is explicitly rejected.
+
+Queue changes
+-------------
+ * Added general option negative_penalty_invalid default off. when set
+ members are seen as invalid/logged out when there penalty is negative.
+ for realtime members when set remove from queue will set penalty to -1.
+ * Added queue option autopausedelay when autopause is enabled it will be
+ delayed for this number of seconds since last successful call if there
+ was no prior call the agent will be autopaused immediately.
+ * Added member option ignorebusy this when set and ringinuse is not
+ will allow per member control of multiple calls as ringinuse does for
+ the Queue.
+
+Applications
+------------
+ * Added 'v' option to MeetMe to play voicemail greetings when a user joins/leaves
+ a MeetMe conference
+ * Added ability to include '@parkinglot' to ParkedCall extension in order to specify
+ a specific parkinglot on which to search the extension.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ----------------
------------------------------------------------------------------------------
@@ -219,7 +257,6 @@ SIP Changes
res_stun_monitor module support in chan_sip.
* Addition of the 'auth_options_requests' option for turning on and off
authentication for OPTIONS requests in chan_sip.
- * Add T38 support for REJECTED state where T.38 Negotiation is explicitly rejected.
IAX2 Changes
@@ -324,10 +361,6 @@ Applications
notices a change.
* Voicemail now includes rdnis within msgXXXX.txt file.
* Added 'D' command to ExternalIVR full details in doc/externalivr.txt
- * Added 'v' option to MeetMe to play voicemail greetings when a user joins/leaves
- a MeetMe conference
- * Added ability to include '@parkinglot' to ParkedCall extension in order to specify
- a specific parkinglot on which to search the extension.
Dialplan Functions
------------------
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 3525621d3..dfa85684e 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -50,5 +50,9 @@ pbx_lua:
- the autoservice now defaults to being on by default
- autoservice_start() and autoservice_start() no longer return a value.
+Queue:
+ - Mark QUEUE_MEMBER_PENALTY Deprecated it never worked for realtime members
+ - QUEUE_MEMBER is now R/W supporting setting paused, ignorebusy and penalty.
+
===========================================================
===========================================================
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 177ee3717..d046c489f 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -77,6 +77,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
</description>
+ <see-also>
+ <ref type="application">ConfBridge</ref>
+ <ref type="function">CONFBRIDGE</ref>
+ <ref type="function">CONFBRIDGE_INFO</ref>
+ </see-also>
</application>
<function name="CONFBRIDGE" language="en_US">
<synopsis>
@@ -233,6 +238,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
</description>
</manager>
+ <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
+ <synopsis>
+ Set a conference user as the single video source distributed to all other participants.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Conference" required="true" />
+ <parameter name="Channel" required="true" />
+ </syntax>
+ <description>
+ </description>
+ </manager>
+
***/
/*!
@@ -547,9 +565,9 @@ static void send_leave_event(struct ast_channel *chan, const char *conf_name)
* \param (OPTIONAL) conference_bridge_user Caller
*
* \note if caller is NULL, the announcment will be sent to all participants in the conference.
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
@@ -557,14 +575,14 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
if (conference_bridge->users == 1) {
/* Awww we are the only person in the conference bridge */
- return;
+ return 0;
} else if (conference_bridge->users == 2) {
if (conference_bridge_user) {
/* Eep, there is one other person */
if (ast_stream_and_wait(conference_bridge_user->chan,
only_one,
"")) {
- return;
+ return -1;
}
} else {
play_sound_file(conference_bridge, only_one);
@@ -575,15 +593,15 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
if (ast_stream_and_wait(conference_bridge_user->chan,
there_are,
"")) {
- return;
+ return -1;
}
if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
- return;
+ return -1;
}
if (ast_stream_and_wait(conference_bridge_user->chan,
other_in_party,
"")) {
- return;
+ return -1;
}
} else {
play_sound_file(conference_bridge, there_are);
@@ -591,6 +609,7 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
play_sound_file(conference_bridge, other_in_party);
}
}
+ return 0;
}
/*!
@@ -600,15 +619,79 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
* \param chan Channel to play audio prompt to
* \param file Prompt to play
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*
* \note This function assumes that conference_bridge is locked
*/
-static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
+static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
{
+ int res;
ao2_unlock(conference_bridge);
- ast_stream_and_wait(chan, file, "");
+ res = ast_stream_and_wait(chan, file, "");
ao2_lock(conference_bridge);
+ return res;
+}
+
+static void handle_video_on_join(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ /* only automatically set video source for marked users */
+ if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
+ return;
+ }
+
+ if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
+ int set = 1;
+ struct conference_bridge_user *tmp_user = NULL;
+ ao2_lock(conference_bridge);
+ /* see if anyone is already the video src */
+ AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
+ if (tmp_user == conference_bridge_user) {
+ continue;
+ }
+ if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
+ set = 0;
+ break;
+ }
+ }
+ ao2_unlock(conference_bridge);
+ if (set) {
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
+ }
+ } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
+ /* we joined and are video capable, we override anyone else that may have already been the video feed */
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
+ }
+}
+
+static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ struct conference_bridge_user *tmp_user = NULL;
+
+ /* if this isn't a video source, nothing to update */
+ if (!ast_bridge_is_video_src(conference_bridge->bridge, conference_bridge_user->chan)) {
+ return;
+ }
+
+ ast_bridge_remove_video_src(conference_bridge->bridge, conference_bridge_user->chan);
+
+ /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
+ if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
+ !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
+ return;
+ }
+
+ /* Make the next avaliable marked user the video src. */
+ ao2_lock(conference_bridge);
+ AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
+ if (tmp_user == conference_bridge_user) {
+ continue;
+ }
+ if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
+ break;
+ }
+ }
+ ao2_unlock(conference_bridge);
}
/*!
@@ -617,16 +700,17 @@ static void play_prompt_to_channel(struct conference_bridge *conference_bridge,
* \param conference_bridge Conference bridge being joined
* \param conference_bridge_user Conference bridge user joining
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
struct conference_bridge_user *other_conference_bridge_user = NULL;
- /* If we are not the first marked user to join just bail out now */
+ /* If we are not the first user to join, then the users are already
+ * in the conference so we do not need to update them. */
if (conference_bridge->markedusers >= 2) {
- return;
+ return 0;
}
/* Iterate through every participant stopping MOH on them if need be */
@@ -661,19 +745,21 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
other_conference_bridge_user->features.mute = 0;
}
}
-
} else {
/* If a marked user already exists in the conference bridge we can just bail out now */
if (conference_bridge->markedusers) {
- return;
+ return 0;
}
/* Be sure we are muted so we can't talk to anybody else waiting */
conference_bridge_user->features.mute = 1;
/* If we have not been quieted play back that they are waiting for the leader */
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
- play_prompt_to_channel(conference_bridge,
+ if (play_prompt_to_channel(conference_bridge,
conference_bridge_user->chan,
- conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds));
+ conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
+ /* user hungup while the sound was playing */
+ return -1;
+ }
}
/* Start music on hold if needed */
/* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
@@ -684,6 +770,7 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
conference_bridge_user->playing_moh = 1;
}
}
+ return 0;
}
/*!
@@ -692,17 +779,20 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
* \param conference_bridge Conference bridge being joined
* \param conference_bridge_user Conference bridge user joining
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
/* Play back audio prompt and start MOH if need be if we are the first participant */
if (conference_bridge->users == 1) {
/* If audio prompts have not been quieted or this prompt quieted play it on out */
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
- play_prompt_to_channel(conference_bridge,
+ if (play_prompt_to_channel(conference_bridge,
conference_bridge_user->chan,
- conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds));
+ conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
+ /* user hungup while the sound was playing */
+ return -1;
+ }
}
/* If we need to start music on hold on the channel do so now */
/* We need to re-check the number of users in the conference bridge here because another conference bridge
@@ -712,13 +802,16 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
conference_bridge_user->playing_moh = 1;
}
- return;
+ return 0;
}
/* Announce number of users if need be */
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
ao2_unlock(conference_bridge);
- announce_user_count(conference_bridge, conference_bridge_user);
+ if (announce_user_count(conference_bridge, conference_bridge_user)) {
+ ao2_lock(conference_bridge);
+ return -1;
+ }
ao2_lock(conference_bridge);
}
@@ -737,9 +830,13 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
(conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
ao2_unlock(conference_bridge);
- announce_user_count(conference_bridge, NULL);
+ if (announce_user_count(conference_bridge, NULL)) {
+ ao2_lock(conference_bridge);
+ return -1;
+ }
ao2_lock(conference_bridge);
}
+ return 0;
}
/*!
@@ -772,6 +869,8 @@ static void destroy_conference_bridge(void *obj)
conf_bridge_profile_destroy(&conference_bridge->b_profile);
}
+static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
+
/*!
* \brief Join a conference bridge
*
@@ -840,6 +939,10 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
/* Set the internal mixing interval on the bridge from the bridge profile */
ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
+ if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
+ ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
+ }
+
/* Setup lock for playback channel */
ast_mutex_init(&conference_bridge->playback_lock);
@@ -876,9 +979,17 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
- post_join_marked(conference_bridge, conference_bridge_user);
+ if (post_join_marked(conference_bridge, conference_bridge_user)) {
+ ao2_unlock(conference_bridge);
+ leave_conference_bridge(conference_bridge, conference_bridge_user);
+ return NULL;
+ }
} else {
- post_join_unmarked(conference_bridge, conference_bridge_user);
+ if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
+ ao2_unlock(conference_bridge);
+ leave_conference_bridge(conference_bridge, conference_bridge_user);
+ return NULL;
+ }
}
/* check to see if recording needs to be started or not */
@@ -1335,14 +1446,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
ast_moh_stop(chan);
}
ast_stream_and_wait(chan, join_sound, "");
- if (conference_bridge_user.playing_moh) {
- ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
- }
ast_autoservice_start(chan);
play_sound_file(conference_bridge, join_sound);
ast_autoservice_stop(chan);
+ if (conference_bridge_user.playing_moh) {
+ ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
+ }
}
+ handle_video_on_join(conference_bridge, &conference_bridge_user);
+
/* Join our conference bridge for real */
send_join_event(conference_bridge_user.chan, conference_bridge->name);
ast_bridge_join(conference_bridge->bridge,
@@ -1352,6 +1465,9 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
&conference_bridge_user.tech_args);
send_leave_event(conference_bridge_user.chan, conference_bridge->name);
+
+ handle_video_on_exit(conference_bridge, &conference_bridge_user);
+
/* if this user has a intro, play it when leaving */
if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
ast_autoservice_start(chan);
@@ -1373,9 +1489,6 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
leave_conference_bridge(conference_bridge, &conference_bridge_user);
conference_bridge = NULL;
- /* Can't forget to clean up the features structure, or else we risk a memory leak */
- ast_bridge_features_cleanup(&conference_bridge_user.features);
-
/* If the user was kicked from the conference play back the audio prompt for it */
if (!quiet && conference_bridge_user.kicked) {
res = ast_stream_and_wait(chan,
@@ -1657,6 +1770,11 @@ static int execute_menu_entry(struct conference_bridge *conference_bridge,
break;
case MENU_ACTION_NOOP:
break;
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
+ ao2_lock(conference_bridge);
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
+ ao2_unlock(conference_bridge);
+ break;
}
}
return res;
@@ -2412,6 +2530,55 @@ static int action_confbridgestoprecord(struct mansession *s, const struct messag
return 0;
}
+static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
+{
+ const char *conference = astman_get_header(m, "Conference");
+ const char *channel = astman_get_header(m, "Channel");
+ struct conference_bridge_user *participant = NULL;
+ struct conference_bridge *bridge = NULL;
+ struct conference_bridge tmp;
+
+ if (ast_strlen_zero(conference)) {
+ astman_send_error(s, m, "No Conference name provided.");
+ return 0;
+ }
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "No channel name provided.");
+ return 0;
+ }
+ if (!ao2_container_count(conference_bridges)) {
+ astman_send_error(s, m, "No active conferences.");
+ return 0;
+ }
+
+ ast_copy_string(tmp.name, conference, sizeof(tmp.name));
+ bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
+ if (!bridge) {
+ astman_send_error(s, m, "No Conference by that name found.");
+ return 0;
+ }
+
+ /* find channel and set as video src. */
+ ao2_lock(bridge);
+ AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
+ if (!strncmp(channel, participant->chan->name, strlen(channel))) {
+ ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
+ break;
+ }
+ }
+ ao2_unlock(bridge);
+ ao2_ref(bridge, -1);
+
+ /* do not access participant after bridge unlock. We are just
+ * using this check to see if it was found or not */
+ if (!participant) {
+ astman_send_error(s, m, "No channel by that name found in conference.");
+ return 0;
+ }
+ astman_send_ack(s, m, "Conference single video source set.");
+ return 0;
+}
+
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
char *parse = NULL;
@@ -2543,6 +2710,7 @@ static int load_module(void)
res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
+ res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
conf_load_config(0);
return res;
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 25172c743..d3b8009de 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/aoc.h"
#include "asterisk/ccss.h"
#include "asterisk/indications.h"
+#include "asterisk/framehook.h"
/*** DOCUMENTATION
<application name="Dial" language="en_US">
@@ -631,7 +632,8 @@ END_OPTIONS );
OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | \
OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \
- !chan->audiohooks && !peer->audiohooks)
+ !chan->audiohooks && !peer->audiohooks && \
+ ast_framehook_list_is_empty(chan->framehooks) && ast_framehook_list_is_empty(peer->framehooks))
/*
* The list of active channels
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index dad293b6e..40f5b0ee4 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -371,7 +371,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Run admin <replaceable>command</replaceable> for a specific
- <replaceable>channel</replaceable> in any coference.</para>
+ <replaceable>channel</replaceable> in any conference.</para>
</description>
</application>
<application name="SLAStation" language="en_US">
diff --git a/apps/app_queue.c b/apps/app_queue.c
index be929c1dc..9442ce649 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -57,7 +57,7 @@
*/
/*** MODULEINFO
- <use>res_monitor</use>
+ <use type="module">res_monitor</use>
***/
#include "asterisk.h"
@@ -521,11 +521,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="count">
<para>Returns the total number of members for the specified queue.</para>
</enum>
+ <enum name="penalty">
+ <para>Gets or sets queue member penalty.</para>
+ </enum>
+ <enum name="paused">
+ <para>Gets or sets queue member paused status.</para>
+ </enum>
+ <enum name="ignorebusy">
+ <para>Gets or sets queue member ignorebusy.</para>
+ </enum>
</enumlist>
</parameter>
+ <parameter name="interface" required="false" />
</syntax>
<description>
- <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
+ <para>Allows access to queue counts [R] and member information [R/W].</para>
+ <para>
+ <replaceable>queuename</replaceable> is required for all operations
+ <replaceable>interface</replaceable> is required for all member operations.
+ </para>
</description>
<see-also>
<ref type="application">Queue</ref>
@@ -658,6 +672,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Gets or sets queue members penalty.</para>
+ <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
</description>
<see-also>
<ref type="application">Queue</ref>
@@ -680,7 +695,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Queues.
</synopsis>
<syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
</description>
@@ -934,6 +948,9 @@ static struct ast_event_sub *device_state_sub;
/*! \brief queues.conf [general] option */
static int update_cdr = 0;
+/*! \brief queues.conf [general] option */
+static int negative_penalty_invalid = 0;
+
enum queue_result {
QUEUE_UNKNOWN = 0,
QUEUE_TIMEOUT = 1,
@@ -1043,6 +1060,7 @@ struct member {
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
+ unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
};
enum empty_conditions {
@@ -1160,6 +1178,7 @@ struct call_queue {
int timeout; /*!< How long to wait for an answer */
int weight; /*!< Respective weight */
int autopause; /*!< Auto pause queue members if they fail to answer */
+ int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
/* Queue strategy things */
@@ -1190,9 +1209,10 @@ static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
static struct ao2_container *queues;
static void update_realtime_members(struct call_queue *q);
+static struct member *interface_exists(struct call_queue *q, const char *interface);
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
-static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
/*! \brief sets the QUEUESTATUS channel variable */
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
{
@@ -1698,6 +1718,7 @@ static void init_queue(struct call_queue *q)
q->numperiodicannounce = 0;
q->autopause = QUEUE_AUTOPAUSE_OFF;
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
+ q->autopausedelay = 0;
if (!q->members) {
if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
/* linear strategy depends on order, so we have to place all members in a single bucket */
@@ -1760,7 +1781,7 @@ static void clear_queue(struct call_queue *q)
* \retval 0 on success
* \note Call this with the rule_lists locked
*/
-static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
+static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
{
char *timestr, *maxstr, *minstr, *contentdup;
struct penalty_rule *rule = NULL, *rule_iter;
@@ -2003,6 +2024,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
q->montype = 1;
} else if (!strcasecmp(param, "autopause")) {
q->autopause = autopause2int(val);
+ } else if (!strcasecmp(param, "autopausedelay")) {
+ q->autopausedelay = atoi(val);
} else if (!strcasecmp(param, "maxlen")) {
q->maxlen = atoi(val);
if (q->maxlen < 0)
@@ -2081,7 +2104,9 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
int penalty = 0;
int paused = 0;
int found = 0;
+ int ignorebusy = 0;
+ const char *config_val;
const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
@@ -2095,8 +2120,11 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
if (penalty_str) {
penalty = atoi(penalty_str);
- if (penalty < 0)
+ if ((penalty < 0) && negative_penalty_invalid) {
+ return;
+ } else if (penalty < 0) {
penalty = 0;
+ }
}
if (paused_str) {
@@ -2105,31 +2133,39 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
paused = 0;
}
- /* Find member by realtime uniqueid and update */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
- m->dead = 0; /* Do not delete this one. */
- ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
- if (paused_str)
- m->paused = paused;
- if (strcasecmp(state_interface, m->state_interface)) {
- ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
- }
- m->penalty = penalty;
- found = 1;
- ao2_ref(m, -1);
- break;
- }
- ao2_ref(m, -1);
- }
+ if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
+ ignorebusy = ast_true(config_val);
+ } else {
+ ignorebusy = 1;
+ }
+
+ /* Find member by realtime uniqueid and update */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
+ m->dead = 0; /* Do not delete this one. */
+ ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
+ if (paused_str)
+ m->paused = paused;
+ if (strcasecmp(state_interface, m->state_interface)) {
+ ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+ }
+ m->penalty = penalty;
+ m->ignorebusy = ignorebusy;
+ found = 1;
+ ao2_ref(m, -1);
+ break;
+ }
+ ao2_ref(m, -1);
+ }
ao2_iterator_destroy(&mem_iter);
- /* Create a new member */
- if (!found) {
+ /* Create a new member */
+ if (!found) {
if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
m->dead = 0;
m->realtime = 1;
+ m->ignorebusy = ignorebusy;
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
ao2_link(q->members, m);
@@ -2873,16 +2909,24 @@ static int num_available_members(struct call_queue *q)
mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
switch (mem->status) {
- case AST_DEVICE_INUSE:
- if (!q->ringinuse)
+ case AST_DEVICE_INVALID:
+ case AST_DEVICE_UNAVAILABLE:
+ break;
+ case AST_DEVICE_INUSE:
+ case AST_DEVICE_BUSY:
+ case AST_DEVICE_RINGING:
+ case AST_DEVICE_RINGINUSE:
+ case AST_DEVICE_ONHOLD:
+ if ((!q->ringinuse) || (!mem->ignorebusy)) {
+ break;
+ }
+ /* else fall through */
+ case AST_DEVICE_NOT_INUSE:
+ case AST_DEVICE_UNKNOWN:
+ if (!mem->paused) {
+ avl++;
+ }
break;
- /* else fall through */
- case AST_DEVICE_NOT_INUSE:
- case AST_DEVICE_UNKNOWN:
- if (!mem->paused) {
- avl++;
- }
- break;
}
ao2_ref(mem, -1);
@@ -3010,38 +3054,54 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
char tech[256];
char *location;
const char *macrocontext, *macroexten;
+ enum ast_device_state newstate;
/* on entry here, we know that tmp->chan == NULL */
- if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
- (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
- ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
- (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
- if (qe->chan->cdr)
+ if (tmp->member->paused) {
+ ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
- (*busies)++;
return 0;
}
- if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
- ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
+ if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
+ (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
+ ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
+ (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
+ (*busies)++;
return 0;
}
- if (tmp->member->paused) {
- ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- return 0;
+ if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
+ if ((tmp->member->status == AST_DEVICE_UNKNOWN) || (tmp->member->status == AST_DEVICE_NOT_INUSE)) {
+ newstate = ast_parse_device_state(tmp->member->interface);
+ if (newstate != tmp->member->status) {
+ ast_log(LOG_ERROR, "Found a channel matching iterface %s while status was %i changed to %i\n",
+ tmp->member->interface, tmp->member->status, newstate);
+ update_status(qe->parent, tmp->member, newstate);
+ }
+ }
+ if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
+ ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr) {
+ ast_cdr_busy(qe->chan->cdr);
+ }
+ tmp->stillgoing = 0;
+ return 0;
+ }
}
+
if (use_weight && compare_weight(qe->parent,tmp->member)) {
ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
(*busies)++;
return 0;
@@ -3056,8 +3116,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
/* Request the peer */
tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
if (!tmp->chan) { /* If we can't, just go on to the next call */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
ao2_lock(qe->parent);
@@ -3396,6 +3457,18 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member
}
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
+ if (qe->parent->autopausedelay > 0) {
+ struct member *mem;
+ ao2_lock(qe->parent);
+ if ((mem = interface_exists(qe->parent, interface))) {
+ time_t idletime = time(&idletime)-mem->lastcall;
+ if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
+ ao2_unlock(qe->parent);
+ return;
+ }
+ }
+ ao2_unlock(qe->parent);
+ }
if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
@@ -4707,8 +4780,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
else
ast_moh_stop(qe->chan);
/* If appropriate, log that we have a destination channel */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_setdestchan(qe->chan->cdr, peer->name);
+ }
/* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) {
@@ -4788,10 +4862,11 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (mixmonapp) {
ast_debug(1, "Starting MixMonitor as requested.\n");
if (!monitorfilename) {
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
- else
+ } else {
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+ }
} else {
const char *m = monitorfilename;
for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
@@ -4858,12 +4933,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
/* We purposely lock the CDR so that pbx_exec does not update the application data */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
+ }
pbx_exec(qe->chan, mixmonapp, mixmonargs);
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
-
+ }
} else {
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
}
@@ -5180,7 +5256,10 @@ static int remove_from_queue(const char *queuename, const char *interface)
ao2_lock(q);
if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
/* XXX future changes should beware of this assumption!! */
- if (!mem->dynamic) {
+ /*Change Penalty on realtime users*/
+ if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
+ update_realtime_member_field(mem, q->name, "penalty", "-1");
+ } else if (!mem->dynamic) {
ao2_ref(mem, -1);
ao2_unlock(q);
queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
@@ -5355,35 +5434,34 @@ static int set_member_penalty(const char *queuename, const char *interface, int
int foundinterface = 0, foundqueue = 0;
struct call_queue *q;
struct member *mem;
- struct ao2_iterator queue_iter;
+ char rtpenalty[80];
- if (penalty < 0) {
+ if (penalty < 0 && !negative_penalty_invalid) {
ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
return RESULT_FAILURE;
}
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+ if ((q = load_realtime_queue(queuename))) {
+ foundqueue++;
ao2_lock(q);
- if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
- foundqueue++;
- if ((mem = interface_exists(q, interface))) {
- foundinterface++;
+ if ((mem = interface_exists(q, interface))) {
+ foundinterface++;
+ if (!mem->realtime) {
mem->penalty = penalty;
-
- ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "Penalty: %d\r\n",
- q->name, mem->interface, penalty);
- ao2_ref(mem, -1);
+ } else {
+ sprintf(rtpenalty,"%i", penalty);
+ update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
}
+ ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Penalty: %d\r\n",
+ q->name, mem->interface, penalty);
+ ao2_ref(mem, -1);
}
ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
}
- ao2_iterator_destroy(&queue_iter);
if (foundinterface) {
return RESULT_SUCCESS;
@@ -5769,7 +5847,6 @@ static void copy_rules(struct queue_ent *qe, const char *rulename)
struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
if (!new_pr) {
ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
- AST_LIST_UNLOCK(&rule_lists);
break;
}
new_pr->time = pr_iter->time;
@@ -6157,31 +6234,37 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
return 0;
}
-/*!
+/*!
* \brief Get number either busy / free / ready or total members of a specific queue
- * \retval number of members (busy / free / ready / total)
+ * \brief Get or set member properties penalty / paused / ignorebusy
+ * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
* \retval -1 on error
*/
-static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int count = 0;
struct member *m;
struct ao2_iterator mem_iter;
struct call_queue *q;
- char *option;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(option);
+ AST_APP_ARG(interface);
+ );
+ /* Make sure the returned value on error is zero length string. */
+ buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
- if ((option = strchr(data, ',')))
- *option++ = '\0';
- else
- option = "logged";
- if ((q = load_realtime_queue(data))) {
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if ((q = load_realtime_queue(args.queuename))) {
ao2_lock(q);
- if (!strcasecmp(option, "logged")) {
+ if (!strcasecmp(args.option, "logged")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
@@ -6191,7 +6274,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else if (!strcasecmp(option, "free")) {
+ } else if (!strcasecmp(args.option, "free")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
@@ -6201,7 +6284,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else if (!strcasecmp(option, "ready")) {
+ } else if (!strcasecmp(args.option, "ready")) {
time_t now;
time(&now);
mem_iter = ao2_iterator_init(q->members, 0);
@@ -6214,22 +6297,104 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else /* must be "count" */
+ } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
count = q->membercount;
+ } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->penalty;
+ } else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->paused;
+ } else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->ignorebusy;
+ }
ao2_unlock(q);
queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
+ } else {
+ ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
+ }
snprintf(buf, len, "%d", count);
return 0;
}
-/*!
+/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
+static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int memvalue;
+ struct call_queue *q;
+ struct member *m;
+ char rtvalue[80];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(option);
+ AST_APP_ARG(interface);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 3) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
+ ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
+ return -1;
+ }
+
+ memvalue = atoi(value);
+
+ if (!strcasecmp(args.option, "penalty")) {
+ /* if queuename = NULL then penalty will be set for interface in all the queues.*/
+ if (set_member_penalty(args.queuename, args.interface, memvalue)) {
+ ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+ return -1;
+ }
+ } else if ((q = load_realtime_queue(args.queuename))) {
+ ao2_lock(q);
+ if ((m = interface_exists(q, args.interface))) {
+ sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
+ if (!strcasecmp(args.option, "paused")) {
+ if (m->realtime) {
+ update_realtime_member_field(m, q->name, args.option, rtvalue);
+ } else {
+ m->paused = (memvalue <= 0) ? 0 : 1;
+ }
+ } else if (!strcasecmp(args.option, "ignorebusy")) {
+ if (m->realtime) {
+ update_realtime_member_field(m, q->name, args.option, rtvalue);
+ } else {
+ m->ignorebusy = (memvalue <= 0) ? 0 : 1;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
+ return -1;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Invalid interface or queue\n");
+ return -1;
+ }
+ ao2_unlock(q);
+ } else {
+ ast_log(LOG_ERROR, "Invalid queue\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
* \brief Get the total number of members in a specific queue (Deprecated)
- * \retval number of members
- * \retval -1 on error
+ * \retval number of members
+ * \retval -1 on error
*/
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
@@ -6435,7 +6600,8 @@ static struct ast_custom_function queuevar_function = {
static struct ast_custom_function queuemembercount_function = {
.name = "QUEUE_MEMBER",
- .read = queue_function_qac,
+ .read = queue_function_mem_read,
+ .write = queue_function_mem_write,
};
static struct ast_custom_function queuemembercount_dep = {
@@ -6494,6 +6660,7 @@ static int reload_queue_rules(int reload)
while ((rulecat = ast_category_browse(cfg, rulecat))) {
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
AST_LIST_UNLOCK(&rule_lists);
+ ast_config_destroy(cfg);
return AST_MODULE_LOAD_FAILURE;
} else {
ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
@@ -6517,8 +6684,9 @@ static void queue_set_global_params(struct ast_config *cfg)
{
const char *general_val = NULL;
queue_persistent_members = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
+ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
queue_persistent_members = ast_true(general_val);
+ }
autofill_default = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
autofill_default = ast_true(general_val);
@@ -6533,6 +6701,9 @@ static void queue_set_global_params(struct ast_config *cfg)
shared_lastcall = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
shared_lastcall = ast_true(general_val);
+ negative_penalty_invalid = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid")))
+ negative_penalty_invalid = ast_true(general_val);
}
/*! \brief reload information pertaining to a single member
@@ -7087,9 +7258,15 @@ static int manager_queues_show(struct mansession *s, const struct message *m)
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
{
const char *rule = astman_get_header(m, "Rule");
+ const char *id = astman_get_header(m, "ActionID");
struct rule_list *rl_iter;
struct penalty_rule *pr_iter;
+ astman_append(s, "Response: Success\r\n");
+ if (!ast_strlen_zero(id)) {
+ astman_append(s, "ActionID: %s\r\n", id);
+ }
+
AST_LIST_LOCK(&rule_lists);
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
@@ -7103,6 +7280,10 @@ static int manager_queue_rule_show(struct mansession *s, const struct message *m
}
AST_LIST_UNLOCK(&rule_lists);
+ /*
+ * Two blank lines instead of one because the Response and
+ * ActionID headers used to not be present.
+ */
astman_append(s, "\r\n\r\n");
return RESULT_SUCCESS;
diff --git a/apps/app_stack.c b/apps/app_stack.c
index 29c37a20b..10a1af48d 100644
--- a/apps/app_stack.c
+++ b/apps/app_stack.c
@@ -26,7 +26,7 @@
*/
/*** MODULEINFO
- <use>res_agi</use>
+ <use type="module">res_agi</use>
***/
#include "asterisk.h"
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 5e342bda0..971e1879d 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -38,8 +38,8 @@
*/
/*** MODULEINFO
- <use>res_adsi</use>
- <use>res_smdi</use>
+ <use type="module">res_adsi</use>
+ <use type="module">res_smdi</use>
***/
/*** MAKEOPTS
@@ -60,7 +60,7 @@
<depend>imap_tk</depend>
<conflict>ODBC_STORAGE</conflict>
<conflict>FILE_STORAGE</conflict>
- <use>openssl</use>
+ <use type="external">openssl</use>
<defaultenabled>no</defaultenabled>
</member>
</category>
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 3d1f31326..8864f52bc 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -284,6 +284,14 @@ static int set_bridge_option(const char *name, const char *value, struct bridge_
}
} else if (!strcasecmp(name, "record_conference")) {
ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE);
+ } else if (!strcasecmp(name, "video_mode")) {
+ if (!strcasecmp(value, "first_marked")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
+ } else if (!strcasecmp(value, "last_marked")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
+ } else if (!strcasecmp(value, "follow_talker")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
+ }
} else if (!strcasecmp(name, "max_members")) {
if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
return -1;
@@ -534,6 +542,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
case MENU_ACTION_ADMIN_KICK_LAST:
case MENU_ACTION_LEAVE:
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
break;
case MENU_ACTION_PLAYBACK:
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
@@ -649,6 +658,8 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
} else if (!strcasecmp(action, "leave_conference")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
+ } else if (!strcasecmp(action, "set_as_single_video_src")) {
+ res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
} else if (!strncasecmp(action, "dialplan_exec(", 14)) {
ast_copy_string(buf, action, sizeof(buf));
action_args = buf;
@@ -983,6 +994,16 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
ast_cli(a->fd,"Max Members: No Limit\n");
}
+ if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
+ ast_cli(a->fd, "Video Mode: last_marked\n");
+ } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
+ ast_cli(a->fd, "Video Mode: first_marked\n");
+ } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
+ ast_cli(a->fd, "Video Mode: follow_talker\n");
+ } else {
+ ast_cli(a->fd, "Video Mode: no video\n");
+ }
+
ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
@@ -1142,6 +1163,9 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s
case MENU_ACTION_LEAVE:
ast_cli(a->fd, "leave_conference");
break;
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
+ ast_cli(a->fd, "set_as_single_video_src");
+ break;
}
action_num++;
}
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index de467b5f7..7a2f6bb07 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -61,6 +61,9 @@ enum user_profile_flags {
enum bridge_profile_flags {
BRIDGE_OPT_RECORD_CONFERENCE = (1 << 0), /*!< Set if the conference should be recorded */
+ BRIDGE_OPT_VIDEO_SRC_LAST_MARKED = (1 << 1), /*!< Set if conference should feed video of last marked user to all participants. */
+ BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED = (1 << 2), /*!< Set if conference should feed video of first marked user to all participants. */
+ BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER = (1 << 3), /*!< Set if conference set the video feed to follow the loudest talker. */
};
enum conf_menu_action_id {
@@ -78,6 +81,7 @@ enum conf_menu_action_id {
MENU_ACTION_ADMIN_KICK_LAST,
MENU_ACTION_LEAVE,
MENU_ACTION_NOOP,
+ MENU_ACTION_SET_SINGLE_VIDEO_SRC,
};
/*! The conference menu action contains both
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 4350905fd..8828d640a 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -70,6 +70,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_SOFTMIX_SILENCE_THRESHOLD 2500
#define DEFAULT_SOFTMIX_TALKING_THRESHOLD 160
+#define DEFAULT_ENERGY_HISTORY_LEN 150
+
+struct video_follow_talker_data {
+ /*! audio energy history */
+ int energy_history[DEFAULT_ENERGY_HISTORY_LEN];
+ /*! The current slot being used in the history buffer, this
+ * increments and wraps around */
+ int energy_history_cur_slot;
+ /*! The current energy sum used for averages. */
+ int energy_accum;
+ /*! The current energy average */
+ int energy_average;
+};
+
/*! \brief Structure which contains per-channel mixing information */
struct softmix_channel {
/*! Lock to protect this structure */
@@ -93,6 +107,8 @@ struct softmix_channel {
short final_buf[MAX_DATALEN];
/*! Buffer containing only the audio from the channel */
short our_buf[MAX_DATALEN];
+ /*! Data pertaining to talker mode for video conferencing */
+ struct video_follow_talker_data video_talker;
};
struct softmix_bridge_data {
@@ -419,12 +435,24 @@ static void softmix_pass_dtmf(struct ast_bridge *bridge, struct ast_bridge_chann
}
}
+static void softmix_pass_video(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_bridge_channel *tmp;
+ AST_LIST_TRAVERSE(&bridge->channels, tmp, entry) {
+ if (tmp->suspended) {
+ continue;
+ }
+ ast_write(tmp->chan, frame);
+ }
+}
+
/*! \brief Function called when a channel writes a frame into the bridge */
static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
{
struct softmix_channel *sc = bridge_channel->bridge_pvt;
struct softmix_bridge_data *softmix_data = bridge->bridge_pvt;
int totalsilence = 0;
+ int cur_energy = 0;
int silence_threshold = bridge_channel->tech_args.silence_threshold ?
bridge_channel->tech_args.silence_threshold :
DEFAULT_SOFTMIX_SILENCE_THRESHOLD;
@@ -434,18 +462,52 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid
/* Only accept audio frames, all others are unsupported */
if (frame->frametype == AST_FRAME_DTMF_END || frame->frametype == AST_FRAME_DTMF_BEGIN) {
softmix_pass_dtmf(bridge, bridge_channel, frame);
- goto no_audio;
- } else if (frame->frametype != AST_FRAME_VOICE) {
+ goto bridge_write_cleanup;
+ } else if (frame->frametype != AST_FRAME_VOICE && frame->frametype != AST_FRAME_VIDEO) {
res = AST_BRIDGE_WRITE_UNSUPPORTED;
- goto no_audio;
+ goto bridge_write_cleanup;
} else if (frame->datalen == 0) {
- goto no_audio;
+ goto bridge_write_cleanup;
+ }
+
+ /* Determine if this video frame should be distributed or not */
+ if (frame->frametype == AST_FRAME_VIDEO) {
+ switch (bridge->video_mode.mode) {
+ case AST_BRIDGE_VIDEO_MODE_NONE:
+ break;
+ case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
+ if (ast_bridge_is_video_src(bridge, bridge_channel->chan)) {
+ softmix_pass_video(bridge, bridge_channel, frame);
+ }
+ break;
+ case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
+ ast_mutex_lock(&sc->lock);
+ ast_bridge_update_talker_src_video_mode(bridge, bridge_channel->chan, sc->video_talker.energy_average, ast_format_get_video_mark(&frame->subclass.format));
+ ast_mutex_unlock(&sc->lock);
+ if (ast_bridge_is_video_src(bridge, bridge_channel->chan)) {
+ softmix_pass_video(bridge, bridge_channel, frame);
+ }
+ break;
+ }
+ goto bridge_write_cleanup;
}
/* If we made it here, we are going to write the frame into the conference */
ast_mutex_lock(&sc->lock);
+ ast_dsp_silence_with_energy(sc->dsp, frame, &totalsilence, &cur_energy);
+
+ if (bridge->video_mode.mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC) {
+ int cur_slot = sc->video_talker.energy_history_cur_slot;
+ sc->video_talker.energy_accum -= sc->video_talker.energy_history[cur_slot];
+ sc->video_talker.energy_accum += cur_energy;
+ sc->video_talker.energy_history[cur_slot] = cur_energy;
+ sc->video_talker.energy_average = sc->video_talker.energy_accum / DEFAULT_ENERGY_HISTORY_LEN;
+ sc->video_talker.energy_history_cur_slot++;
+ if (sc->video_talker.energy_history_cur_slot == DEFAULT_ENERGY_HISTORY_LEN) {
+ sc->video_talker.energy_history_cur_slot = 0; /* wrap around */
+ }
+ }
- ast_dsp_silence(sc->dsp, frame, &totalsilence);
if (totalsilence < silence_threshold) {
if (!sc->talking) {
update_talking = 1;
@@ -487,7 +549,7 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid
return res;
-no_audio:
+bridge_write_cleanup:
/* Even though the frame is not being written into the conference because it is not audio,
* we should use this opportunity to check to see if a frame is ready to be written out from
* the conference to the channel. */
@@ -817,7 +879,7 @@ softmix_cleanup:
static struct ast_bridge_technology softmix_bridge = {
.name = "softmix",
- .capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE,
+ .capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE | AST_BRIDGE_CAPABILITY_VIDEO,
.preference = AST_BRIDGE_PREFERENCE_LOW,
.create = softmix_bridge_create,
.destroy = softmix_bridge_destroy,
diff --git a/build_tools/prep_tarball b/build_tools/prep_tarball
index 6f4dadb3a..3fbe6ca9f 100755
--- a/build_tools/prep_tarball
+++ b/build_tools/prep_tarball
@@ -6,7 +6,6 @@
# It will be executed from the top-level directory of the project.
make -C sounds MENUSELECT_CORE_SOUNDS=CORE-SOUNDS-EN-GSM MENUSELECT_MOH=MOH-OPSOUND-WAV WGET=wget DOWNLOAD=wget all
-make AWK=awk GREP=grep menuselect-tree
if ! which wikiexport.py 2>&1 > /dev/null ; then
echo
diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c
index a58cfcc59..7b01909ae 100644
--- a/channels/chan_bridge.c
+++ b/channels/chan_bridge.c
@@ -229,6 +229,9 @@ static struct ast_channel *bridge_request(const char *type, struct ast_format_ca
ast_format_copy(&p->input->rawwriteformat, &slin);
ast_format_copy(&p->output->rawwriteformat, &slin);
+ ast_answer(p->output);
+ ast_answer(p->input);
+
return p->input;
}
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 131279ea0..321be6625 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -38,12 +38,12 @@
*/
/*** MODULEINFO
- <use>res_smdi</use>
+ <use type="module">res_smdi</use>
<depend>dahdi</depend>
<depend>tonezone</depend>
- <use>pri</use>
- <use>ss7</use>
- <use>openr2</use>
+ <use type="external">pri</use>
+ <use type="external">ss7</use>
+ <use type="external">openr2</use>
***/
#include "asterisk.h"
@@ -9491,6 +9491,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
int features;
struct ast_str *chan_name;
struct ast_variable *v;
+ char *dashptr;
+ char device_name[AST_CHANNEL_NAME];
if (i->subs[idx].owner) {
ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
@@ -9672,7 +9674,13 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
/* Configure the new channel jb */
ast_jb_configure(tmp, &global_jbconf);
- ast_devstate_changed_literal(ast_state_chan2dev(state), tmp->name);
+ /* Set initial device state */
+ ast_copy_string(device_name, tmp->name, sizeof(device_name));
+ dashptr = strrchr(device_name, '-');
+ if (dashptr) {
+ *dashptr = '\0';
+ }
+ ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
for (v = i->vars ; v ; v = v->next)
pbx_builtin_setvar_helper(tmp, v->name, v->value);
@@ -13933,17 +13941,22 @@ static void *mfcr2_monitor(void *data)
#endif
static void dahdi_pri_message(struct pri *pri, char *s)
{
- int x, y;
- int dchan = -1, span = -1, dchancount = 0;
+ int x;
+ int y;
+ int dchan = -1;
+ int span = -1;
+ int dchancount = 0;
if (pri) {
for (x = 0; x < NUM_SPANS; x++) {
for (y = 0; y < SIG_PRI_NUM_DCHANS; y++) {
- if (pris[x].pri.dchans[y])
+ if (pris[x].pri.dchans[y]) {
dchancount++;
+ }
- if (pris[x].pri.dchans[y] == pri)
+ if (pris[x].pri.dchans[y] == pri) {
dchan = y;
+ }
}
if (dchan >= 0) {
span = x;
@@ -13951,14 +13964,18 @@ static void dahdi_pri_message(struct pri *pri, char *s)
}
dchancount = 0;
}
- if (dchancount > 1 && (span > -1))
- ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s);
- else if (span > -1)
- ast_verbose("%d %s", span+1, s);
- else
- ast_verbose("%s", s);
- } else
- ast_verbose("%s", s);
+ if (-1 < span) {
+ if (1 < dchancount) {
+ ast_verbose("[PRI Span: %d D-Channel: %d] %s", span + 1, dchan, s);
+ } else {
+ ast_verbose("PRI Span: %d %s", span + 1, s);
+ }
+ } else {
+ ast_verbose("PRI Span: ? %s", s);
+ }
+ } else {
+ ast_verbose("PRI Span: ? %s", s);
+ }
ast_mutex_lock(&pridebugfdlock);
@@ -13975,18 +13992,22 @@ static void dahdi_pri_message(struct pri *pri, char *s)
#if defined(HAVE_PRI)
static void dahdi_pri_error(struct pri *pri, char *s)
{
- int x, y;
- int dchan = -1, span = -1;
+ int x;
+ int y;
+ int dchan = -1;
+ int span = -1;
int dchancount = 0;
if (pri) {
for (x = 0; x < NUM_SPANS; x++) {
for (y = 0; y < SIG_PRI_NUM_DCHANS; y++) {
- if (pris[x].pri.dchans[y])
+ if (pris[x].pri.dchans[y]) {
dchancount++;
+ }
- if (pris[x].pri.dchans[y] == pri)
+ if (pris[x].pri.dchans[y] == pri) {
dchan = y;
+ }
}
if (dchan >= 0) {
span = x;
@@ -13994,14 +14015,18 @@ static void dahdi_pri_error(struct pri *pri, char *s)
}
dchancount = 0;
}
- if ((dchancount > 1) && (span > -1))
- ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s);
- else if (span > -1)
- ast_log(LOG_ERROR, "%d %s", span+1, s);
- else
- ast_log(LOG_ERROR, "%s", s);
- } else
- ast_log(LOG_ERROR, "%s", s);
+ if (-1 < span) {
+ if (1 < dchancount) {
+ ast_log(LOG_ERROR, "[PRI Span: %d D-Channel: %d] %s", span + 1, dchan, s);
+ } else {
+ ast_log(LOG_ERROR, "PRI Span: %d %s", span + 1, s);
+ }
+ } else {
+ ast_log(LOG_ERROR, "PRI Span: ? %s", s);
+ }
+ } else {
+ ast_log(LOG_ERROR, "PRI Span: ? %s", s);
+ }
ast_mutex_lock(&pridebugfdlock);
diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c
index d8dd736e4..3b08d8ce4 100644
--- a/channels/chan_gtalk.c
+++ b/channels/chan_gtalk.c
@@ -34,7 +34,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
<depend>res_jabber</depend>
- <use>openssl</use>
+ <use type="external">openssl</use>
***/
#include "asterisk.h"
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 0802474ee..9f5f32deb 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -32,7 +32,7 @@
*/
/*** MODULEINFO
- <use>crypto</use>
+ <use type="external">crypto</use>
***/
#include "asterisk.h"
@@ -5363,10 +5363,6 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
/* these two cannot be sent, because they require a result */
errno = ENOSYS;
return -1;
- case AST_OPTION_FORMAT_READ:
- case AST_OPTION_FORMAT_WRITE:
- case AST_OPTION_MAKE_COMPATIBLE:
- return -1;
case AST_OPTION_OPRMODE:
errno = EINVAL;
return -1;
@@ -5383,7 +5379,16 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
ast_mutex_unlock(&iaxsl[callno]);
return 0;
}
- default:
+ /* These options are sent to the other side across the network where
+ * they will be passed to whatever channel is bridged there. Don't
+ * do anything silly like pass an option that transmits pointers to
+ * memory on this machine to a remote machine to use */
+ case AST_OPTION_TONE_VERIFY:
+ case AST_OPTION_TDD:
+ case AST_OPTION_RELAXDTMF:
+ case AST_OPTION_AUDIO_MODE:
+ case AST_OPTION_DIGIT_DETECT:
+ case AST_OPTION_FAX_DETECT:
{
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
struct chan_iax2_pvt *pvt;
@@ -5411,7 +5416,12 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
ast_free(h);
return res;
}
+ default:
+ return -1;
}
+
+ /* Just in case someone does a break instead of a return */
+ return -1;
}
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen)
diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c
index d0a027c3d..53b1a85e4 100644
--- a/channels/chan_jingle.c
+++ b/channels/chan_jingle.c
@@ -30,7 +30,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
<depend>res_jabber</depend>
- <use>openssl</use>
+ <use type="external">openssl</use>
***/
#include "asterisk.h"
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 293a2c1fe..bd92fe9b0 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -30,7 +30,7 @@
*/
/*** MODULEINFO
- <use>res_pktccops</use>
+ <use type="module">res_pktccops</use>
***/
#include "asterisk.h"
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 4c0e2a66c..18eba2371 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -162,7 +162,7 @@
*/
/*** MODULEINFO
- <use>res_crypto</use>
+ <use type="module">res_crypto</use>
<depend>chan_local</depend>
***/
@@ -1121,9 +1121,10 @@ static void temp_pvt_cleanup(void *);
/*! \brief A per-thread temporary pvt structure */
AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup);
-/*! \brief Authentication list for realm authentication
- * \todo Move the sip_auth list to AST_LIST */
-static struct sip_auth *authl = NULL;
+/*! \brief Authentication container for realm authentication */
+static struct sip_auth_container *authl = NULL;
+/*! \brief Global authentication container protection while adjusting the references. */
+AST_MUTEX_DEFINE_STATIC(authl_lock);
/* --- Sockets and networking --------------*/
@@ -1230,8 +1231,8 @@ static int get_address_family_filter(const struct ast_sockaddr *addr);
/*--- Transmitting responses and requests */
static int sipsock_read(int *id, int fd, short events, void *ignore);
-static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len);
-static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod);
+static int __sip_xmit(struct sip_pvt *p, struct ast_str *data);
+static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int fatal, int sipmethod);
static void add_cc_call_info_to_response(struct sip_pvt *p, struct sip_request *resp);
static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
static int retrans_pkt(const void *data);
@@ -1329,13 +1330,11 @@ static int add_sip_domain(const char *domain, const enum domain_mode mode, const
static void clear_sip_domains(void);
/*--- SIP realm authentication */
-static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno);
-static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */
-static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm);
+static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno);
+static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm);
/*--- Misc functions */
static void check_rtp_timeout(struct sip_pvt *dialog, time_t t);
-static int sip_do_reload(enum channelreloadreason reason);
static int reload_config(enum channelreloadreason reason);
static int expire_register(const void *data);
static void *do_monitor(void *data);
@@ -1471,7 +1470,7 @@ static int method_match(enum sipmethod id, const char *name);
static void parse_copy(struct sip_request *dst, const struct sip_request *src);
static const char *find_alias(const char *name, const char *_default);
static const char *__get_header(const struct sip_request *req, const char *name, int *start);
-static int lws2sws(char *msgbuf, int len);
+static void lws2sws(struct ast_str *msgbuf);
static void extract_uri(struct sip_pvt *p, struct sip_request *req);
static char *remove_uri_parameters(char *uri);
static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
@@ -1549,7 +1548,6 @@ static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest
static void handle_response_refer(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response_subscribe(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static int handle_response_register(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
-static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
/*------ SRTP Support -------- */
@@ -2639,7 +2637,7 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
req.socket.fd = tcptls_session->fd;
/* Read in headers one line at a time */
- while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
+ while (ast_str_strlen(req.data) < 4 || strncmp(REQ_OFFSET_TO_STR(&req, data->used - 4), "\r\n\r\n", 4)) {
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
@@ -2686,7 +2684,6 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
goto cleanup;
}
ast_str_append(&req.data, 0, "%s", buf);
- req.len = req.data->used;
}
copy_request(&reqcpy, &req);
parse_request(&reqcpy);
@@ -2739,7 +2736,6 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
}
cl -= strlen(buf);
ast_str_append(&req.data, 0, "%s", buf);
- req.len = req.data->used;
}
}
/*! \todo XXX If there's no Content-Length or if the content-length and what
@@ -3342,13 +3338,18 @@ static inline const char *get_transport_pvt(struct sip_pvt *p)
return get_transport(p->socket.type);
}
-/*! \brief Transmit SIP message
- Sends a SIP request or response on a given socket (in the pvt)
- Called by retrans_pkt, send_request, send_response and
- __sip_reliable_xmit
- \return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
-*/
-static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len)
+/*!
+ * \internal
+ * \brief Transmit SIP message
+ *
+ * \details
+ * Sends a SIP request or response on a given socket (in the pvt)
+ * \note
+ * Called by retrans_pkt, send_request, send_response and __sip_reliable_xmit
+ *
+ * \return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
+ */
+static int __sip_xmit(struct sip_pvt *p, struct ast_str *data)
{
int res = 0;
const struct ast_sockaddr *dst = sip_real_dst(p);
@@ -3360,9 +3361,9 @@ static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len)
}
if (p->socket.type == SIP_TRANSPORT_UDP) {
- res = ast_sendto(p->socket.fd, data->str, len, 0, dst);
+ res = ast_sendto(p->socket.fd, data->str, ast_str_strlen(data), 0, dst);
} else if (p->socket.tcptls_session) {
- res = sip_tcptls_write(p->socket.tcptls_session, data->str, len);
+ res = sip_tcptls_write(p->socket.tcptls_session, data->str, ast_str_strlen(data));
} else {
ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
return XMIT_ERROR;
@@ -3378,8 +3379,8 @@ static int __sip_xmit(struct sip_pvt *p, struct ast_str *data, int len)
res = XMIT_ERROR; /* Don't bother with trying to transmit again */
}
}
- if (res != len) {
- ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, ast_sockaddr_stringify(dst), res, strerror(errno));
+ if (res != ast_str_strlen(data)) {
+ ast_log(LOG_WARNING, "sip_xmit of %p (len %zu) to %s returned %d: %s\n", data, ast_str_strlen(data), ast_sockaddr_stringify(dst), res, strerror(errno));
}
return res;
@@ -3394,7 +3395,7 @@ static void build_via(struct sip_pvt *p)
/* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */
snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s;branch=z9hG4bK%08x%s",
get_transport_pvt(p),
- ast_sockaddr_stringify(&p->ourip),
+ ast_sockaddr_stringify_remote(&p->ourip),
(int) p->branch, rport);
}
@@ -3626,7 +3627,7 @@ static int retrans_pkt(const void *data)
}
append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str);
- xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
+ xmitres = __sip_xmit(pkt->owner, pkt->data);
/* If there was no error during the network transmission, schedule the next retransmission,
* but if the next retransmission is going to be beyond our timeout period, mark the packet's
@@ -3745,10 +3746,12 @@ static int retrans_pkt(const void *data)
return 0;
}
-/*! \brief Transmit packet with retransmits
- \return 0 on success, -1 on failure to allocate packet
-*/
-static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int len, int fatal, int sipmethod)
+/*!
+ * \internal
+ * \brief Transmit packet with retransmits
+ * \return 0 on success, -1 on failure to allocate packet
+ */
+static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int fatal, int sipmethod)
{
struct sip_pkt *pkt = NULL;
int siptimer_a = DEFAULT_RETRANS;
@@ -3764,7 +3767,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
/*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
- xmitres = __sip_xmit(p, data, len); /* Send packet */
+ xmitres = __sip_xmit(p, data); /* Send packet */
if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
return AST_FAILURE;
@@ -3777,12 +3780,11 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
return AST_FAILURE;
}
/* copy data, add a terminator and save length */
- if (!(pkt->data = ast_str_create(len))) {
+ if (!(pkt->data = ast_str_create(ast_str_strlen(data)))) {
ast_free(pkt);
return AST_FAILURE;
}
ast_str_set(&pkt->data, 0, "%s%s", data->str, "\0");
- pkt->packetlen = len;
/* copy other parameters from the caller */
pkt->method = sipmethod;
pkt->seqno = seqno;
@@ -3812,7 +3814,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid);
}
- xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */
+ xmitres = __sip_xmit(pkt->owner, pkt->data); /* Send packet */
if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
@@ -4097,7 +4099,6 @@ static void add_blank(struct sip_request *req)
if (!req->lines) {
/* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
ast_str_append(&req->data, 0, "\r\n");
- req->len = ast_str_strlen(req->data);
}
}
@@ -4170,8 +4171,8 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty
}
res = (reliable) ?
- __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
- __sip_xmit(p, req->data, req->len);
+ __sip_reliable_xmit(p, seqno, 1, req->data, (reliable == XMIT_CRITICAL), req->method) :
+ __sip_xmit(p, req->data);
deinit_req(req);
if (res > 0) {
return 0;
@@ -4179,9 +4180,11 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty
return res;
}
-/*! \brief Send SIP Request to the other part of the dialogue
- \return see \ref __sip_xmit
-*/
+/*!
+ * \internal
+ * \brief Send SIP Request to the other part of the dialogue
+ * \return see \ref __sip_xmit
+ */
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
{
int res;
@@ -4209,8 +4212,8 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittyp
deinit_req(&tmp);
}
res = (reliable) ?
- __sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
- __sip_xmit(p, req->data, req->len);
+ __sip_reliable_xmit(p, seqno, 0, req->data, (reliable == XMIT_CRITICAL), req->method) :
+ __sip_xmit(p, req->data);
deinit_req(req);
return res;
}
@@ -4438,8 +4441,11 @@ static const char *sip_get_callid(struct ast_channel *chan)
return chan->tech_pvt ? ((struct sip_pvt *) chan->tech_pvt)->callid : "";
}
-/*! \brief Send SIP MESSAGE text within a call
- Called from PBX core sendtext() application */
+/*!
+ * \internal
+ * \brief Send SIP MESSAGE text within a call
+ * \note Called from PBX core sendtext() application
+ */
static int sip_sendtext(struct ast_channel *ast, const char *text)
{
struct sip_pvt *dialog = ast->tech_pvt;
@@ -4609,8 +4615,10 @@ static void sip_destroy_peer(struct sip_peer *peer)
ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
} else
ast_atomic_fetchadd_int(&speerobjs, -1);
- clear_realm_authentication(peer->auth);
- peer->auth = NULL;
+ if (peer->auth) {
+ ao2_t_ref(peer->auth, -1, "Removing peer authentication");
+ peer->auth = NULL;
+ }
if (peer->dnsmgr)
ast_dnsmgr_release(peer->dnsmgr);
clear_peer_mailboxes(peer);
@@ -5111,6 +5119,8 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
*/
static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
{
+ struct sip_auth_container *credentials;
+
/* this checks that the dialog is contacting the peer on a valid
* transport type based on the peers transport configuration,
* otherwise, this function bails out */
@@ -5199,12 +5209,26 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
dialog->allowtransfer = peer->allowtransfer;
dialog->jointnoncodeccapability = dialog->noncodeccapability;
dialog->rtptimeout = peer->rtptimeout;
- dialog->peerauth = peer->auth;
+
+ /* Update dialog authorization credentials */
+ ao2_lock(peer);
+ credentials = peer->auth;
+ if (credentials) {
+ ao2_t_ref(credentials, +1, "Ref peer auth for dialog");
+ }
+ ao2_unlock(peer);
+ ao2_lock(dialog);
+ if (dialog->peerauth) {
+ ao2_t_ref(dialog->peerauth, -1, "Unref old dialog peer auth");
+ }
+ dialog->peerauth = credentials;
+ ao2_unlock(dialog);
+
dialog->maxcallbitrate = peer->maxcallbitrate;
dialog->disallowed_methods = peer->disallowed_methods;
ast_cc_copy_config_params(dialog->cc_params, peer->cc_params);
if (ast_strlen_zero(dialog->tohost))
- ast_string_field_set(dialog, tohost, ast_sockaddr_stringify_host(&dialog->sa));
+ ast_string_field_set(dialog, tohost, ast_sockaddr_stringify_host_remote(&dialog->sa));
if (!ast_strlen_zero(peer->fromdomain)) {
ast_string_field_set(dialog, fromdomain, peer->fromdomain);
if (!dialog->initreq.headers) {
@@ -5464,7 +5488,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
return -1;
}
- if (p->trtp && !p->vsrtp && setup_srtp(&p->tsrtp) < 0) {
+ if (p->trtp && !p->tsrtp && setup_srtp(&p->tsrtp) < 0) {
ast_log(LOG_WARNING, "SRTP text setup failed\n");
return -1;
}
@@ -5726,6 +5750,12 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
ao2_ref(p->socket.tcptls_session, -1);
p->socket.tcptls_session = NULL;
}
+
+ if (p->peerauth) {
+ ao2_t_ref(p->peerauth, -1, "Removing active peer authentication");
+ p->peerauth = NULL;
+ }
+
p->caps = ast_format_cap_destroy(p->caps);
p->jointcaps = ast_format_cap_destroy(p->jointcaps);
p->peercaps = ast_format_cap_destroy(p->peercaps);
@@ -7350,7 +7380,7 @@ static char *generate_uri(struct sip_pvt *pvt, char *buf, size_t size)
* use the handy random string generation function we already have
*/
ast_str_append(&uri, 0, "%s", generate_random_string(buf, size));
- ast_str_append(&uri, 0, "@%s", ast_sockaddr_stringify(&pvt->ourip));
+ ast_str_append(&uri, 0, "@%s", ast_sockaddr_stringify_remote(&pvt->ourip));
ast_copy_string(buf, ast_str_buffer(uri), size);
return buf;
}
@@ -7360,7 +7390,7 @@ static void build_callid_pvt(struct sip_pvt *pvt)
{
char buf[33];
- const char *host = S_OR(pvt->fromdomain, ast_sockaddr_stringify(&pvt->ourip));
+ const char *host = S_OR(pvt->fromdomain, ast_sockaddr_stringify_remote(&pvt->ourip));
ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
@@ -7371,7 +7401,7 @@ static void build_callid_registry(struct sip_registry *reg, const struct ast_soc
{
char buf[33];
- const char *host = S_OR(fromdomain, ast_sockaddr_stringify_host(ourip));
+ const char *host = S_OR(fromdomain, ast_sockaddr_stringify_host_remote(ourip));
ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
}
@@ -8291,8 +8321,10 @@ static unsigned int set_pvt_allowed_methods(struct sip_pvt *pvt, struct sip_requ
/*! \brief Parse multiline SIP headers into one header
This is enabled if pedanticsipchecking is enabled */
-static int lws2sws(char *msgbuf, int len)
+static void lws2sws(struct ast_str *data)
{
+ char *msgbuf = data->str;
+ int len = ast_str_strlen(data);
int h = 0, t = 0;
int lws = 0;
@@ -8332,7 +8364,7 @@ static int lws2sws(char *msgbuf, int len)
lws = 0;
}
msgbuf[t] = '\0';
- return t;
+ data->used = t;
}
/*! \brief Parse a SIP message
@@ -8708,11 +8740,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
memset(p->offered_media, 0, sizeof(p->offered_media));
-
- /* default: novideo and notext set */
- p->novideo = TRUE;
- p->notext = TRUE;
-
if (p->vrtp) {
ast_rtp_codecs_payloads_clear(&newvideortp, NULL);
}
@@ -8770,7 +8797,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_debug(3, "Processing session-level SDP %c=%s... %s\n", type, value, (processed == TRUE)? "OK." : "UNSUPPORTED.");
}
-
+ /* default: novideo and notext set */
+ p->novideo = TRUE;
+ p->notext = TRUE;
/* Scan media stream (m=) specific parameters loop */
while (!ast_strlen_zero(nextm)) {
@@ -8789,8 +8818,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
nextm = get_sdp_iterate(&next, req, "m");
/* Search for audio media definition */
- if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0)) {
+ if ((sscanf(m, "audio %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0 && x) ||
+ (sscanf(m, "audio %30u RTP/%4s %n", &x, protocol, &len) == 2 && len > 0 && x)) {
if (!strcmp(protocol, "SAVP")) {
secure_audio = 1;
} else if (strcmp(protocol, "AVP")) {
@@ -8817,8 +8846,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_rtp_codecs_payloads_set_m_type(&newaudiortp, NULL, codec);
}
/* Search for video media definition */
- } else if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len >= 0)) {
+ } else if ((sscanf(m, "video %30u/%30u RTP/%4s %n", &x, &numberofports, protocol, &len) == 3 && len > 0 && x) ||
+ (sscanf(m, "video %30u RTP/%4s %n", &x, protocol, &len) == 2 && len >= 0 && x)) {
if (!strcmp(protocol, "SAVP")) {
secure_video = 1;
} else if (strcmp(protocol, "AVP")) {
@@ -8845,8 +8874,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_rtp_codecs_payloads_set_m_type(&newvideortp, NULL, codec);
}
/* Search for text media definition */
- } else if ((sscanf(m, "text %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
- (sscanf(m, "text %30u RTP/AVP %n", &x, &len) == 1 && len > 0)) {
+ } else if ((sscanf(m, "text %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0 && x) ||
+ (sscanf(m, "text %30u RTP/AVP %n", &x, &len) == 1 && len > 0 && x)) {
text = TRUE;
p->notext = FALSE;
p->offered_media[SDP_TEXT].offered = TRUE;
@@ -8867,8 +8896,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
ast_rtp_codecs_payloads_set_m_type(&newtextrtp, NULL, codec);
}
/* Search for image media definition */
- } else if (p->udptl && ((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0) ||
- (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0) )) {
+ } else if (p->udptl && ((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0 && x) ||
+ (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0 && x) )) {
image = TRUE;
if (debug)
ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
@@ -9707,9 +9736,8 @@ static int add_header(struct sip_request *req, const char *var, const char *valu
}
ast_str_append(&req->data, 0, "%s: %s\r\n", var, value);
- req->header[req->headers] = req->len;
+ req->header[req->headers] = ast_str_strlen(req->data);
- req->len = ast_str_strlen(req->data);
req->headers++;
return 0;
@@ -9743,7 +9771,6 @@ static int finalize_content(struct sip_request *req)
if (ast_str_strlen(req->content)) {
ast_str_append(&req->data, 0, "\r\n%s", ast_str_buffer(req->content));
- req->len = ast_str_strlen(req->data);
}
req->lines = ast_str_strlen(req->content) ? 1 : 0;
return 0;
@@ -9839,13 +9866,13 @@ static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const st
/* Add rport to first VIA header if requested */
snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s",
- leftmost, ast_sockaddr_stringify_addr(&p->recv),
+ leftmost, ast_sockaddr_stringify_addr_remote(&p->recv),
ast_sockaddr_port(&p->recv),
others ? "," : "", others ? others : "");
} else {
/* We should *always* add a received to the topmost via */
snprintf(new, sizeof(new), "%s;received=%s%s%s",
- leftmost, ast_sockaddr_stringify_addr(&p->recv),
+ leftmost, ast_sockaddr_stringify_addr_remote(&p->recv),
others ? "," : "", others ? others : "");
}
oh = new; /* the header to copy */
@@ -9981,7 +10008,6 @@ static int init_resp(struct sip_request *resp, const char *msg)
goto e_free_data;
resp->header[0] = 0;
ast_str_set(&resp->data, 0, "SIP/2.0 %s\r\n", msg);
- resp->len = resp->data->used;
resp->headers++;
return 0;
@@ -10004,7 +10030,6 @@ static int init_req(struct sip_request *req, int sipmethod, const char *recip)
req->method = sipmethod;
req->header[0] = 0;
ast_str_set(&req->data, 0, "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
- req->len = ast_str_strlen(req->data);
req->headers++;
return 0;
@@ -10719,7 +10744,7 @@ static int add_rpid(struct sip_request *req, struct sip_pvt *p)
return 0;
if (ast_strlen_zero(lid_name))
lid_name = lid_num;
- fromdomain = S_OR(p->fromdomain, ast_sockaddr_stringify_host(&p->ourip));
+ fromdomain = S_OR(p->fromdomain, ast_sockaddr_stringify_host_remote(&p->ourip));
lid_num = ast_uri_encode(lid_num, tmp2, sizeof(tmp2), ast_uri_sip_user);
@@ -11200,12 +11225,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
p->sessionid, p->sessionversion,
(ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
"IP6" : "IP4",
- ast_sockaddr_stringify_addr(&dest));
+ ast_sockaddr_stringify_addr_remote(&dest));
snprintf(connection, sizeof(connection), "c=IN %s %s\r\n",
(ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
"IP6" : "IP4",
- ast_sockaddr_stringify_addr(&dest));
+ ast_sockaddr_stringify_addr_remote(&dest));
if (add_audio) {
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) {
@@ -11388,7 +11413,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
if (!ast_sockaddr_cmp(&udptldest, &dest)) {
ast_str_append(&m_modem, 0, "c=IN %s %s\r\n",
(ast_sockaddr_is_ipv6(&dest) && !ast_sockaddr_is_ipv4_mapped(&dest)) ?
- "IP6" : "IP4", ast_sockaddr_stringify_addr(&udptldest));
+ "IP6" : "IP4", ast_sockaddr_stringify_addr_remote(&udptldest));
}
ast_str_append(&a_modem, 0, "a=T38FaxVersion:%d\r\n", p->t38.our_parms.version);
@@ -11743,10 +11768,10 @@ static void build_contact(struct sip_pvt *p)
if (p->socket.type == SIP_TRANSPORT_UDP) {
ast_string_field_build(p, our_contact, "<sip:%s%s%s>", user,
- ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify(&p->ourip));
+ ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify_remote(&p->ourip));
} else {
ast_string_field_build(p, our_contact, "<sip:%s%s%s;transport=%s>", user,
- ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify(&p->ourip),
+ ast_strlen_zero(user) ? "" : "@", ast_sockaddr_stringify_remote(&p->ourip),
get_transport(p->socket.type));
}
}
@@ -11787,7 +11812,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
- d = S_OR(p->fromdomain, ast_sockaddr_stringify_host(&p->ourip));
+ d = S_OR(p->fromdomain, ast_sockaddr_stringify_host_remote(&p->ourip));
if (p->owner) {
if ((ast_party_id_presentation(&p->owner->connected.id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
l = p->owner->connected.id.number.valid ? p->owner->connected.id.number.str : NULL;
@@ -11951,11 +11976,11 @@ static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt)
if (!pvt->owner->redirecting.from.name.valid
|| ast_strlen_zero(diverting_name)) {
snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s", diverting_number,
- ast_sockaddr_stringify_host(&pvt->ourip), reason);
+ ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
} else {
snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s",
diverting_name, diverting_number,
- ast_sockaddr_stringify_host(&pvt->ourip), reason);
+ ast_sockaddr_stringify_host_remote(&pvt->ourip), reason);
}
add_header(req, "Diversion", header_text);
@@ -12582,7 +12607,7 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs,
struct sip_request req;
struct ast_str *out = ast_str_alloca(500);
int ourport = (p->fromdomainport) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
- const char *domain = S_OR(p->fromdomain, ast_sockaddr_stringify_host(&p->ourip));
+ const char *domain = S_OR(p->fromdomain, ast_sockaddr_stringify_host_remote(&p->ourip));
const char *exten = S_OR(vmexten, default_vmexten);
initreqprep(&req, p, SIP_NOTIFY, NULL);
@@ -12912,6 +12937,19 @@ static int sip_reg_timeout(const void *data)
return 0;
}
+static const char *sip_sanitized_host(const char *host)
+{
+ struct ast_sockaddr addr = { { 0, 0, }, };
+
+ /* peer/sip_pvt->tohost and sip_registry->hostname should never have a port
+ * in them, so we use PARSE_PORT_FORBID here. If this lookup fails, we return
+ * the original host which is most likely a host name and not an IP. */
+ if (!ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID)) {
+ return host;
+ }
+ return ast_sockaddr_stringify_host_remote(&addr);
+}
+
/*! \brief Transmit register to SIP proxy or UA
* auth = NULL on the initial registration (from sip_reregister())
*/
@@ -13065,19 +13103,19 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
ast_debug(1, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout);
}
- snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain,p->tohost), p->tag);
+ snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)), p->tag);
if (!ast_strlen_zero(p->theirtag)) {
- snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain,p->tohost), p->theirtag);
+ snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)), p->theirtag);
} else {
- snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, S_OR(r->regdomain,p->tohost));
+ snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, S_OR(r->regdomain, sip_sanitized_host(p->tohost)));
}
/* Fromdomain is what we are registering to, regardless of actual
host name from SRV */
if (portno && portno != STANDARD_SIP_PORT) {
- snprintf(addr, sizeof(addr), "sip:%s:%d", S_OR(p->fromdomain,S_OR(r->regdomain,r->hostname)), portno);
+ snprintf(addr, sizeof(addr), "sip:%s:%d", S_OR(p->fromdomain,S_OR(r->regdomain, sip_sanitized_host(r->hostname))), portno);
} else {
- snprintf(addr, sizeof(addr), "sip:%s", S_OR(p->fromdomain,S_OR(r->regdomain,r->hostname)));
+ snprintf(addr, sizeof(addr), "sip:%s", S_OR(p->fromdomain,S_OR(r->regdomain, sip_sanitized_host(r->hostname))));
}
ast_string_field_set(p, uri, addr);
@@ -13151,6 +13189,7 @@ static int transmit_message_with_msg(struct sip_pvt *p, const struct ast_msg *ms
struct ast_msg_var_iterator *i;
const char *var, *val;
+ build_via(p);
initreqprep(&req, p, SIP_MESSAGE, NULL);
ast_string_field_set(p, msg_body, ast_msg_get_body(msg));
initialize_initreq(p, &req);
@@ -13368,7 +13407,7 @@ static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xm
return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
}
-/*! \brief return the request and response heade for a 401 or 407 code */
+/*! \brief return the request and response header for a 401 or 407 code */
static void auth_headers(enum sip_auth_type code, char **header, char **respheader)
{
if (code == WWW_AUTH) { /* 401 */
@@ -16116,6 +16155,15 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
}
}
+ /* Override the context with the message context _BEFORE_
+ * getting the destination. This way we can guarantee the correct
+ * extension is used in the message context when it is present. */
+ if (!ast_strlen_zero(p->messagecontext)) {
+ ast_string_field_set(p, context, p->messagecontext);
+ } else if (!ast_strlen_zero(sip_cfg.messagecontext)) {
+ ast_string_field_set(p, context, sip_cfg.messagecontext);
+ }
+
get_destination(p, NULL, NULL);
if (!(msg = ast_msg_alloc())) {
@@ -16132,14 +16180,7 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
res = ast_msg_set_to(msg, "%s", to);
res |= ast_msg_set_from(msg, "%s", get_in_brackets(from));
res |= ast_msg_set_body(msg, "%s", ast_str_buffer(buf));
-
- if (!ast_strlen_zero(p->messagecontext)) {
- res |= ast_msg_set_context(msg, "%s", p->messagecontext);
- } else if (!ast_strlen_zero(sip_cfg.messagecontext)) {
- res |= ast_msg_set_context(msg, "%s", sip_cfg.messagecontext);
- } else {
- res |= ast_msg_set_context(msg, "%s", p->context);
- }
+ res |= ast_msg_set_context(msg, "%s", p->context);
if (!ast_strlen_zero(p->peername)) {
res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername);
@@ -17210,7 +17251,6 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
char codec_buf[512];
struct ast_codec_pref *pref;
struct ast_variable *v;
- struct sip_auth *auth;
int x = 0, load_realtime;
struct ast_format codec;
int realtimepeers;
@@ -17238,6 +17278,15 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
}
if (peer && type==0 ) { /* Normal listing */
struct ast_str *mailbox_str = ast_str_alloca(512);
+ struct sip_auth_container *credentials;
+
+ ao2_lock(peer);
+ credentials = peer->auth;
+ if (credentials) {
+ ao2_t_ref(credentials, +1, "Ref peer auth for show");
+ }
+ ao2_unlock(peer);
+
ast_cli(fd, "\n\n");
ast_cli(fd, " * Name : %s\n", peer->name);
ast_cli(fd, " Description : %s\n", peer->description);
@@ -17247,9 +17296,19 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>");
ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"<Not set>":"<Set>");
ast_cli(fd, " Remote Secret: %s\n", ast_strlen_zero(peer->remotesecret)?"<Not set>":"<Set>");
- for (auth = peer->auth; auth; auth = auth->next) {
- ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s ", auth->realm, auth->username);
- ast_cli(fd, "%s\n", !ast_strlen_zero(auth->secret)?"<Secret set>":(!ast_strlen_zero(auth->md5secret)?"<MD5secret set>" : "<Not set>"));
+ if (credentials) {
+ struct sip_auth *auth;
+
+ AST_LIST_TRAVERSE(&credentials->list, auth, node) {
+ ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s %s\n",
+ auth->realm,
+ auth->username,
+ !ast_strlen_zero(auth->secret)
+ ? "<Secret set>"
+ : (!ast_strlen_zero(auth->md5secret)
+ ? "<MD5secret set>" : "<Not set>"));
+ }
+ ao2_t_ref(credentials, -1, "Unref peer auth for show");
}
ast_cli(fd, " Context : %s\n", peer->context);
ast_cli(fd, " Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") );
@@ -17339,13 +17398,13 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Status : ");
peer_status(peer, status, sizeof(status));
ast_cli(fd, "%s\n", status);
- ast_cli(fd, " Useragent : %s\n", peer->useragent);
- ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
+ ast_cli(fd, " Useragent : %s\n", peer->useragent);
+ ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
ast_cli(fd, " Qualify Freq : %d ms\n", peer->qualifyfreq);
if (peer->chanvars) {
- ast_cli(fd, " Variables :\n");
+ ast_cli(fd, " Variables :\n");
for (v = peer->chanvars ; v ; v = v->next)
- ast_cli(fd, " %s = %s\n", v->name, v->value);
+ ast_cli(fd, " %s = %s\n", v->name, v->value);
}
ast_cli(fd, " Sess-Timers : %s\n", stmode2str(peer->stimer.st_mode_oper));
@@ -17439,13 +17498,13 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
astman_append(s, "Status: ");
peer_status(peer, status, sizeof(status));
astman_append(s, "%s\r\n", status);
- astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent);
- astman_append(s, "Reg-Contact: %s\r\n", peer->fullcontact);
+ astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent);
+ astman_append(s, "Reg-Contact: %s\r\n", peer->fullcontact);
astman_append(s, "QualifyFreq: %d ms\r\n", peer->qualifyfreq);
astman_append(s, "Parkinglot: %s\r\n", peer->parkinglot);
if (peer->chanvars) {
for (v = peer->chanvars ; v ; v = v->next) {
- astman_append(s, "ChanVariable: %s=%s\r\n", v->name, v->value);
+ astman_append(s, "ChanVariable: %s=%s\r\n", v->name, v->value);
}
}
astman_append(s, "SIP-Use-Reason-Header : %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON)) ? "Y" : "N");
@@ -17806,6 +17865,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
int realtimeregs;
char codec_buf[SIPBUFSIZE];
const char *msg; /* temporary msg pointer */
+ struct sip_auth_container *credentials;
switch (cmd) {
case CLI_INIT:
@@ -17818,12 +17878,19 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
return NULL;
}
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
realtimepeers = ast_check_realtime("sippeers");
realtimeregs = ast_check_realtime("sipregs");
- if (a->argc != 3)
- return CLI_SHOWUSAGE;
+ ast_mutex_lock(&authl_lock);
+ credentials = authl;
+ if (credentials) {
+ ao2_t_ref(credentials, +1, "Ref global auth for show");
+ }
+ ast_mutex_unlock(&authl_lock);
+
ast_cli(a->fd, "\n\nGlobal Settings:\n");
ast_cli(a->fd, "----------------\n");
ast_cli(a->fd, " UDP Bindaddress: %s\n", ast_sockaddr_stringify(&bindaddr));
@@ -17847,10 +17914,24 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(a->fd, " Allow unknown access: %s\n", AST_CLI_YESNO(sip_cfg.allowguest));
ast_cli(a->fd, " Allow subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
ast_cli(a->fd, " Allow overlap dialing: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
- ast_cli(a->fd, " Allow promsic. redir: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
+ ast_cli(a->fd, " Allow promisc. redir: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
ast_cli(a->fd, " Enable call counters: %s\n", AST_CLI_YESNO(global_callcounter));
ast_cli(a->fd, " SIP domain support: %s\n", AST_CLI_YESNO(!AST_LIST_EMPTY(&domain_list)));
- ast_cli(a->fd, " Realm. auth: %s\n", AST_CLI_YESNO(authl != NULL));
+ ast_cli(a->fd, " Realm. auth: %s\n", AST_CLI_YESNO(credentials != NULL));
+ if (credentials) {
+ struct sip_auth *auth;
+
+ AST_LIST_TRAVERSE(&credentials->list, auth, node) {
+ ast_cli(a->fd, " Realm. auth entry: Realm %-15.15s User %-10.20s %s\n",
+ auth->realm,
+ auth->username,
+ !ast_strlen_zero(auth->secret)
+ ? "<Secret set>"
+ : (!ast_strlen_zero(auth->md5secret)
+ ? "<MD5secret set>" : "<Not set>"));
+ }
+ ao2_t_ref(credentials, -1, "Unref global auth for show");
+ }
ast_cli(a->fd, " Our auth realm %s\n", sip_cfg.realm);
ast_cli(a->fd, " Use domains as realms: %s\n", AST_CLI_YESNO(sip_cfg.domainsasrealm));
ast_cli(a->fd, " Call to non-local dom.: %s\n", AST_CLI_YESNO(sip_cfg.allow_external_domains));
@@ -19048,20 +19129,39 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
const char *username;
const char *secret;
const char *md5secret;
- struct sip_auth *auth = NULL; /* Realm authentication */
+ struct sip_auth *auth; /* Realm authentication credential */
+ struct sip_auth_container *credentials;
if (!ast_strlen_zero(p->domain))
ast_copy_string(uri, p->domain, sizeof(uri));
else if (!ast_strlen_zero(p->uri))
ast_copy_string(uri, p->uri, sizeof(uri));
else
- snprintf(uri, sizeof(uri), "sip:%s@%s", p->username, ast_sockaddr_stringify_host(&p->sa));
+ snprintf(uri, sizeof(uri), "sip:%s@%s", p->username, ast_sockaddr_stringify_host_remote(&p->sa));
snprintf(cnonce, sizeof(cnonce), "%08lx", ast_random());
- /* Check if we have separate auth credentials */
- if(!(auth = find_realm_authentication(p->peerauth, p->realm))) /* Start with peer list */
- auth = find_realm_authentication(authl, p->realm); /* If not, global list */
+ /* Check if we have peer credentials */
+ ao2_lock(p);
+ credentials = p->peerauth;
+ if (credentials) {
+ ao2_t_ref(credentials, +1, "Ref peer auth for digest");
+ }
+ ao2_unlock(p);
+ auth = find_realm_authentication(credentials, p->realm);
+ if (!auth) {
+ /* If not, check global credentials */
+ if (credentials) {
+ ao2_t_ref(credentials, -1, "Unref peer auth for digest");
+ }
+ ast_mutex_lock(&authl_lock);
+ credentials = authl;
+ if (credentials) {
+ ao2_t_ref(credentials, +1, "Ref global auth for digest");
+ }
+ ast_mutex_unlock(&authl_lock);
+ auth = find_realm_authentication(credentials, p->realm);
+ }
if (auth) {
ast_debug(3, "use realm [%s] from peer [%s][%s]\n", auth->username, p->peername, p->username);
@@ -19075,11 +19175,16 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
username = p->authname;
secret = p->relatedpeer
&& !ast_strlen_zero(p->relatedpeer->remotesecret)
- ? p->relatedpeer->remotesecret : p->peersecret;
+ ? p->relatedpeer->remotesecret : p->peersecret;
md5secret = p->peermd5secret;
}
- if (ast_strlen_zero(username)) /* We have no authentication */
+ if (ast_strlen_zero(username)) {
+ /* We have no authentication */
+ if (credentials) {
+ ao2_t_ref(credentials, -1, "Unref auth for digest");
+ }
return -1;
+ }
/* Calculate SIP digest response */
snprintf(a1, sizeof(a1), "%s:%s:%s", username, p->realm, secret);
@@ -19099,7 +19204,7 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
/* only include the opaque string if it's set */
if (!ast_strlen_zero(p->opaque)) {
- snprintf(opaque, sizeof(opaque), ", opaque=\"%s\"", p->opaque);
+ snprintf(opaque, sizeof(opaque), ", opaque=\"%s\"", p->opaque);
}
/* XXX We hard code our qop to "auth" for now. XXX */
@@ -19110,6 +19215,9 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %d", username, p->realm, p->noncecount);
+ if (credentials) {
+ ao2_t_ref(credentials, -1, "Unref auth for digest");
+ }
return 0;
}
@@ -20586,6 +20694,131 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
ref_peer(peer, "adding poke peer ref"));
}
+/*!
+ * \internal
+ * \brief Handle responses to INFO messages
+ *
+ * \note The INFO method MUST NOT change the state of calls or
+ * related sessions (RFC 2976).
+ */
+static void handle_response_info(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
+{
+ int sipmethod = SIP_INFO;
+
+ switch (resp) {
+ case 401: /* Not www-authorized on SIP method */
+ case 407: /* Proxy auth required */
+ ast_log(LOG_WARNING, "Host '%s' requests authentication (%d) for '%s'\n",
+ ast_sockaddr_stringify(&p->sa), resp, sip_methods[sipmethod].text);
+ break;
+ case 405: /* Method not allowed */
+ case 501: /* Not Implemented */
+ mark_method_unallowed(&p->allowed_methods, sipmethod);
+ if (p->relatedpeer) {
+ mark_method_allowed(&p->relatedpeer->disallowed_methods, sipmethod);
+ }
+ ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n",
+ ast_sockaddr_stringify(&p->sa), sip_methods[sipmethod].text);
+ break;
+ default:
+ if (300 <= resp && resp < 700) {
+ ast_verb(3, "Got SIP %s response %d \"%s\" back from host '%s'\n",
+ sip_methods[sipmethod].text, resp, rest, ast_sockaddr_stringify(&p->sa));
+ }
+ break;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Handle auth requests to a MESSAGE request
+ * \return TRUE if authentication failed.
+ */
+static int do_message_auth(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
+{
+ char *header;
+ char *respheader;
+ char digest[1024];
+
+ if (p->options) {
+ p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
+ }
+
+ if (p->authtries == MAX_AUTHTRIES) {
+ ast_log(LOG_NOTICE, "Failed to authenticate MESSAGE with host '%s'\n",
+ ast_sockaddr_stringify(&p->sa));
+ return -1;
+ }
+
+ ++p->authtries;
+ auth_headers((resp == 401 ? WWW_AUTH : PROXY_AUTH), &header, &respheader);
+ memset(digest, 0, sizeof(digest));
+ if (reply_digest(p, req, header, SIP_MESSAGE, digest, sizeof(digest))) {
+ /* There's nothing to use for authentication */
+ ast_debug(1, "Nothing to use for MESSAGE authentication\n");
+ return -1;
+ }
+
+ if (p->do_history) {
+ append_history(p, "MessageAuth", "Try: %d", p->authtries);
+ }
+
+ transmit_message_with_text(p, p->msg_body, 0, 1);
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Handle responses to MESSAGE messages
+ *
+ * \note The MESSAGE method should not change the state of calls
+ * or related sessions if associated with a dialog. (Implied by
+ * RFC 3428 Section 2).
+ */
+static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
+{
+ int sipmethod = SIP_MESSAGE;
+ int in_dialog = ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+
+ switch (resp) {
+ case 401: /* Not www-authorized on SIP method */
+ case 407: /* Proxy auth required */
+ if (do_message_auth(p, resp, rest, req, seqno) && !in_dialog) {
+ pvt_set_needdestroy(p, "MESSAGE authentication failed");
+ }
+ break;
+ case 405: /* Method not allowed */
+ case 501: /* Not Implemented */
+ mark_method_unallowed(&p->allowed_methods, sipmethod);
+ if (p->relatedpeer) {
+ mark_method_allowed(&p->relatedpeer->disallowed_methods, sipmethod);
+ }
+ ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n",
+ ast_sockaddr_stringify(&p->sa), sip_methods[sipmethod].text);
+ if (!in_dialog) {
+ pvt_set_needdestroy(p, "MESSAGE not implemented or allowed");
+ }
+ break;
+ default:
+ if (100 <= resp && resp < 200) {
+ /* Must allow provisional responses for out-of-dialog requests. */
+ } else if (200 <= resp && resp < 300) {
+ p->authtries = 0; /* Reset authentication counter */
+ if (!in_dialog) {
+ pvt_set_needdestroy(p, "MESSAGE delivery accepted");
+ }
+ } else if (300 <= resp && resp < 700) {
+ ast_verb(3, "Got SIP %s response %d \"%s\" back from host '%s'\n",
+ sip_methods[sipmethod].text, resp, rest, ast_sockaddr_stringify(&p->sa));
+ if (!in_dialog) {
+ pvt_set_needdestroy(p, (300 <= resp && resp < 600)
+ ? "MESSAGE delivery failed" : "MESSAGE delivery refused");
+ }
+ }
+ break;
+ }
+}
+
/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
static void stop_media_flows(struct sip_pvt *p)
{
@@ -20706,6 +20939,12 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
* we just always call the response handler. Good gravy!
*/
handle_response_publish(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_INFO) {
+ /* More good gravy! */
+ handle_response_info(p, resp, rest, req, seqno);
+ } else if (sipmethod == SIP_MESSAGE) {
+ /* More good gravy! */
+ handle_response_message(p, resp, rest, req, seqno);
} else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
switch(resp) {
case 100: /* 100 Trying */
@@ -20719,11 +20958,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
break;
case 200: /* 200 OK */
p->authtries = 0; /* Reset authentication counter */
- if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO) {
- /* We successfully transmitted a message
- or a video update request in INFO */
- /* Nothing happens here - the message is inside a dialog */
- } else if (sipmethod == SIP_INVITE) {
+ if (sipmethod == SIP_INVITE) {
handle_response_invite(p, resp, rest, req, seqno);
} else if (sipmethod == SIP_NOTIFY) {
handle_response_notify(p, resp, rest, req, seqno);
@@ -20749,8 +20984,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
handle_response_register(p, resp, rest, req, seqno);
else if (sipmethod == SIP_UPDATE) {
handle_response_update(p, resp, rest, req, seqno);
- } else if (sipmethod == SIP_MESSAGE) {
- handle_response_message(p, resp, rest, req, seqno);
} else if (sipmethod == SIP_BYE) {
if (p->options)
p->options->auth_type = resp;
@@ -20852,7 +21085,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
pvt_set_needdestroy(p, "received 491 response");
}
break;
- case 405:
+ case 405: /* Method not allowed */
case 501: /* Not Implemented */
mark_method_unallowed(&p->allowed_methods, sipmethod);
if (p->relatedpeer) {
@@ -20863,7 +21096,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
else
ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_sockaddr_stringify(&p->sa), msg);
break;
- /* Fallthrough */
default:
if ((resp >= 200) && (resp < 300)) { /* on any 2XX response do the following */
if (sipmethod == SIP_INVITE) {
@@ -20883,17 +21115,17 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
case 301: /* Moved permanently */
case 302: /* Moved temporarily */
case 305: /* Use Proxy */
- if (p->owner) {
- struct ast_party_redirecting redirecting;
- struct ast_set_party_redirecting update_redirecting;
-
- ast_party_redirecting_init(&redirecting);
- change_redirecting_information(p, req, &redirecting,
- &update_redirecting, TRUE);
- ast_channel_set_redirecting(p->owner, &redirecting,
- &update_redirecting);
- ast_party_redirecting_free(&redirecting);
- }
+ if (p->owner) {
+ struct ast_party_redirecting redirecting;
+ struct ast_set_party_redirecting update_redirecting;
+
+ ast_party_redirecting_init(&redirecting);
+ change_redirecting_information(p, req, &redirecting,
+ &update_redirecting, TRUE);
+ ast_channel_set_redirecting(p->owner, &redirecting,
+ &update_redirecting);
+ ast_party_redirecting_free(&redirecting);
+ }
/* Fall through */
case 486: /* Busy here */
case 600: /* Busy everywhere */
@@ -20922,15 +21154,14 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
break;
default:
/* Send hangup */
- if (owner && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE)
+ if (owner && sipmethod != SIP_BYE)
ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
break;
}
/* ACK on invite */
if (sipmethod == SIP_INVITE)
transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
- if (sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO)
- sip_alreadygone(p);
+ sip_alreadygone(p);
if (!p->owner) {
pvt_set_needdestroy(p, "transaction completed");
}
@@ -20991,10 +21222,6 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
}
} else if (sipmethod == SIP_BYE) {
pvt_set_needdestroy(p, "transaction completed");
- } else if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO) {
- /* We successfully transmitted a message or
- a video update request in INFO */
- ;
}
break;
case 401: /* www-auth */
@@ -23298,13 +23525,21 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
}
ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */
-
- /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
- servers - generate an INVITE with Replaces. Either way, let the dial plan decided */
- /* indicate before masquerade so the indication actually makes it to the real channel
- when using local channels with MOH passthru */
- ast_indicate(current.chan2, AST_CONTROL_UNHOLD);
- res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
+ {
+ char *refer_to_context = ast_strdupa(p->refer->refer_to_context);
+ char *refer_to = ast_strdupa(p->refer->refer_to);
+
+ /* Do not hold the pvt lock during the indicate and async_goto. Those functions
+ * lock channels which will invalidate locking order if the pvt lock is held.*/
+ ao2_unlock(p);
+ /* For blind transfers, move the call to the new extensions. For attended transfers on multiple
+ * servers - generate an INVITE with Replaces. Either way, let the dial plan decided */
+ /* indicate before masquerade so the indication actually makes it to the real channel
+ *when using local channels with MOH passthru */
+ ast_indicate(current.chan2, AST_CONTROL_UNHOLD);
+ res = ast_async_goto(current.chan2, refer_to_context, refer_to, 1);
+ ao2_lock(p);
+ }
if (!res) {
ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
@@ -23397,7 +23632,7 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
}
else
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- if (p->initreq.len > 0) {
+ if (ast_str_strlen(p->initreq.data) > 0) {
struct sip_pkt *pkt, *prev_pkt;
/* If the CANCEL we are receiving is a retransmission, and we already have scheduled
* a reliable 487, then we don't want to schedule another one on top of the previous
@@ -23580,42 +23815,6 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
return 1;
}
-/*!
- * \internal
- * \brief Handle auth requests to a MESSAGE request
- */
-static void handle_response_message(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno)
-{
- char *header, *respheader;
- char digest[1024];
-
- if (p->options) {
- p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
- }
-
- if ((p->authtries == MAX_AUTHTRIES)) {
- ast_log(LOG_NOTICE, "Failed to authenticate on MESSAGE to '%s'\n", get_header(&p->initreq, "From"));
- pvt_set_needdestroy(p, "MESSAGE authentication failed");
- return;
- }
-
- p->authtries++;
- auth_headers((resp == 401 ? WWW_AUTH : PROXY_AUTH), &header, &respheader);
- memset(digest, 0, sizeof(digest));
- if (reply_digest(p, req, header, SIP_MESSAGE, digest, sizeof(digest))) {
- /* There's nothing to use for authentication */
- ast_debug(1, "Nothing to use for MESSAGE authentication\n");
- pvt_set_needdestroy(p, "MESSAGE authentication failed");
- return;
- }
-
- if (p->do_history) {
- append_history(p, "MessageAuth", "Try: %d", p->authtries);
- }
-
- transmit_message_with_text(p, p->msg_body, 0, 1);
-}
-
/*! \brief Handle incoming MESSAGE request */
static int handle_request_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
{
@@ -23654,6 +23853,8 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
}
if (ast_strlen_zero(peer)) {
ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
+ dialog_unlink_all(pvt, TRUE, TRUE);
+ dialog_unref(pvt, "MESSAGE(to) is invalid for SIP");
return -1;
}
@@ -25015,7 +25216,6 @@ static int sipsock_read(int *id, int fd, short events, void *ignore)
return -1;
}
- req.len = res;
req.socket.fd = sipsock;
set_socket_transport(&req.socket, SIP_TRANSPORT_UDP);
req.socket.tcptls_session = NULL;
@@ -25041,7 +25241,7 @@ static int handle_request_do(struct sip_request *req, struct ast_sockaddr *addr)
if (sip_debug_test_addr(addr)) /* Set the debug flag early on packet level */
req->debug = 1;
if (sip_cfg.pedanticsipchecking)
- req->len = lws2sws(req->data->str, req->len); /* Fix multiline headers */
+ lws2sws(req->data); /* Fix multiline headers */
if (req->debug) {
ast_verbose("\n<--- SIP read from %s:%s --->\n%s\n<------------->\n",
get_transport(req->socket.type), ast_sockaddr_stringify(addr), req->data->str);
@@ -25065,7 +25265,7 @@ static int handle_request_do(struct sip_request *req, struct ast_sockaddr *addr)
/* Find the active SIP dialog or create a new one */
p = find_call(req, addr, req->method); /* returns p with a reference only. _NOT_ locked*/
if (p == NULL) {
- ast_debug(1, "Invalid SIP message - rejected , no callid, len %d\n", req->len);
+ ast_debug(1, "Invalid SIP message - rejected , no callid, len %zu\n", ast_str_strlen(req->data));
ast_mutex_unlock(&netlock);
return 1;
}
@@ -25296,13 +25496,14 @@ create_tcptls_session_fail:
/*!
* \brief Get cached MWI info
- * \retval 0 At least one message is waiting
- * \retval 1 no messages waiting
+ * \return TRUE if found MWI in cache
*/
static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
{
struct sip_mailbox *mailbox;
+ int in_cache;
+ in_cache = 0;
AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
struct ast_event *event;
event = ast_event_get_cached(AST_EVENT_MWI,
@@ -25314,9 +25515,10 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
*new += ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
*old += ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
ast_event_destroy(event);
+ in_cache = 1;
}
- return (*new || *old) ? 0 : 1;
+ return in_cache;
}
/*! \brief Send message waiting indication to alert peer that they've got voicemail */
@@ -25336,12 +25538,11 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *e
if (event) {
newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
oldmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
- } else if (!cache_only) { /* Fall back to manually checking the mailbox */
+ } else if (!get_cached_mwi(peer, &newmsgs, &oldmsgs) && !cache_only) {
+ /* Fall back to manually checking the mailbox */
struct ast_str *mailbox_str = ast_str_alloca(512);
peer_mailboxes_to_str(&mailbox_str, peer);
ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
- } else {
- get_cached_mwi(peer, &newmsgs, &oldmsgs);
}
if (peer->mwipvt) {
@@ -25974,7 +26175,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
if (!ast_strlen_zero(peer->tohost))
ast_string_field_set(p, tohost, peer->tohost);
else
- ast_string_field_set(p, tohost, ast_sockaddr_stringify_host(&peer->addr));
+ ast_string_field_set(p, tohost, ast_sockaddr_stringify_host_remote(&peer->addr));
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
@@ -26605,20 +26806,48 @@ static void clear_sip_domains(void)
AST_LIST_UNLOCK(&domain_list);
}
+/*!
+ * \internal
+ * \brief Realm authentication container destructor.
+ *
+ * \param obj Container object to destroy.
+ *
+ * \return Nothing
+ */
+static void destroy_realm_authentication(void *obj)
+{
+ struct sip_auth_container *credentials = obj;
+ struct sip_auth *auth;
+
+ while ((auth = AST_LIST_REMOVE_HEAD(&credentials->list, node))) {
+ ast_free(auth);
+ }
+}
-/*! \brief Add realm authentication in list */
-static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, const char *configuration, int lineno)
+/*!
+ * \internal
+ * \brief Add realm authentication to credentials.
+ *
+ * \param credentials Realm authentication container to create/add authentication credentials.
+ * \param configuration Credential configuration value.
+ * \param lineno Line number in config file.
+ *
+ * \return Nothing
+ */
+static void add_realm_authentication(struct sip_auth_container **credentials, const char *configuration, int lineno)
{
- char authcopy[256];
+ char *authcopy;
char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
- struct sip_auth *a, *b, *auth;
+ struct sip_auth *auth;
- if (ast_strlen_zero(configuration))
- return authlist;
+ if (ast_strlen_zero(configuration)) {
+ /* Nothing to add */
+ return;
+ }
ast_debug(1, "Auth config :: %s\n", configuration);
- ast_copy_string(authcopy, configuration, sizeof(authcopy));
+ authcopy = ast_strdupa(configuration);
username = authcopy;
/* split user[:secret] and relm */
@@ -26627,7 +26856,7 @@ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, cons
*realm++ = '\0';
if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
- return authlist;
+ return;
}
/* parse username at ':' for secret, or '#" for md5secret */
@@ -26637,9 +26866,21 @@ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, cons
*md5secret++ = '\0';
}
- if (!(auth = ast_calloc(1, sizeof(*auth))))
- return authlist;
+ /* Create the continer if needed. */
+ if (!*credentials) {
+ *credentials = ao2_t_alloc(sizeof(**credentials), destroy_realm_authentication,
+ "Create realm auth container.");
+ if (!*credentials) {
+ /* Failed to create the credentials container. */
+ return;
+ }
+ }
+ /* Create the authentication credential entry. */
+ auth = ast_calloc(1, sizeof(*auth));
+ if (!auth) {
+ return;
+ }
ast_copy_string(auth->realm, realm, sizeof(auth->realm));
ast_copy_string(auth->username, username, sizeof(auth->username));
if (secret)
@@ -26647,46 +26888,36 @@ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, cons
if (md5secret)
ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
- /* find the end of the list */
- for (b = NULL, a = authlist; a ; b = a, a = a->next)
- ;
- if (b)
- b->next = auth; /* Add structure add end of list */
- else
- authlist = auth;
+ /* Add credential to container list. */
+ AST_LIST_INSERT_TAIL(&(*credentials)->list, auth, node);
ast_verb(3, "Added authentication for realm %s\n", realm);
-
- return authlist;
-
-}
-
-/*! \brief Clear realm authentication list (at reload) */
-static int clear_realm_authentication(struct sip_auth *authlist)
-{
- struct sip_auth *a = authlist;
- struct sip_auth *b;
-
- while (a) {
- b = a;
- a = a->next;
- ast_free(b);
- }
-
- return 1;
}
-/*! \brief Find authentication for a specific realm */
-static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm)
+/*!
+ * \internal
+ * \brief Find authentication for a specific realm.
+ *
+ * \param credentials Realm authentication container to search.
+ * \param realm Authentication realm to find.
+ *
+ * \return Found authentication credential or NULL.
+ */
+static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm)
{
- struct sip_auth *a;
+ struct sip_auth *auth;
- for (a = authlist; a; a = a->next) {
- if (!strcasecmp(a->realm, realm))
- break;
+ if (credentials) {
+ AST_LIST_TRAVERSE(&credentials->list, auth, node) {
+ if (!strcasecmp(auth->realm, realm)) {
+ break;
+ }
+ }
+ } else {
+ auth = NULL;
}
- return a;
+ return auth;
}
/*! \brief
@@ -26932,8 +27163,13 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
peer->portinuri = 0;
/* If we have realm authentication information, remove them (reload) */
- clear_realm_authentication(peer->auth);
- peer->auth = NULL;
+ ao2_lock(peer);
+ if (peer->auth) {
+ ao2_t_ref(peer->auth, -1, "Removing old peer authentication");
+ peer->auth = NULL;
+ }
+ ao2_unlock(peer);
+
/* clear the transport information. We will detect if a default value is required after parsing the config */
peer->default_outbound_transport = 0;
peer->transports = 0;
@@ -26997,7 +27233,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
} else if (!strcasecmp(v->name, "md5secret")) {
ast_string_field_set(peer, md5secret, v->value);
} else if (!strcasecmp(v->name, "auth")) {
- peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno);
+ add_realm_authentication(&peer->auth, v->value, v->lineno);
} else if (!strcasecmp(v->name, "callerid")) {
char cid_name[80] = { '\0' }, cid_num[80] = { '\0' };
@@ -27609,9 +27845,13 @@ static int reload_config(enum channelreloadreason reason)
if (reason != CHANNEL_MODULE_LOAD) {
ast_debug(4, "--------------- SIP reload started\n");
- clear_realm_authentication(authl);
clear_sip_domains();
- authl = NULL;
+ ast_mutex_lock(&authl_lock);
+ if (authl) {
+ ao2_t_ref(authl, -1, "Removing old global authentication");
+ authl = NULL;
+ }
+ ast_mutex_unlock(&authl_lock);
/* First, destroy all outstanding registry calls */
/* This is needed, since otherwise active registry entries will not be destroyed */
@@ -28340,12 +28580,12 @@ static int reload_config(enum channelreloadreason reason)
}
/* Build list of authentication to various SIP realms, i.e. service providers */
- for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
- /* Format for authentication is auth = username:password@realm */
- if (!strcasecmp(v->name, "auth")) {
- authl = add_realm_authentication(authl, v->value, v->lineno);
+ for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) {
+ /* Format for authentication is auth = username:password@realm */
+ if (!strcasecmp(v->name, "auth")) {
+ add_realm_authentication(&authl, v->value, v->lineno);
}
- }
+ }
if (bindport) {
if (ast_sockaddr_port(&bindaddr)) {
@@ -28665,24 +28905,22 @@ static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
{
struct sip_pvt *p;
+ /* Lock the channel and the private safely. */
+ ast_channel_lock(chan);
p = chan->tech_pvt;
if (!p) {
+ ast_channel_unlock(chan);
return -1;
}
- /*
- * Lock both the pvt and it's owner safely.
- */
sip_pvt_lock(p);
- while (p->owner && ast_channel_trylock(p->owner)) {
- sip_pvt_unlock(p);
- usleep(1);
- sip_pvt_lock(p);
- }
-
- if (!p->owner) {
+ if (p->owner != chan) {
+ /* I suppose it could be argued that if this happens it is a bug. */
+ ast_debug(1, "The private is not owned by channel %s anymore.\n", chan->name);
sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
return 0;
}
+
if (udptl) {
ast_udptl_get_peer(udptl, &p->udptlredirip);
} else {
@@ -28701,8 +28939,8 @@ static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
}
/* Reset lastrtprx timer */
p->lastrtprx = p->lastrtptx = time(NULL);
- ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
return 0;
}
@@ -28809,10 +29047,21 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
struct sip_pvt *p;
int changed = 0;
+ /* Lock the channel and the private safely. */
+ ast_channel_lock(chan);
p = chan->tech_pvt;
if (!p) {
+ ast_channel_unlock(chan);
return -1;
}
+ sip_pvt_lock(p);
+ if (p->owner != chan) {
+ /* I suppose it could be argued that if this happens it is a bug. */
+ ast_debug(1, "The private is not owned by channel %s anymore.\n", chan->name);
+ sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
+ return 0;
+ }
/* Disable early RTP bridge */
if ((instance || vinstance || tinstance) &&
@@ -28821,25 +29070,10 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
return 0;
}
- /*
- * Lock both the pvt and it's owner safely.
- */
- sip_pvt_lock(p);
- while (p->owner && ast_channel_trylock(p->owner)) {
- sip_pvt_unlock(p);
- usleep(1);
- sip_pvt_lock(p);
- }
-
- if (!p->owner) {
- sip_pvt_unlock(p);
- return 0;
- }
-
if (p->alreadygone) {
/* If we're destroyed, don't bother */
- ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
return 0;
}
@@ -28847,8 +29081,8 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
that are known to be behind a NAT, then stop the process now
*/
if (nat_active && !ast_test_flag(&p->flags[0], SIP_DIRECT_MEDIA_NAT)) {
- ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
return 0;
}
@@ -28890,8 +29124,8 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
}
/* Reset lastrtprx timer */
p->lastrtprx = p->lastrtptx = time(NULL);
- ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
+ ast_channel_unlock(chan);
return 0;
}
@@ -29892,6 +30126,7 @@ static int load_module(void)
if (!(sip_tech.capabilities = ast_format_cap_alloc())) {
return AST_MODULE_LOAD_FAILURE;
}
+
/* the fact that ao2_containers can't resize automatically is a major worry! */
/* if the number of objects gets above MAX_XXX_BUCKETS, things will slow down */
peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers");
@@ -29900,6 +30135,11 @@ static int load_module(void)
dialogs_needdestroy = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs_needdestroy");
dialogs_rtpcheck = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs for rtpchecks");
threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table");
+ if (!peers || !peers_by_ip || !dialogs || !dialogs_needdestroy || !dialogs_rtpcheck
+ || !threadt) {
+ ast_log(LOG_ERROR, "Unable to create primary SIP container(s)\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
if (!(sip_cfg.caps = ast_format_cap_alloc())) {
return AST_MODULE_LOAD_FAILURE;
@@ -29923,7 +30163,7 @@ static int load_module(void)
sip_reloadreason = CHANNEL_MODULE_LOAD;
can_parse_xml = sip_is_xml_parsable();
- if(reload_config(sip_reloadreason)) { /* Load the configuration from sip.conf */
+ if (reload_config(sip_reloadreason)) { /* Load the configuration from sip.conf */
return AST_MODULE_LOAD_DECLINE;
}
@@ -30133,7 +30373,12 @@ static int unload_module(void)
/* Free memory for local network address mask */
ast_free_ha(localaddr);
- clear_realm_authentication(authl);
+ ast_mutex_lock(&authl_lock);
+ if (authl) {
+ ao2_t_ref(authl, -1, "Removing global authentication");
+ authl = NULL;
+ }
+ ast_mutex_unlock(&authl_lock);
destroy_escs();
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index e8dba3067..f5cf93d2b 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -745,7 +745,6 @@ struct sip_socket {
struct sip_request {
ptrdiff_t rlPart1; /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */
ptrdiff_t rlPart2; /*!< Offset of the Request URI or Response Status */
- int len; /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */
int headers; /*!< # of SIP Headers */
int method; /*!< Method of this request */
int lines; /*!< Body Content */
@@ -830,11 +829,16 @@ struct sip_history {
/*! \brief sip_auth: Credentials for authentication to other SIP services */
struct sip_auth {
+ AST_LIST_ENTRY(sip_auth) node;
char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */
char username[256]; /*!< Username */
char secret[256]; /*!< Secret */
char md5secret[256]; /*!< MD5Secret */
- struct sip_auth *next; /*!< Next auth structure in list */
+};
+
+/*! \brief Container of SIP authentication credentials. */
+struct sip_auth_container {
+ AST_LIST_HEAD_NOLOCK(, sip_auth) list;
};
/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */
@@ -1047,7 +1051,7 @@ struct sip_pvt {
struct ast_channel *owner; /*!< Who owns us (if we have an owner) */
struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */
struct sip_notify *notify; /*!< Custom notify type */
- struct sip_auth *peerauth; /*!< Realm authentication */
+ struct sip_auth_container *peerauth;/*!< Realm authentication credentials */
int noncecount; /*!< Nonce-count */
unsigned int stalenonce:1; /*!< Marks the current nonce as responded too */
char lastmsg[256]; /*!< Last Message sent/received */
@@ -1144,7 +1148,6 @@ struct sip_pkt {
struct timeval time_sent; /*!< When pkt was sent */
int64_t retrans_stop_time; /*!< Time in ms after 'now' that retransmission must stop */
int retrans_stop; /*!< Timeout is reached, stop retransmission */
- int packetlen; /*!< Length of packet */
struct ast_str *data;
};
@@ -1211,7 +1214,7 @@ struct sip_peer {
* for incoming calls
*/
unsigned short deprecated_username:1; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */
- struct sip_auth *auth; /*!< Realm authentication list */
+ struct sip_auth_container *auth;/*!< Realm authentication credentials */
int amaflags; /*!< AMA Flags (for billing) */
int callingpres; /*!< Calling id presentation */
int inUse; /*!< Number of calls in use */
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c
index 6c7031730..37d77d418 100644
--- a/channels/sip/reqresp_parser.c
+++ b/channels/sip/reqresp_parser.c
@@ -1029,14 +1029,14 @@ int get_in_brackets_full(char *tmp,char **out,char **residue)
only affects token based display-names there is no danger of brackets being in quotes */
if (first_bracket) {
parse = first_bracket;
- } else {
+ } else {
parse = tmp;
}
if ((second_bracket = strchr(parse, '>'))) {
*second_bracket++ = '\0';
if (out) {
- *out = first_bracket;
+ *out = (char *) parse;
}
if (residue) {
*residue = second_bracket;
@@ -1045,9 +1045,9 @@ int get_in_brackets_full(char *tmp,char **out,char **residue)
}
if ((first_bracket)) {
- ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
+ ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
return -1;
- }
+ }
if (out) {
*out = tmp;
@@ -1076,6 +1076,7 @@ AST_TEST_DEFINE(get_in_brackets_test)
char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
char no_name_no_brackets[] = "sip:name@host";
+ char missing_start_bracket[] = "name not in quotes sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
char *uri = NULL;
switch (cmd) {
@@ -1140,6 +1141,13 @@ AST_TEST_DEFINE(get_in_brackets_test)
res = AST_TEST_FAIL;
}
+ /* Test 8, no start bracket, but with ending bracket. */
+ if (!(uri = get_in_brackets(missing_start_bracket)) || !(strcmp(uri, in_brackets))) {
+
+ ast_test_status_update(test, "Test 8 failed. %s\n", uri);
+ res = AST_TEST_FAIL;
+ }
+
return res;
}
diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c
index d48e21f28..6554ee000 100644
--- a/codecs/codec_speex.c
+++ b/codecs/codec_speex.c
@@ -33,7 +33,7 @@
/*** MODULEINFO
<depend>speex</depend>
<depend>speex_preprocess</depend>
- <use>speexdsp</use>
+ <use type="external">speexdsp</use>
***/
#include "asterisk.h"
diff --git a/configs/confbridge.conf.sample b/configs/confbridge.conf.sample
index 1781b88a0..408387012 100644
--- a/configs/confbridge.conf.sample
+++ b/configs/confbridge.conf.sample
@@ -168,6 +168,26 @@ type=bridge
; larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
; or 80. By default 20ms is used.
+;video_mode = follow_talker ; Sets how confbridge handles video distribution to the conference participants.
+ ; Note that participants wanting to view and be the source of a video feed
+ ; _MUST_ be sharing the same video codec.
+ ; --- MODES ---
+ ; none: No video sources are set by default in the conference. It is still
+ ; possible for a user to be set as a video source via AMI or DTMF action
+ ; at any time.
+ ;
+ ; follow_talker: The video feed will follow whoever is talking and providing video.
+ ;
+ ; last_marked: The last marked user to join the conference with video capabilities
+ ; will be the single source of video distributed to all participants.
+ ; If multiple marked users are capable of video, the last one to join
+ ; is always the source, when that user leaves it goes to the one who
+ ; joined before them.
+ ;
+ ; first_marked: The first marked user to join the conference with video capabilities
+ ; is the single source of video distribution among all participants. If
+ ; that user leaves, the marked user to join after them becomes the source.
+
; All sounds in the conference are customizable using the bridge profile options below.
; Simply state the option followed by the filename or full path of the filename after
; the option. Example: sound_had_joined=conf-hasjoin This will play the conf-hasjoin
@@ -264,6 +284,8 @@ type=bridge
; admin_toggle_conference_lock ; This action allows an Admin to toggle locking and
; unlocking the conference. Non admins can not use
; this action even if it is in their menu.
+; set_as_single_video_src ; This action allows any user to set themselves as the
+ ; single video source distributed to all participants.
[sample_user_menu]
type=menu
diff --git a/configs/queuerules.conf.sample b/configs/queuerules.conf.sample
index ccabe2cfa..fb2a1ba87 100644
--- a/configs/queuerules.conf.sample
+++ b/configs/queuerules.conf.sample
@@ -7,7 +7,7 @@
;
; Note: There is a limitation to these rules; a caller will follow the penaltychange rules for
; the queue that were defined at the time the caller entered the queue. If an update to the rules is
-; made during the the caller's stay in the queue, these will not be reflected for that caller.
+; made during the caller's stay in the queue, these will not be reflected for that caller.
;
; The syntax for these rules is
; penaltychange => <number of seconds into the call>,<absolute or relative change to QUEUE_MAX_PENALTY>[,absolute or relative change to QUEUE_MIN_PENALTY]
diff --git a/configs/queues.conf.sample b/configs/queues.conf.sample
index c2045f90c..3f67d6df7 100644
--- a/configs/queues.conf.sample
+++ b/configs/queues.conf.sample
@@ -61,6 +61,10 @@ monitor-type = MixMonitor
;
;shared_lastcall=no
;
+; Negative_penalty_invalid will treat members with a negative penalty as logged off
+;
+;negative_penalty_invalid = no
+;
;[markq]
;
; A sample call queue
@@ -196,6 +200,10 @@ monitor-type = MixMonitor
; all: Memeber will be paused in all queues he/she is a member
;autopause=yes
;
+; Autopausedelay delay autopause for autopausedelay seconds from the
+; last call if a member has not taken a call the delay has no effect.
+;autopausedelay=60
+;
; Maximum number of people waiting in the queue (0 for unlimited)
;
;maxlen = 0
@@ -459,6 +467,9 @@ monitor-type = MixMonitor
; uncomment this option. (Note: only the SIP channel driver currently is able
; to report 'in use'.)
;
+; A member can have the ignorebusy flag set or unset when ringinuse is set to
+; allow a per member control.
+;
; ringinuse = no
;
; If you wish to have a delay before the member is connected to the caller (or
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 21235a6ef..f33b1cf32 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -130,7 +130,7 @@ allowoverlap=no ; Disable overlap dialing support. (Default is y
; asterisk.conf, it defaults to that system name
; Realms MUST be globally unique according to RFC 3261
; Set this to your host name or domain name
-;domainsasrealm=no ; Use domans list as realms
+;domainsasrealm=no ; Use domains list as realms
; You can serve multiple Realms specifying several
; 'domain=...' directives (see below).
; In this case Realm will be based on request 'From'/'To' header
diff --git a/contrib/scripts/file.convert.sh b/contrib/scripts/file.convert.sh
new file mode 100755
index 000000000..9fc2e66eb
--- /dev/null
+++ b/contrib/scripts/file.convert.sh
@@ -0,0 +1,20 @@
+#/bin/bash
+
+# Script written by Trey Blancher (support@digium.com)
+
+# This script is designed to convert all files of type $SRC to
+# the $DST format, for the given $LANGUAGE. It traverses the given
+# language directory (by default in /var/lib/asterisk/sounds/), and
+# converts each file with filename extension $SRC, and converts them
+# using Asterisk to files with type and extension $DST.
+
+LANGUAGE=en # change accordingly, if converting custom sounds you may want to omit this variable
+SRC=gsm # change accordingly (e.g. to wav, etc.)
+DST=g729 # change accordingly (e.g. to wav, etc.)
+SOUNDS=/var/lib/asterisk/sounds # for custom sounds change this directory to your custom sound directory
+
+for file in $(find ${SOUNDS}/${LANGUAGE}/ -depth -type f -name *.${SRC});
+do
+ #echo $file
+ asterisk -rx "file convert $file $(dirname $file)/$(basename $file $SRC)$DST"
+done
diff --git a/funcs/func_aes.c b/funcs/func_aes.c
index 64597a9b4..891106b6f 100644
--- a/funcs/func_aes.c
+++ b/funcs/func_aes.c
@@ -23,7 +23,7 @@
*/
/*** MODULEINFO
- <use>crypto</use>
+ <use type="external">crypto</use>
***/
#include "asterisk.h"
diff --git a/funcs/func_speex.c b/funcs/func_speex.c
index 51cea99e1..e17e8ca9c 100644
--- a/funcs/func_speex.c
+++ b/funcs/func_speex.c
@@ -33,7 +33,7 @@
/*** MODULEINFO
<depend>speex</depend>
<depend>speex_preprocess</depend>
- <use>speexdsp</use>
+ <use type="external">speexdsp</use>
***/
#include "asterisk.h"
diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h
index 58f61d6fd..849f88741 100644
--- a/include/asterisk/bridging.h
+++ b/include/asterisk/bridging.h
@@ -167,12 +167,48 @@ struct ast_bridge_channel {
AST_LIST_ENTRY(ast_bridge_channel) entry;
};
+enum ast_bridge_video_mode_type {
+ /*! Video is not allowed in the bridge */
+ AST_BRIDGE_VIDEO_MODE_NONE = 0,
+ /*! A single user is picked as the only distributed of video across the bridge */
+ AST_BRIDGE_VIDEO_MODE_SINGLE_SRC,
+ /*! A single user's video feed is distributed to all bridge channels, but
+ * that feed is automatically picked based on who is talking the most. */
+ AST_BRIDGE_VIDEO_MODE_TALKER_SRC,
+};
+
+/*! This is used for both SINGLE_SRC mode to set what channel
+ * should be the current single video feed */
+struct ast_bridge_video_single_src_data {
+ /*! Only accept video coming from this channel */
+ struct ast_channel *chan_vsrc;
+};
+
+/*! This is used for both SINGLE_SRC_TALKER mode to set what channel
+ * should be the current single video feed */
+struct ast_bridge_video_talker_src_data {
+ /*! Only accept video coming from this channel */
+ struct ast_channel *chan_vsrc;
+ int average_talking_energy;
+};
+
+struct ast_bridge_video_mode {
+ enum ast_bridge_video_mode_type mode;
+ /* Add data for all the video modes here. */
+ union {
+ struct ast_bridge_video_single_src_data single_src_data;
+ struct ast_bridge_video_talker_src_data talker_src_data;
+ } mode_data;
+};
+
/*!
* \brief Structure that contains information about a bridge
*/
struct ast_bridge {
/*! Number of channels participating in the bridge */
int num;
+ /*! The video mode this bridge is using */
+ struct ast_bridge_video_mode video_mode;
/*! The internal sample rate this bridge is mixed at when multiple channels are being mixed.
* If this value is 0, the bridge technology may auto adjust the internal mixing rate. */
unsigned int internal_sample_rate;
@@ -475,6 +511,31 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
*/
void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval);
+/*!
+ * \brief Set a bridge to feed a single video source to all participants.
+ */
+void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan);
+
+/*!
+ * \brief Set the bridge to pick the strongest talker supporting
+ * video as the single source video feed
+ */
+void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge);
+
+/*!
+ * \brief Update information about talker energy for talker src video mode.
+ */
+void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyfame);
+
+/*!
+ * \brief Determine if a channel is a video src for the bridge
+ */
+int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*!
+ * \brief remove a channel as a source of video for the bridge.
+ */
+void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
#if defined(__cplusplus) || defined(c_plusplus)
}
diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h
index 79e4da695..333415200 100644
--- a/include/asterisk/dsp.h
+++ b/include/asterisk/dsp.h
@@ -109,6 +109,11 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
number of seconds of silence */
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence);
+/*! \brief Return non-zero if this is silence. Updates "totalsilence" with the total
+ number of seconds of silence. Returns the average energy of the samples in the frame
+ in frames_energy variable. */
+int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy);
+
/*!
* \brief Return non-zero if this is noise. Updates "totalnoise" with the total
* number of seconds of noise
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 8189c648c..6e8c17779 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -334,45 +334,51 @@ enum ast_control_transfer {
#define AST_OPTION_FLAG_WTF 6
/*! Verify touchtones by muting audio transmission
- (and reception) and verify the tone is still present */
+ * (and reception) and verify the tone is still present
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_TONE_VERIFY 1
-/*! Put a compatible channel into TDD (TTY for the hearing-impared) mode */
+/*! Put a compatible channel into TDD (TTY for the hearing-impared) mode
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_TDD 2
-/*! Relax the parameters for DTMF reception (mainly for radio use) */
+/*! Relax the parameters for DTMF reception (mainly for radio use)
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_RELAXDTMF 3
-/*! Set (or clear) Audio (Not-Clear) Mode */
+/*! Set (or clear) Audio (Not-Clear) Mode
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_AUDIO_MODE 4
/*! Set channel transmit gain
- * Option data is a single signed char
- representing number of decibels (dB)
- to set gain to (on top of any gain
- specified in channel driver)
-*/
+ * Option data is a single signed char representing number of decibels (dB)
+ * to set gain to (on top of any gain specified in channel driver) */
#define AST_OPTION_TXGAIN 5
/*! Set channel receive gain
- * Option data is a single signed char
- representing number of decibels (dB)
- to set gain to (on top of any gain
- specified in channel driver)
-*/
+ * Option data is a single signed char representing number of decibels (dB)
+ * to set gain to (on top of any gain specified in channel driver) */
#define AST_OPTION_RXGAIN 6
-/* set channel into "Operator Services" mode */
+/* set channel into "Operator Services" mode
+ * Option data is a struct oprmode
+ *
+ * \note This option should never be sent over the network */
#define AST_OPTION_OPRMODE 7
-/*! Explicitly enable or disable echo cancelation for the given channel */
+/*! Explicitly enable or disable echo cancelation for the given channel
+ * Option data is a single signed char value 0 or 1
+ *
+ * \note This option appears to be unused in the code. It is handled, but never
+ * set or queried. */
#define AST_OPTION_ECHOCAN 8
/*! \brief Handle channel write data
* If a channel needs to process the data from a func_channel write operation
* after func_channel_write executes, it can define the setoption callback
* and process this option. A pointer to an ast_chan_write_info_t will be passed.
- * */
+ *
+ * \note This option should never be passed over the network. */
#define AST_OPTION_CHANNEL_WRITE 9
/* !
@@ -381,28 +387,38 @@ enum ast_control_transfer {
*/
#define AST_OPTION_T38_STATE 10
-/*! Request that the channel driver deliver frames in a specific format */
+/*! Request that the channel driver deliver frames in a specific format
+ * Option data is a format_t */
#define AST_OPTION_FORMAT_READ 11
-/*! Request that the channel driver be prepared to accept frames in a specific format */
+/*! Request that the channel driver be prepared to accept frames in a specific format
+ * Option data is a format_t */
#define AST_OPTION_FORMAT_WRITE 12
-/*! Request that the channel driver make two channels of the same tech type compatible if possible */
+/*! Request that the channel driver make two channels of the same tech type compatible if possible
+ * Option data is an ast_channel
+ *
+ * \note This option should never be passed over the network */
#define AST_OPTION_MAKE_COMPATIBLE 13
-/*! Get or set the digit detection state of the channel */
+/*! Get or set the digit detection state of the channel
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_DIGIT_DETECT 14
-/*! Get or set the fax tone detection state of the channel */
+/*! Get or set the fax tone detection state of the channel
+ * Option data is a single signed char value 0 or 1 */
#define AST_OPTION_FAX_DETECT 15
-/*! Get the device name from the channel */
+/*! Get the device name from the channel (Read only)
+ * Option data is a character buffer of suitable length */
#define AST_OPTION_DEVICE_NAME 16
-/*! Get the CC agent type from the channel */
+/*! Get the CC agent type from the channel (Read only)
+ * Option data is a character buffer of suitable length */
#define AST_OPTION_CC_AGENT_TYPE 17
-/*! Get or set the security options on a channel */
+/*! Get or set the security options on a channel
+ * Option data is an integer value of 0 or 1 */
#define AST_OPTION_SECURE_SIGNALING 18
#define AST_OPTION_SECURE_MEDIA 19
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index 322214e73..a2a28809f 100644
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -181,7 +181,7 @@ void ast_console_toggle_loglevel(int fd, int level, int state);
#endif
#define AST_LOG_DTMF __LOG_DTMF, _A_
-#define NUMLOGLEVELS 6
+#define NUMLOGLEVELS 7
/*!
* \brief Get the debug level for a module
diff --git a/include/asterisk/netsock2.h b/include/asterisk/netsock2.h
index c5c08cf73..afb1ea90f 100644
--- a/include/asterisk/netsock2.h
+++ b/include/asterisk/netsock2.h
@@ -152,8 +152,13 @@ int ast_sockaddr_cmp_addr(const struct ast_sockaddr *a, const struct ast_sockadd
#define AST_SOCKADDR_STR_ADDR (1 << 0)
#define AST_SOCKADDR_STR_PORT (1 << 1)
#define AST_SOCKADDR_STR_BRACKETS (1 << 2)
-#define AST_SOCKADDR_STR_HOST AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_BRACKETS
-#define AST_SOCKADDR_STR_DEFAULT AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT
+#define AST_SOCKADDR_STR_REMOTE (1 << 3)
+#define AST_SOCKADDR_STR_HOST (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_BRACKETS)
+#define AST_SOCKADDR_STR_DEFAULT (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT)
+#define AST_SOCKADDR_STR_ADDR_REMOTE (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_REMOTE)
+#define AST_SOCKADDR_STR_HOST_REMOTE (AST_SOCKADDR_STR_HOST | AST_SOCKADDR_STR_REMOTE)
+#define AST_SOCKADDR_STR_DEFAULT_REMOTE (AST_SOCKADDR_STR_DEFAULT | AST_SOCKADDR_STR_REMOTE)
+#define AST_SOCKADDR_STR_FORMAT_MASK (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT | AST_SOCKADDR_STR_BRACKETS)
/*!
* \since 1.8
@@ -203,6 +208,23 @@ static inline char *ast_sockaddr_stringify(const struct ast_sockaddr *addr)
* \since 1.8
*
* \brief
+ * Wrapper around ast_sockaddr_stringify_fmt() with default format
+ *
+ * \note This address will be suitable for passing to a remote machine via the
+ * application layer. For example, the scope-id on a link-local IPv6 address
+ * will be stripped.
+ *
+ * \return same as ast_sockaddr_stringify_fmt()
+ */
+static inline char *ast_sockaddr_stringify_remote(const struct ast_sockaddr *addr)
+{
+ return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_DEFAULT_REMOTE);
+}
+
+/*!
+ * \since 1.8
+ *
+ * \brief
* Wrapper around ast_sockaddr_stringify_fmt() to return an address only
*
* \return same as ast_sockaddr_stringify_fmt()
@@ -216,6 +238,23 @@ static inline char *ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
* \since 1.8
*
* \brief
+ * Wrapper around ast_sockaddr_stringify_fmt() to return an address only
+ *
+ * \note This address will be suitable for passing to a remote machine via the
+ * application layer. For example, the scope-id on a link-local IPv6 address
+ * will be stripped.
+ *
+ * \return same as ast_sockaddr_stringify_fmt()
+ */
+static inline char *ast_sockaddr_stringify_addr_remote(const struct ast_sockaddr *addr)
+{
+ return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_ADDR_REMOTE);
+}
+
+/*!
+ * \since 1.8
+ *
+ * \brief
* Wrapper around ast_sockaddr_stringify_fmt() to return an address only,
* suitable for a URL (with brackets for IPv6).
*
@@ -230,6 +269,24 @@ static inline char *ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
* \since 1.8
*
* \brief
+ * Wrapper around ast_sockaddr_stringify_fmt() to return an address only,
+ * suitable for a URL (with brackets for IPv6).
+ *
+ * \note This address will be suitable for passing to a remote machine via the
+ * application layer. For example, the scope-id on a link-local IPv6 address
+ * will be stripped.
+ *
+ * \return same as ast_sockaddr_stringify_fmt()
+ */
+static inline char *ast_sockaddr_stringify_host_remote(const struct ast_sockaddr *addr)
+{
+ return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_HOST_REMOTE);
+}
+
+/*!
+ * \since 1.8
+ *
+ * \brief
* Wrapper around ast_sockaddr_stringify_fmt() to return a port only
*
* \return same as ast_sockaddr_stringify_fmt()
@@ -413,6 +470,20 @@ int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr);
* \since 1.8
*
* \brief
+ * Determine if this is a link-local IPv6 address
+ *
+ * \warning You should rarely need this function. Only use if you know what
+ * you're doing.
+ *
+ * \retval 1 This is a link-local IPv6 address.
+ * \retval 0 This is link-local IPv6 address.
+ */
+int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr);
+
+/*!
+ * \since 1.8
+ *
+ * \brief
* Determine if this is an IPv6 address
*
* \warning You should rarely need this function. Only use if you know what
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index 429e60537..1489ee2d6 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -877,6 +877,8 @@ int ast_context_unlockmacro(const char *macrocontext);
/*!
* \brief Set the channel to next execute the specified dialplan location.
* \see ast_async_parseable_goto, ast_async_goto_if_exists
+ *
+ * \note Do _NOT_ hold any channel locks when calling this function.
*/
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index 9a52115f0..5d2c903ff 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -42,6 +42,8 @@ enum ast_fax_capabilities {
AST_FAX_TECH_T38 = (1 << 3),
/*! sending mulitple documents supported */
AST_FAX_TECH_MULTI_DOC = (1 << 4),
+ /*! T.38 - T.30 Gateway */
+ AST_FAX_TECH_GATEWAY = (1 << 5),
};
/*! \brief fax modem capabilities */
@@ -168,6 +170,8 @@ struct ast_fax_session_details {
struct ast_fax_t38_parameters our_t38_parameters;
/*! the other endpoint's T.38 session parameters, if any */
struct ast_fax_t38_parameters their_t38_parameters;
+ /*! the id of the t.38 gateway framehook for this channel */
+ int gateway_id;
};
struct ast_fax_tech;
@@ -204,6 +208,9 @@ struct ast_fax_session {
struct ast_smoother *smoother;
};
+/* if this overlaps with any AST_FRFLAG_* values, problems will occur */
+#define AST_FAX_FRFLAG_GATEWAY (1 << 13)
+
/*! \brief used to register a FAX technology module with res_fax */
struct ast_fax_tech {
/*! the type of fax session supported with this ast_fax_tech structure */
diff --git a/main/asterisk.c b/main/asterisk.c
index 7b93294ca..a325fec85 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -1484,7 +1484,7 @@ static struct sigaction urg_handler = {
static void _hup_handler(int num)
{
- int a = 0;
+ int a = 0, save_errno = errno;
if (option_verbose > 1)
printf("Received HUP signal -- Reloading configs\n");
if (restartnow)
@@ -1495,6 +1495,7 @@ static void _hup_handler(int num)
fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno));
}
}
+ errno = save_errno;
}
static struct sigaction hup_handler = {
@@ -1505,7 +1506,7 @@ static struct sigaction hup_handler = {
static void _child_handler(int sig)
{
/* Must not ever ast_log or ast_verbose within signal handler */
- int n, status;
+ int n, status, save_errno = errno;
/*
* Reap all dead children -- not just one
@@ -1514,6 +1515,7 @@ static void _child_handler(int sig)
;
if (n == 0 && option_debug)
printf("Huh? Child handler, but nobody there?\n");
+ errno = save_errno;
}
static struct sigaction child_handler = {
diff --git a/main/bridging.c b/main/bridging.c
index 444eea8d5..1563e4789 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -50,6 +50,8 @@ static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
/* Grow rate of bridge array of channels */
#define BRIDGE_ARRAY_GROW 32
+static void cleanup_video_mode(struct ast_bridge *bridge);
+
/*! Default DTMF keys for built in features */
static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
@@ -457,6 +459,8 @@ static void destroy_bridge(void *obj)
/* Drop the array of channels */
ast_free(bridge->array);
+ cleanup_video_mode(bridge);
+
return;
}
@@ -1470,3 +1474,116 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
bridge->internal_sample_rate = sample_rate;
ao2_unlock(bridge);
}
+
+static void cleanup_video_mode(struct ast_bridge *bridge)
+{
+ switch (bridge->video_mode.mode) {
+ case AST_BRIDGE_VIDEO_MODE_NONE:
+ break;
+ case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
+ if (bridge->video_mode.mode_data.single_src_data.chan_vsrc) {
+ ast_channel_unref(bridge->video_mode.mode_data.single_src_data.chan_vsrc);
+ }
+ break;
+ case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
+ if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc) {
+ ast_channel_unref(bridge->video_mode.mode_data.talker_src_data.chan_vsrc);
+ }
+ }
+ memset(&bridge->video_mode, 0, sizeof(bridge->video_mode));
+}
+
+void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
+{
+ ao2_lock(bridge);
+ cleanup_video_mode(bridge);
+ bridge->video_mode.mode = AST_BRIDGE_VIDEO_MODE_SINGLE_SRC;
+ bridge->video_mode.mode_data.single_src_data.chan_vsrc = ast_channel_ref(video_src_chan);
+ ast_indicate(video_src_chan, AST_CONTROL_VIDUPDATE);
+ ao2_unlock(bridge);
+}
+
+void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
+{
+ ao2_lock(bridge);
+ cleanup_video_mode(bridge);
+ bridge->video_mode.mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC;
+ ao2_unlock(bridge);
+}
+
+void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyframe)
+{
+ struct ast_bridge_video_talker_src_data *data;
+ /* If the channel doesn't support video, we don't care about it */
+ if (!ast_format_cap_has_type(chan->nativeformats, AST_FORMAT_TYPE_VIDEO)) {
+ return;
+ }
+
+ ao2_lock(bridge);
+ data = &bridge->video_mode.mode_data.talker_src_data;
+
+ if (data->chan_vsrc == chan) {
+ data->average_talking_energy = talker_energy;
+ } else if ((data->average_talking_energy < talker_energy) && is_keyframe) {
+ if (data->chan_vsrc) {
+ ast_channel_unref(data->chan_vsrc);
+ }
+ data->chan_vsrc = ast_channel_ref(chan);
+ data->average_talking_energy = talker_energy;
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+ } else if ((data->average_talking_energy < talker_energy) && !is_keyframe) {
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+ } else if (!data->chan_vsrc && is_keyframe) {
+ data->chan_vsrc = ast_channel_ref(chan);
+ data->average_talking_energy = talker_energy;
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+ }
+ ao2_unlock(bridge);
+}
+
+int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ int res = 0;
+
+ ao2_lock(bridge);
+ switch (bridge->video_mode.mode) {
+ case AST_BRIDGE_VIDEO_MODE_NONE:
+ break;
+ case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
+ if (bridge->video_mode.mode_data.single_src_data.chan_vsrc == chan) {
+ res = 1;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
+ if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
+ res = 1;
+ }
+ }
+ ao2_unlock(bridge);
+ return res;
+}
+
+void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ ao2_lock(bridge);
+ switch (bridge->video_mode.mode) {
+ case AST_BRIDGE_VIDEO_MODE_NONE:
+ break;
+ case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
+ if (bridge->video_mode.mode_data.single_src_data.chan_vsrc == chan) {
+ if (bridge->video_mode.mode_data.single_src_data.chan_vsrc) {
+ ast_channel_unref(bridge->video_mode.mode_data.single_src_data.chan_vsrc);
+ }
+ bridge->video_mode.mode_data.single_src_data.chan_vsrc = NULL;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
+ if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
+ if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc) {
+ ast_channel_unref(bridge->video_mode.mode_data.talker_src_data.chan_vsrc);
+ }
+ bridge->video_mode.mode_data.talker_src_data.chan_vsrc = NULL;
+ }
+ }
+ ao2_unlock(bridge);
+}
diff --git a/main/channel.c b/main/channel.c
index 57629dbc5..ed84da1e6 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -7499,6 +7499,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
(c0->tech->bridge == c1->tech->bridge) &&
!c0->monitor && !c1->monitor &&
!c0->audiohooks && !c1->audiohooks &&
+ ast_framehook_list_is_empty(c0->framehooks) && ast_framehook_list_is_empty(c1->framehooks) &&
!c0->masq && !c0->masqr && !c1->masq && !c1->masqr) {
int timeoutms = to - 1000 > 0 ? to - 1000 : to;
/* Looks like they share a bridge method and nothing else is in the way */
diff --git a/main/dsp.c b/main/dsp.c
index 9e3e2e724..ee1891823 100644
--- a/main/dsp.c
+++ b/main/dsp.c
@@ -1103,7 +1103,7 @@ int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
return __ast_dsp_call_progress(dsp, inf->data.ptr, inf->datalen / 2);
}
-static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise)
+static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise, int *frames_energy)
{
int accum;
int x;
@@ -1163,6 +1163,9 @@ static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *
if (totalnoise) {
*totalnoise = dsp->totalnoise;
}
+ if (frames_energy) {
+ *frames_energy = accum;
+ }
return res;
}
@@ -1318,7 +1321,25 @@ int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
}
s = f->data.ptr;
len = f->datalen/2;
- return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL);
+ return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL, NULL);
+}
+
+int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy)
+{
+ short *s;
+ int len;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
+ return 0;
+ }
+ if (!ast_format_is_slinear(&f->subclass.format)) {
+ ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n");
+ return 0;
+ }
+ s = f->data.ptr;
+ len = f->datalen/2;
+ return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL, frames_energy);
}
int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
@@ -1336,7 +1357,7 @@ int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
}
s = f->data.ptr;
len = f->datalen/2;
- return __ast_dsp_silence_noise(dsp, s, len, NULL, totalnoise);
+ return __ast_dsp_silence_noise(dsp, s, len, NULL, totalnoise, NULL);
}
@@ -1393,7 +1414,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
/* Need to run the silence detection stuff for silence suppression and busy detection */
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) || (dsp->features & DSP_FEATURE_BUSY_DETECT)) {
- res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL);
+ res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL, NULL);
}
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
diff --git a/main/features.c b/main/features.c
index 728118188..06ec1b698 100644
--- a/main/features.c
+++ b/main/features.c
@@ -3740,10 +3740,21 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
break;
case AST_CONTROL_OPTION:
aoh = f->data.ptr;
- /* Forward option Requests */
+ /* Forward option Requests, but only ones we know are safe
+ * These are ONLY sent by chan_iax2 and I'm not convinced that
+ * they are useful. I haven't deleted them entirely because I
+ * just am not sure of the ramifications of removing them. */
if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
- ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
- f->datalen - sizeof(struct ast_option_header), 0);
+ switch (ntohs(aoh->option)) {
+ case AST_OPTION_TONE_VERIFY:
+ case AST_OPTION_TDD:
+ case AST_OPTION_RELAXDTMF:
+ case AST_OPTION_AUDIO_MODE:
+ case AST_OPTION_DIGIT_DETECT:
+ case AST_OPTION_FAX_DETECT:
+ ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
+ f->datalen - sizeof(struct ast_option_header), 0);
+ }
}
break;
}
diff --git a/main/file.c b/main/file.c
index ffae33b5a..6912a37bc 100644
--- a/main/file.c
+++ b/main/file.c
@@ -29,6 +29,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <dirent.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <math.h>
#include "asterisk/_private.h" /* declare ast_file_init() */
@@ -289,6 +290,8 @@ static int exts_compare(const char *exts, const char *type)
static void filestream_destructor(void *arg)
{
struct ast_filestream *f = arg;
+ int status;
+ int pid = -1;
/* Stop a running stream if there is one */
if (f->owner) {
@@ -306,8 +309,14 @@ static void filestream_destructor(void *arg)
ast_translator_free_path(f->trans);
if (f->realfilename && f->filename) {
- if (ast_safe_fork(0) == 0) {
+ pid = ast_safe_fork(0);
+ if (!pid) {
execl("/bin/mv", "mv", "-f", f->filename, f->realfilename, SENTINEL);
+ _exit(1);
+ }
+ else if (pid > 0) {
+ /* Block the parent until the move is complete.*/
+ waitpid(pid, &status, 0);
}
}
diff --git a/main/frame.c b/main/frame.c
index d82a46313..bb32386ca 100644
--- a/main/frame.c
+++ b/main/frame.c
@@ -381,7 +381,7 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
out->samples = fr->samples;
out->offset = fr->offset;
/* Copy the timing data */
- ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO);
+ ast_copy_flags(out, fr, AST_FLAGS_ALL);
if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
out->ts = fr->ts;
out->len = fr->len;
@@ -505,7 +505,7 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
/* Must have space since we allocated for it */
strcpy(src, f->src);
}
- ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO);
+ ast_copy_flags(out, f, AST_FLAGS_ALL);
out->ts = f->ts;
out->len = f->len;
out->seqno = f->seqno;
diff --git a/main/manager.c b/main/manager.c
index c4d1e1101..5b3da313d 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -134,6 +134,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="ActionID">
<para>ActionID for this transaction. Will be returned.</para>
</parameter>
+ <parameter name="Username" required="true">
+ <para>Username to login with as specified in manager.conf.</para>
+ </parameter>
+ <parameter name="Secret">
+ <para>Secret to login with as specified in manager.conf.</para>
+ </parameter>
</syntax>
<description>
<para>Login Manager.</para>
@@ -806,6 +812,52 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Generates an AOC-D or AOC-E message on a channel.</para>
</description>
</manager>
+ <manager name="Filter" language="en_US">
+ <synopsis>
+ Dynamically add filters for the current manager session.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Operation">
+ <enumlist>
+ <enum name="Add">
+ <para>Add a filter.</para>
+ </enum>
+ </enumlist>
+ </parameter>
+ <parameter name="Filter">
+ <para>Filters can be whitelist or blacklist</para>
+ <para>Example whitelist filter: "Event: Newchannel"</para>
+ <para>Example blacklist filter: "!Channel: DAHDI.*"</para>
+ <para>This filter option is used to whitelist or blacklist events per user to be
+ reported with regular expressions and are allowed if both the regex matches
+ and the user has read access as defined in manager.conf. Filters are assumed to be for whitelisting
+ unless preceeded by an exclamation point, which marks it as being black.
+ Evaluation of the filters is as follows:</para>
+ <para>- If no filters are configured all events are reported as normal.</para>
+ <para>- If there are white filters only: implied black all filter processed first, then white filters.</para>
+ <para>- If there are black filters only: implied white all filter processed first, then black filters.</para>
+ <para>- If there are both white and black filters: implied black all filter processed first, then white
+ filters, and lastly black filters.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>The filters added are only used for the current session.
+ Once the connection is closed the filters are removed.</para>
+ <para>This comand requires the system permission because
+ this command can be used to create filters that may bypass
+ filters defined in manager.conf</para>
+ </description>
+ </manager>
+ <manager name="FilterList" language="en_US">
+ <synopsis>
+ Show current event filters for this session
+ </synopsis>
+ <description>
+ <para>The filters displayed are for the current session. Only those filters defined in
+ manager.conf will be present upon starting a new session.</para>
+ </description>
+ </manager>
***/
enum error_type {
@@ -822,6 +874,11 @@ enum error_type {
FAILURE_APPEND
};
+enum add_filter_result {
+ FILTER_SUCCESS,
+ FILTER_ALLOC_FAILED,
+ FILTER_COMPILE_FAIL,
+};
/*!
* Linked list of events.
@@ -1013,6 +1070,8 @@ static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
static void free_channelvars(void);
+static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters);
+
/*! \brief Add a custom hook to be called when an event is fired */
void ast_manager_register_hook(struct manager_custom_hook *hook)
{
@@ -4116,6 +4175,88 @@ static int blackfilter_cmp_fn(void *obj, void *arg, void *data, int flags)
return 0;
}
+/*
+ * \brief Manager command to add an event filter to a manager session
+ * \see For more details look at manager_add_filter
+ */
+static int action_filter(struct mansession *s, const struct message *m)
+{
+ const char *filter = astman_get_header(m, "Filter");
+ const char *operation = astman_get_header(m, "Operation");
+ int res;
+
+ if (!strcasecmp(operation, "Add")) {
+ res = manager_add_filter(filter, s->session->whitefilters, s->session->blackfilters);
+
+ if (res != FILTER_SUCCESS) {
+ if (res == FILTER_ALLOC_FAILED) {
+ astman_send_error(s, m, "Internal Error. Failed to allocate regex for filter");
+ return 0;
+ } else if (res == FILTER_COMPILE_FAIL) {
+ astman_send_error(s, m, "Filter did not compile. Check the syntax of the filter given.");
+ return 0;
+ } else {
+ astman_send_error(s, m, "Internal Error. Failed adding filter.");
+ return 0;
+ }
+ }
+
+ astman_send_ack(s, m, "Success");
+ return 0;
+ }
+
+ astman_send_error(s, m, "Unknown operation");
+ return 0;
+}
+
+/*
+ * \brief Add an event filter to a manager session
+ *
+ * \param s manager session to modify filters on
+ * \param filter_pattern Filter syntax to add, see below for syntax
+ *
+ * \return FILTER_ALLOC_FAILED Memory allocation failure
+ * \return FILTER_COMPILE_FAIL If the filter did not compile
+ * \return FILTER_SUCCESS Success
+ *
+ * Filter will be used to match against each line of a manager event
+ * Filter can be any valid regular expression
+ * Filter can be a valid regular expression prefixed with !, which will add the filter as a black filter
+ *
+ * \example filter_pattern = "Event: Newchannel"
+ * \example filter_pattern = "Event: New.*"
+ * \example filter_pattern = "!Channel: DAHDI.*"
+ *
+ */
+static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters) {
+ regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
+ int is_blackfilter;
+
+ if (!new_filter) {
+ return FILTER_ALLOC_FAILED;
+ }
+
+ if (filter_pattern[0] == '!') {
+ is_blackfilter = 1;
+ filter_pattern++;
+ } else {
+ is_blackfilter = 0;
+ }
+
+ if (regcomp(new_filter, filter_pattern, 0)) {
+ ao2_t_ref(new_filter, -1, "failed to make regx");
+ return FILTER_COMPILE_FAIL;
+ }
+
+ if (is_blackfilter) {
+ ao2_t_link(blackfilters, new_filter, "link new filter into black user container");
+ } else {
+ ao2_t_link(whitefilters, new_filter, "link new filter into white user container");
+ }
+
+ return FILTER_SUCCESS;
+}
+
static int match_filter(struct mansession *s, char *eventdata)
{
int result = 0;
@@ -6278,6 +6419,7 @@ static int __init_manager(int reload)
ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
+ ast_manager_register_xml("Filter", EVENT_FLAG_SYSTEM, action_filter);
ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
@@ -6557,25 +6699,7 @@ static int __init_manager(int reload)
}
} else if (!strcasecmp(var->name, "eventfilter")) {
const char *value = var->value;
- regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
- if (new_filter) {
- int is_blackfilter;
- if (value[0] == '!') {
- is_blackfilter = 1;
- value++;
- } else {
- is_blackfilter = 0;
- }
- if (regcomp(new_filter, value, 0)) {
- ao2_t_ref(new_filter, -1, "failed to make regx");
- } else {
- if (is_blackfilter) {
- ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
- } else {
- ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
- }
- }
- }
+ manager_add_filter(value, user->whitefilters, user->blackfilters);
} else {
ast_debug(1, "%s is an unknown option.\n", var->name);
}
diff --git a/main/netsock2.c b/main/netsock2.c
index d6561fba2..4ac1d0ffb 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -95,7 +95,14 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
return "";
}
- switch (format) {
+ if ((format & AST_SOCKADDR_STR_REMOTE) == AST_SOCKADDR_STR_REMOTE) {
+ char *p;
+ if (ast_sockaddr_is_ipv6_link_local(sa) && (p = strchr(host, '%'))) {
+ *p = '\0';
+ }
+ }
+
+ switch ((format & AST_SOCKADDR_STR_FORMAT_MASK)) {
case AST_SOCKADDR_STR_DEFAULT:
ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ?
"[%s]:%s" : "%s:%s", host, port);
@@ -397,6 +404,12 @@ int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr)
return ((ast_sockaddr_ipv4(addr) & 0xf0000000) == 0xe0000000);
}
+int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr)
+{
+ const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr->ss;
+ return ast_sockaddr_is_ipv6(addr) && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr);
+}
+
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
{
return addr->ss.ss_family == AF_INET6 &&
diff --git a/main/pbx.c b/main/pbx.c
index b9c853505..821fdbfad 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -252,7 +252,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>If the location that is put into the channel information is bogus, and asterisk cannot
find that location in the dialplan, then the execution engine will try to find and execute the code in
the <literal>i</literal> (invalid) extension in the current context. If that does not exist, it will try to execute the
- <literal>h</literal> extension. If either or neither the <literal>h</literal> or <literal>i</literal> extensions
+ <literal>h</literal> extension. If neither the <literal>h</literal> nor <literal>i</literal> extensions
have been defined, the channel is hung up, and the execution of instructions on the channel is terminated.
What this means is that, for example, you specify a context that does not exist, then
it will not be possible to find the <literal>h</literal> or <literal>i</literal> extensions,
@@ -289,7 +289,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
next instruction. If the target location is bogus, and does not exist, the execution engine will try
to find and execute the code in the <literal>i</literal> (invalid) extension in the current context.
If that does not exist, it will try to execute the <literal>h</literal> extension.
- If either or neither the <literal>h</literal> or <literal>i</literal> extensions have been defined,
+ If neither the <literal>h</literal> nor <literal>i</literal> extensions have been defined,
the channel is hung up, and the execution of instructions on the channel is terminated.
Remember that this command can set the current context, and if the context specified
does not exist, then it will not be able to find any 'h' or 'i' extensions there, and
@@ -7949,55 +7949,82 @@ int ast_explicit_goto(struct ast_channel *chan, const char *context, const char
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
{
int res = 0;
+ struct ast_channel *tmpchan;
+ struct {
+ char *accountcode;
+ char *exten;
+ char *context;
+ char *linkedid;
+ char *name;
+ struct ast_cdr *cdr;
+ int amaflags;
+ int state;
+ struct ast_format readformat;
+ struct ast_format writeformat;
+ } tmpvars = { 0, };
ast_channel_lock(chan);
-
if (chan->pbx) { /* This channel is currently in the PBX */
ast_explicit_goto(chan, context, exten, priority + 1);
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
+ ast_channel_unlock(chan);
+ return res;
+ }
+
+ /* In order to do it when the channel doesn't really exist within
+ * the PBX, we have to make a new channel, masquerade, and start the PBX
+ * at the new location */
+ tmpvars.accountcode = ast_strdupa(chan->accountcode);
+ tmpvars.exten = ast_strdupa(chan->exten);
+ tmpvars.context = ast_strdupa(chan->context);
+ tmpvars.linkedid = ast_strdupa(chan->linkedid);
+ tmpvars.name = ast_strdupa(chan->name);
+ tmpvars.amaflags = chan->amaflags;
+ tmpvars.state = chan->_state;
+ ast_format_copy(&tmpvars.writeformat, &chan->writeformat);
+ ast_format_copy(&tmpvars.readformat, &chan->readformat);
+ tmpvars.cdr = chan->cdr ? ast_cdr_dup(chan->cdr) : NULL;
+
+ ast_channel_unlock(chan);
+
+ /* Do not hold any channel locks while calling channel_alloc() since the function
+ * locks the channel container when linking the new channel in. */
+ if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode, tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags, "AsyncGoto/%s", tmpvars.name))) {
+ ast_cdr_discard(tmpvars.cdr);
+ return -1;
+ }
+
+ /* copy the cdr info over */
+ if (tmpvars.cdr) {
+ ast_cdr_discard(tmpchan->cdr);
+ tmpchan->cdr = tmpvars.cdr;
+ tmpvars.cdr = NULL;
+ }
+
+ /* Make formats okay */
+ ast_format_copy(&tmpchan->readformat, &tmpvars.readformat);
+ ast_format_copy(&tmpchan->writeformat, &tmpvars.writeformat);
+
+ /* Setup proper location. Never hold another channel lock while calling this function. */
+ ast_explicit_goto(tmpchan, S_OR(context, tmpvars.context), S_OR(exten, tmpvars.exten), priority);
+
+ /* Masquerade into tmp channel */
+ if (ast_channel_masquerade(tmpchan, chan)) {
+ /* Failed to set up the masquerade. It's probably chan_local
+ * in the middle of optimizing itself out. Sad. :( */
+ ast_hangup(tmpchan);
+ tmpchan = NULL;
+ res = -1;
} else {
- /* In order to do it when the channel doesn't really exist within
- the PBX, we have to make a new channel, masquerade, and start the PBX
- at the new location */
- struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->linkedid, chan->amaflags, "AsyncGoto/%s", chan->name);
- if (!tmpchan) {
+ ast_do_masquerade(tmpchan);
+ /* Start the PBX going on our stolen channel */
+ if (ast_pbx_start(tmpchan)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
+ ast_hangup(tmpchan);
res = -1;
- } else {
- if (chan->cdr) {
- ast_cdr_discard(tmpchan->cdr);
- tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
- }
- /* Make formats okay */
- tmpchan->readformat = chan->readformat;
- tmpchan->writeformat = chan->writeformat;
- /* Setup proper location */
- ast_explicit_goto(tmpchan,
- S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
-
- /* Masquerade into temp channel */
- if (ast_channel_masquerade(tmpchan, chan)) {
- /* Failed to set up the masquerade. It's probably chan_local
- * in the middle of optimizing itself out. Sad. :( */
- ast_hangup(tmpchan);
- tmpchan = NULL;
- res = -1;
- } else {
- /* it may appear odd to unlock chan here since the masquerade is on
- * tmpchan, but no channel locks should be held when doing a masquerade
- * since a masquerade requires a lock on the channels ao2 container. */
- ast_channel_unlock(chan);
- ast_do_masquerade(tmpchan);
- ast_channel_lock(chan);
- /* Start the PBX going on our stolen channel */
- if (ast_pbx_start(tmpchan)) {
- ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
- ast_hangup(tmpchan);
- res = -1;
- }
- }
}
}
- ast_channel_unlock(chan);
+
return res;
}
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index a1c460578..a3d9c4d8e 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/translate.h"
#include "asterisk/netsock2.h"
#include "asterisk/_private.h"
+#include "asterisk/framehook.h"
struct ast_srtp_res *res_srtp = NULL;
struct ast_srtp_policy_res *res_srtp_policy = NULL;
@@ -853,7 +854,8 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
if ((c0->tech_pvt != pvt0) ||
(c1->tech_pvt != pvt1) ||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
- (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks) ||
+ (!ast_framehook_list_is_empty(c0->framehooks) || !ast_framehook_list_is_empty(c1->framehooks))) {
ast_debug(1, "rtp-engine-local-bridge: Oooh, something is weird, backing out\n");
/* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */
if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) {
@@ -1046,7 +1048,8 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
if ((c0->tech_pvt != pvt0) ||
(c1->tech_pvt != pvt1) ||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
- (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks) ||
+ (!ast_framehook_list_is_empty(c0->framehooks) || !ast_framehook_list_is_empty(c1->framehooks))) {
ast_debug(1, "Oooh, something is weird, backing out\n");
res = AST_BRIDGE_RETRY;
break;
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index f65869e92..e02ef9de6 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -1439,10 +1439,16 @@ static int pbx_load_config(const char *config_file)
char *tc = NULL;
char realext[256] = "";
char *stringp, *ext;
+ const char *vfile;
+
+ /* get filename for error reporting from top level or an #include */
+ vfile = !*v->file ? config_file : v->file;
if (!strncasecmp(v->name, "same", 4)) {
if (ast_strlen_zero(lastextension)) {
- ast_log(LOG_ERROR, "No previous pattern in the first entry of context '%s' to match '%s' at line %d!\n", cxt, v->name, v->lineno);
+ ast_log(LOG_ERROR,
+ "No previous pattern in the first entry of context '%s' to match '%s' at line %d of %s!\n",
+ cxt, v->name, v->lineno, vfile);
continue;
}
if ((stringp = tc = ast_strdup(v->value))) {
@@ -1473,7 +1479,9 @@ process_extension:
if ((end = strchr(label, ')'))) {
*end = '\0';
} else {
- ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
+ ast_log(LOG_WARNING,
+ "Label missing trailing ')' at line %d of %s\n",
+ v->lineno, vfile);
ast_free(tc);
continue;
}
@@ -1487,7 +1495,9 @@ process_extension:
if (lastpri > -2) {
ipri = lastpri + 1;
} else {
- ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry at line %d!\n", v->lineno);
+ ast_log(LOG_WARNING,
+ "Can't use 'next' priority on the first entry at line %d of %s!\n",
+ v->lineno, vfile);
ast_free(tc);
continue;
}
@@ -1495,18 +1505,23 @@ process_extension:
if (lastpri > -2) {
ipri = lastpri;
} else {
- ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry at line %d!\n", v->lineno);
+ ast_log(LOG_WARNING,
+ "Can't use 'same' priority on the first entry at line %d of %s!\n",
+ v->lineno, vfile);
ast_free(tc);
continue;
}
} else if (sscanf(pri, "%30d", &ipri) != 1 &&
(ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
- ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
+ ast_log(LOG_WARNING,
+ "Invalid priority/label '%s' at line %d of %s\n",
+ pri, v->lineno, vfile);
ipri = 0;
ast_free(tc);
continue;
} else if (ipri < 1) {
- ast_log(LOG_WARNING, "Invalid priority '%s' at line %d\n", pri, v->lineno);
+ ast_log(LOG_WARNING, "Invalid priority '%s' at line %d of %s\n",
+ pri, v->lineno, vfile);
ast_free(tc);
continue;
}
@@ -1537,7 +1552,9 @@ process_extension:
if ((end = strrchr(data, ')'))) {
*end = '\0';
} else {
- ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s' at line %d\n", appl, data, v->lineno);
+ ast_log(LOG_WARNING,
+ "No closing parenthesis found? '%s(%s' at line %d of %s\n",
+ appl, data, v->lineno, vfile);
}
}
ast_free(orig_appl);
@@ -1550,10 +1567,14 @@ process_extension:
}
lastpri = ipri;
if (!ast_opt_dont_warn && (!strcmp(realext, "_.") || !strcmp(realext, "_!"))) {
- ast_log(LOG_WARNING, "The use of '%s' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X%c' instead at line %d\n", realext, realext[1], v->lineno);
+ ast_log(LOG_WARNING,
+ "The use of '%s' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X%c' instead at line %d of %s\n",
+ realext, realext[1], v->lineno, vfile);
}
if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free_ptr, registrar)) {
- ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
+ ast_log(LOG_WARNING,
+ "Unable to register extension at line %d of %s\n",
+ v->lineno, vfile);
}
}
free(tc);
@@ -1561,35 +1582,40 @@ process_extension:
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
if (ast_context_add_include2(con, realvalue, registrar)) {
switch (errno) {
- case ENOMEM:
- ast_log(LOG_WARNING, "Out of memory for context addition\n");
- break;
-
- case EBUSY:
- ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
- break;
-
- case EEXIST:
- ast_log(LOG_WARNING, "Context '%s' already included in '%s' context on include at line %d\n",
- v->value, cxt, v->lineno);
- break;
-
- case ENOENT:
- case EINVAL:
- ast_log(LOG_WARNING, "There is no existence of context '%s' included at line %d\n",
- errno == ENOENT ? v->value : cxt, v->lineno);
- break;
-
- default:
- ast_log(LOG_WARNING, "Failed to include '%s' in '%s' context at line %d\n",
- v->value, cxt, v->lineno);
- break;
+ case ENOMEM:
+ ast_log(LOG_WARNING, "Out of memory for context addition\n");
+ break;
+
+ case EBUSY:
+ ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
+ break;
+
+ case EEXIST:
+ ast_log(LOG_WARNING,
+ "Context '%s' already included in '%s' context on include at line %d of %s\n",
+ v->value, cxt, v->lineno, vfile);
+ break;
+
+ case ENOENT:
+ case EINVAL:
+ ast_log(LOG_WARNING,
+ "There is no existence of context '%s' included at line %d of %s\n",
+ errno == ENOENT ? v->value : cxt, v->lineno, vfile);
+ break;
+
+ default:
+ ast_log(LOG_WARNING,
+ "Failed to include '%s' in '%s' context at line %d of %s\n",
+ v->value, cxt, v->lineno, vfile);
+ break;
}
}
} else if (!strcasecmp(v->name, "ignorepat")) {
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
if (ast_context_add_ignorepat2(con, realvalue, registrar)) {
- ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s' at line %d\n", v->value, cxt, v->lineno);
+ ast_log(LOG_WARNING,
+ "Unable to include ignorepat '%s' in context '%s' at line %d of %s\n",
+ v->value, cxt, v->lineno, vfile);
}
} else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
char *stringp = realvalue;
@@ -1603,10 +1629,14 @@ process_extension:
appl = strsep(&stringp, "/");
data = S_OR(stringp, "");
if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) {
- ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s' at line %d\n", v->value, cxt, v->lineno);
+ ast_log(LOG_WARNING,
+ "Unable to include switch '%s' in context '%s' at line %d of %s\n",
+ v->value, cxt, v->lineno, vfile);
}
} else {
- ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
+ ast_log(LOG_WARNING,
+ "==!!== Unknown directive: %s at line %d of %s -- IGNORING!!!\n",
+ v->name, v->lineno, vfile);
}
}
}
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
index 4c52d9d7c..26418d760 100644
--- a/pbx/pbx_dundi.c
+++ b/pbx/pbx_dundi.c
@@ -23,7 +23,7 @@
/*** MODULEINFO
<depend>zlib</depend>
- <use>crypto</use>
+ <use type="external">crypto</use>
***/
#include "asterisk.h"
diff --git a/res/res_fax.c b/res/res_fax.c
index e323fba65..431e7086a 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -5,6 +5,23 @@
*
* Dwayne M. Hubbard <dhubbard@digium.com>
* Kevin P. Fleming <kpfleming@digium.com>
+ * Matthew Nicholson <mnicholson@digium.com>
+ *
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
+ * Created by Nethemba s.r.o. http://www.nethemba.com
+ * Sponsored by IPEX a.s. http://www.ipex.cz
+ *
+ * T.38-gateway integration into asterisk app_fax and rework
+ * 2008-2011, Gregory Hinton Nietsky <gregory@distrotech.co.za>
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin <mymail@verevkin.it>
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
@@ -27,6 +44,8 @@
*
* \author Dwayne M. Hubbard <dhubbard@digium.com>
* \author Kevin P. Fleming <kpfleming@digium.com>
+ * \author Matthew Nicholson <mnicholson@digium.com>
+ * \author Gregory H. Nietsky <gregory@distrotech.co.za>
*
* A generic FAX resource module that provides SendFAX and ReceiveFAX applications.
* This module requires FAX technology modules, like res_fax_spandsp, to register with it
@@ -58,6 +77,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dsp.h"
#include "asterisk/indications.h"
#include "asterisk/ast_version.h"
+#include "asterisk/translate.h"
/*** DOCUMENTATION
<application name="ReceiveFax" language="en_US">
@@ -165,6 +185,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="modem">
<para>R/W Modem type (v17/v27/v29).</para>
</enum>
+ <enum name="gateway">
+ <para>R/W T38 Gateway Enabled (yes/no)</para>
+ </enum>
<enum name="pages">
<para>R/O Number of pages transferred.</para>
</enum>
@@ -215,12 +238,37 @@ struct ast_fax_debug_info {
struct ast_dsp *dsp;
};
+/*! \brief used for gateway framehook */
+struct fax_gateway {
+ /*! \brief FAX Session */
+ struct ast_fax_session *s;
+ /*! \brief reserved fax session token */
+ struct ast_fax_tech_token *token;
+ /*! \brief the start of our timeout counter */
+ struct timeval timeout_start;
+ /*! \brief DSP Processor */
+ struct ast_dsp *chan_dsp;
+ struct ast_dsp *peer_dsp;
+ /*! \brief framehook used in gateway mode */
+ int framehook;
+ /*! \brief bridged */
+ int bridged:1;
+ /*! \brief a flag to track the state of our negotiation */
+ enum ast_t38_state t38_state;
+ /*! \brief original audio formats */
+ struct ast_format chan_read_format;
+ struct ast_format chan_write_format;
+ struct ast_format peer_read_format;
+ struct ast_format peer_write_format;
+};
+
static int fax_logger_level = -1;
/*! \brief maximum buckets for res_fax ao2 containers */
#define FAX_MAXBUCKETS 10
#define RES_FAX_TIMEOUT 10000
+#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT
/*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */
static struct {
@@ -397,10 +445,40 @@ static struct ast_fax_session_details *session_details_new(void)
d->modems = general_options.modems;
d->minrate = general_options.minrate;
d->maxrate = general_options.maxrate;
+ d->gateway_id = -1;
return d;
}
+static struct ast_control_t38_parameters our_t38_parameters = {
+ .version = 0,
+ .max_ifp = 400,
+ .rate = AST_T38_RATE_14400,
+ .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+};
+
+static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src)
+{
+ dst->version = src->version;
+ dst->max_ifp = src->max_ifp;
+ dst->rate = src->rate;
+ dst->rate_management = src->rate_management;
+ dst->fill_bit_removal = src->fill_bit_removal;
+ dst->transcoding_mmr = src->transcoding_mmr;
+ dst->transcoding_jbig = src->transcoding_jbig;
+}
+
+static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src)
+{
+ dst->version = src->version;
+ dst->max_ifp = src->max_ifp;
+ dst->rate = src->rate;
+ dst->rate_management = src->rate_management;
+ dst->fill_bit_removal = src->fill_bit_removal;
+ dst->transcoding_mmr = src->transcoding_mmr;
+ dst->transcoding_jbig = src->transcoding_jbig;
+}
+
/*! \brief returns a reference counted details structure from the channel's fax datastore. If the datastore
* does not exist it will be created */
static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan)
@@ -423,6 +501,11 @@ static struct ast_fax_session_details *find_or_create_details(struct ast_channel
}
/* add the datastore to the channel and increment the refcount */
datastore->data = details;
+
+ /* initialize default T.38 parameters */
+ t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
+ t38_parameters_ast_to_fax(&details->their_t38_parameters, &our_t38_parameters);
+
ao2_ref(details, 1);
ast_channel_lock(chan);
ast_channel_datastore_add(chan, datastore);
@@ -645,6 +728,19 @@ static unsigned int fax_rate_str_to_int(const char *ratestr)
}
}
+/*! \brief Release a session token.
+ * \param s a session returned from fax_session_reserve()
+ * \param token a token generated from fax_session_reserve()
+ *
+ * This function releases the given token and marks the given session as no
+ * longer reserved. It is safe to call on a session that is not actually
+ * reserved and with a NULL token. This is so that sessions returned by
+ * technologies that do not support reserved sessions don't require extra logic
+ * to handle.
+ *
+ * \note This function DOES NOT release the given fax session, only the given
+ * token.
+ */
static void fax_session_release(struct ast_fax_session *s, struct ast_fax_tech_token *token)
{
if (token) {
@@ -691,6 +787,19 @@ static void destroy_session(void *session)
ast_free(s->chan_uniqueid);
}
+/*! \brief Reserve a fax session.
+ * \param details the fax session details
+ * \param token a pointer to a place to store a token to be passed to fax_session_new() later
+ *
+ * This function reserves a fax session for use later. If the selected fax
+ * technology does not support reserving sessions a session will still be
+ * returned but token will not be set.
+ *
+ * \note The reference returned by this function does not get consumed by
+ * fax_session_new() and must always be dereferenced separately.
+ *
+ * \return NULL or an uninitialized and possibly reserved session
+ */
static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details, struct ast_fax_tech_token **token)
{
struct ast_fax_session *s;
@@ -701,6 +810,8 @@ static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_detail
}
s->state = AST_FAX_STATE_INACTIVE;
+ s->details = details;
+ ao2_ref(s->details, 1);
/* locate a FAX technology module that can handle said requirements
* Note: the requirements have not yet been finalized as T.38
@@ -739,7 +850,22 @@ static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_detail
return s;
}
-/*! \brief create a FAX session */
+/*! \brief create a FAX session
+ *
+ * \param details details for the session
+ * \param chan the channel the session will run on
+ * \param reserved a reserved session to base this session on (can be NULL)
+ * \param token the token for a reserved session (can be NULL)
+ *
+ * Create a new fax session based on the given details structure.
+ *
+ * \note The given token is always consumed (by tech->new_session() or by
+ * fax_session_release() in the event of a failure). The given reference to a
+ * reserved session is never consumed and must be dereferenced separately from
+ * the reference returned by this function.
+ *
+ * \return NULL or a reference to a new fax session
+ */
static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *details, struct ast_channel *chan, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
{
struct ast_fax_session *s = NULL;
@@ -749,6 +875,12 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
s = reserved;
ao2_ref(reserved, +1);
+ /* NOTE: we don't consume the reference to the reserved
+ * session. The session returned from fax_session_new() is a
+ * new reference and must be derefed in addition to the
+ * reserved session.
+ */
+
if (s->state == AST_FAX_STATE_RESERVED) {
ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1);
s->state = AST_FAX_STATE_UNINITIALIZED;
@@ -791,8 +923,10 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
}
s->chan = chan;
- s->details = details;
- ao2_ref(s->details, 1);
+ if (!s->details) {
+ s->details = details;
+ ao2_ref(s->details, 1);
+ }
details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1);
@@ -892,9 +1026,6 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
{
char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
- if (!filenames) {
- return 1;
- }
ast_channel_lock(chan);
if (details->option.statusevents) {
@@ -902,24 +1033,30 @@ static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_de
get_manager_event_info(chan, &info);
manager_event(EVENT_FLAG_CALL,
- (details->caps & AST_FAX_TECH_RECEIVE) ? "ReceiveFAXStatus" : "SendFAXStatus",
+ "FAXStatus",
+ "Operation: %s\r\n"
"Status: %s\r\n"
"Channel: %s\r\n"
"Context: %s\r\n"
"Exten: %s\r\n"
"CallerID: %s\r\n"
"LocalStationID: %s\r\n"
- "%s\r\n",
+ "%s%s",
+ (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
status,
chan->name,
info.context,
info.exten,
info.cid,
details->localstationid,
- filenames);
+ S_OR(filenames, ""),
+ filenames ? "\r\n" : "");
}
ast_channel_unlock(chan);
- ast_free(filenames);
+
+ if (filenames) {
+ ast_free(filenames);
+ }
return 0;
}
@@ -963,28 +1100,6 @@ static void set_channel_variables(struct ast_channel *chan, struct ast_fax_sessi
GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \
} while (0)
-static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src)
-{
- dst->version = src->version;
- dst->max_ifp = src->max_ifp;
- dst->rate = src->rate;
- dst->rate_management = src->rate_management;
- dst->fill_bit_removal = src->fill_bit_removal;
- dst->transcoding_mmr = src->transcoding_mmr;
- dst->transcoding_jbig = src->transcoding_jbig;
-}
-
-static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src)
-{
- dst->version = src->version;
- dst->max_ifp = src->max_ifp;
- dst->rate = src->rate;
- dst->rate_management = src->rate_management;
- dst->fill_bit_removal = src->fill_bit_removal;
- dst->transcoding_mmr = src->transcoding_mmr;
- dst->transcoding_jbig = src->transcoding_jbig;
-}
-
static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details)
{
switch (ast_channel_get_t38_state(chan)) {
@@ -995,6 +1110,8 @@ static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_det
case T38_STATE_UNAVAILABLE:
details->caps |= AST_FAX_TECH_AUDIO;
break;
+ case T38_STATE_NEGOTIATED:
+ /* already in T.38 mode? This should not happen. */
case T38_STATE_NEGOTIATING: {
/* the other end already sent us a T.38 reinvite, so we need to prod the channel
* driver into resending their parameters to us if it supports doing so... if
@@ -1076,13 +1193,6 @@ static int disable_t38(struct ast_channel *chan)
return 0;
}
-static struct ast_control_t38_parameters our_t38_parameters = {
- .version = 0,
- .max_ifp = 400,
- .rate = AST_T38_RATE_14400,
- .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
-};
-
/*! \brief this is the generic FAX session handling function */
static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
{
@@ -1350,8 +1460,6 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
struct ast_frame *frame = NULL;
struct ast_control_t38_parameters t38_parameters;
- t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
-
/* don't send any audio if we've already received a T.38 reinvite */
if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
/* generate 3 seconds of CED */
@@ -1535,6 +1643,14 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->caps & AST_FAX_TECH_GATEWAY) {
+ ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n");
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (details->maxrate < details->minrate) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "maxrate is less than minrate");
@@ -1741,8 +1857,6 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
struct ast_frame *frame = NULL;
struct ast_control_t38_parameters t38_parameters;
- t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
-
/* send CNG tone while listening for the receiver to initiate a switch
* to T.38 mode; if they do, stop sending the CNG tone and proceed with
* the switch.
@@ -1999,6 +2113,14 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->caps & AST_FAX_TECH_GATEWAY) {
+ ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n");
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (details->maxrate < details->minrate) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "maxrate is less than minrate");
@@ -2230,6 +2352,647 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
return (!channel_alive) ? -1 : 0;
}
+/*! \brief destroy a FAX gateway session structure */
+static void destroy_gateway(void *data)
+{
+ struct fax_gateway *gateway = data;
+
+ if (gateway->chan_dsp) {
+ ast_dsp_free(gateway->chan_dsp);
+ gateway->chan_dsp = NULL;
+ }
+
+ if (gateway->peer_dsp) {
+ ast_dsp_free(gateway->peer_dsp);
+ gateway->peer_dsp = NULL;
+ }
+
+ if (gateway->s) {
+ fax_session_release(gateway->s, gateway->token);
+ gateway->token = NULL;
+ gateway->s->details->caps |= ~AST_FAX_TECH_GATEWAY;
+
+ ao2_lock(faxregistry.container);
+ ao2_unlink(faxregistry.container, gateway->s);
+ ao2_unlock(faxregistry.container);
+
+ ao2_ref(gateway->s, -1);
+ gateway->s = NULL;
+ }
+}
+
+/*! \brief Create a new fax gateway object.
+ * \param details the fax session details
+ * \return NULL or a fax gateway object
+ */
+static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details)
+{
+ struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
+ if (!gateway) {
+ return NULL;
+ }
+
+ gateway->chan_dsp = ast_dsp_new();
+ if (!gateway->chan_dsp) {
+ ao2_ref(gateway, -1);
+ return NULL;
+ }
+
+ gateway->peer_dsp = ast_dsp_new();
+ if (!gateway->peer_dsp) {
+ ao2_ref(gateway, -1);
+ return NULL;
+ }
+
+ gateway->framehook = -1;
+
+ ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
+ ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CED);
+
+ ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
+ ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
+
+ details->caps = AST_FAX_TECH_GATEWAY;
+ if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
+ details->caps |= ~AST_FAX_TECH_GATEWAY;
+ ast_log(LOG_ERROR, "Can't reserve a FAX session, gateway attempt failed.\n");
+ ao2_ref(gateway, -1);
+ return NULL;
+ }
+
+ return gateway;
+}
+
+/*! \brief Create a fax session and start T.30<->T.38 gateway mode
+ * \param gateway a fax gateway object
+ * \param details fax session details
+ * \param chan active channel
+ * \return 0 on error 1 on success*/
+static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan)
+{
+ struct ast_fax_session *s;
+
+ /* create the FAX session */
+ if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) {
+ gateway->token = NULL;
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting gateway session");
+ ast_string_field_set(details, error, "INIT_ERROR");
+ set_channel_variables(chan, details);
+ report_fax_status(chan, details, "No Available Resource");
+ ast_log(LOG_ERROR, "Can't create a FAX session, gateway attempt failed.\n");
+ return -1;
+ }
+ /* release the reference for the reserved session and replace it with
+ * the real session */
+ ao2_ref(gateway->s, -1);
+ gateway->s = s;
+ gateway->token = NULL;
+
+ if (gateway->s->tech->start_session(gateway->s) < 0) {
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting gateway session");
+ ast_string_field_set(details, error, "INIT_ERROR");
+ set_channel_variables(chan, details);
+ return -1;
+ }
+
+ gateway->timeout_start.tv_sec = 0;
+ gateway->timeout_start.tv_usec = 0;
+
+ report_fax_status(chan, details, "FAX Transmission In Progress");
+
+ return 0;
+}
+
+static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+ struct ast_frame *dfr = ast_frdup(f);
+ struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
+ struct ast_channel *other = (active == chan) ? peer : chan;
+
+ if (!dfr) {
+ return f;
+ }
+
+ if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
+ return f;
+ }
+
+ if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
+ if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+ struct ast_control_t38_parameters t38_parameters = {
+ .request_response = AST_T38_REQUEST_NEGOTIATE,
+ };
+ struct ast_frame control_frame = {
+ .src = "res_fax",
+ .frametype = AST_FRAME_CONTROL,
+ .datalen = sizeof(t38_parameters),
+ .subclass.integer = AST_CONTROL_T38_PARAMETERS,
+ .data.ptr = &t38_parameters,
+ };
+
+ struct ast_fax_session_details *details = find_details(chan);
+ ast_frfree(dfr);
+
+ if (!details) {
+ ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+ ast_framehook_detach(chan, gateway->framehook);
+ return f;
+ }
+
+ t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+ ao2_ref(details, -1);
+
+ if (!(dfr = ast_frisolate(&control_frame))) {
+ ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
+ return f;
+ }
+
+ gateway->t38_state = T38_STATE_NEGOTIATING;
+ gateway->timeout_start = ast_tvnow();
+
+ ast_debug(1, "detected CED tone on %s, requesting T.38 on %s for T.38 gateway session\n", active->name, other->name);
+ return dfr;
+ } else {
+ ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
+ }
+ }
+
+ ast_frfree(dfr);
+ return f;
+}
+
+static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel *active, struct ast_control_t38_parameters *control_params)
+{
+ if (active == chan) {
+ return ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+ } else {
+ return ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+ }
+}
+
+/*! \brief T38 Gateway Negotiate t38 parameters
+ * \param gateway gateway object
+ * \param chan channel running the gateway
+ * \param peer channel im bridged too
+ * \param active channel the frame originated on
+ * \param f the control frame to process
+ * \return processed control frame or null frame
+ */
+static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+ struct ast_control_t38_parameters *control_params = f->data.ptr;
+ struct ast_channel *other = (active == chan) ? peer : chan;
+ struct ast_fax_session_details *details;
+
+ if (f->datalen != sizeof(struct ast_control_t38_parameters)) {
+ /* invalaid AST_CONTROL_T38_PARAMETERS frame, we can't
+ * do anything with it, pass it on */
+ return f;
+ }
+
+ /* ignore frames from ourselves */
+ if ((gateway->t38_state == T38_STATE_NEGOTIATED && control_params->request_response == AST_T38_NEGOTIATED)
+ || (gateway->t38_state == T38_STATE_REJECTED && control_params->request_response == AST_T38_REFUSED)
+ || (gateway->t38_state == T38_STATE_NEGOTIATING && control_params->request_response == AST_T38_REQUEST_TERMINATE)) {
+
+ return f;
+ }
+
+ if (!(details = find_details(chan))) {
+ ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+ ast_framehook_detach(chan, gateway->framehook);
+ return f;
+ }
+
+ if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
+ enum ast_t38_state state = ast_channel_get_t38_state(other);
+ if (state == T38_STATE_UNKNOWN) {
+ /* we detected a request to negotiate T.38 and the
+ * other channel appears to support T.38, we'll pass
+ * the request through and only step in if the other
+ * channel rejects the request */
+ ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name);
+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+ gateway->t38_state = T38_STATE_UNKNOWN;
+ gateway->timeout_start = ast_tvnow();
+ ao2_ref(details, -1);
+ return f;
+ } else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) {
+ /* the other channel does not support T.38, we need to
+ * step in here */
+ ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name);
+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+ t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+ if (fax_gateway_start(gateway, details, chan)) {
+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+ gateway->t38_state = T38_STATE_REJECTED;
+ control_params->request_response = AST_T38_REFUSED;
+
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+ } else {
+ gateway->t38_state = T38_STATE_NEGOTIATED;
+ control_params->request_response = AST_T38_NEGOTIATED;
+ report_fax_status(chan, details, "T.38 Negotiated");
+ }
+
+ fax_gateway_indicate_t38(chan, active, control_params);
+
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING) {
+ /* we got a request to negotiate T.38 after we already
+ * sent one to the other party based on CED tone
+ * detection. We'll just pretend we passed this request
+ * through in the first place. */
+
+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+ gateway->t38_state = T38_STATE_UNKNOWN;
+ gateway->timeout_start = ast_tvnow();
+
+ ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on CED detection\n", active->name);
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
+ /* we got a request to negotiate T.38 after we already
+ * sent one to the other party based on CED tone
+ * detection and received a response. We need to
+ * respond to this and shut down the gateway. */
+
+ t38_parameters_fax_to_ast(control_params, &details->their_t38_parameters);
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+
+ control_params->request_response = AST_T38_NEGOTIATED;
+
+ fax_gateway_indicate_t38(chan, active, control_params);
+
+ ast_string_field_set(details, result, "SUCCESS");
+ ast_string_field_set(details, resultstr, "no gateway necessary");
+ ast_string_field_set(details, error, "NATIVE_T38");
+ set_channel_variables(chan, details);
+
+ ast_debug(1, "%s is attempting to negotiate T.38 after we already negotiated T.38 with %s, disabling the gateway\n", active->name, other->name);
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else {
+ ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name);
+ ao2_ref(details, -1);
+ return f;
+ }
+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING
+ && control_params->request_response == AST_T38_REFUSED) {
+
+ ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name);
+
+ /* our request to negotiate T.38 was refused, if the other
+ * channel supports T.38, they might still reinvite and save
+ * the day. Otherwise disable the gateway. */
+ if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+ gateway->t38_state = T38_STATE_UNAVAILABLE;
+ } else {
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "unable to negotiate T.38");
+ ast_string_field_set(details, error, "T38_NEG_ERROR");
+ set_channel_variables(chan, details);
+ }
+
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else if (gateway->t38_state == T38_STATE_NEGOTIATING
+ && control_params->request_response == AST_T38_NEGOTIATED) {
+
+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+ t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+
+ if (fax_gateway_start(gateway, details, chan)) {
+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+ gateway->t38_state = T38_STATE_NEGOTIATING;
+ control_params->request_response = AST_T38_REQUEST_TERMINATE;
+
+ fax_gateway_indicate_t38(chan, active, control_params);
+ } else {
+ gateway->t38_state = T38_STATE_NEGOTIATED;
+ report_fax_status(chan, details, "T.38 Negotiated");
+ }
+
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else if (control_params->request_response == AST_T38_REFUSED) {
+ /* the other channel refused the request to negotiate T.38,
+ * we'll step in here and pretend the request was accepted */
+
+ ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name);
+ ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name);
+
+ t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+ if (fax_gateway_start(gateway, details, chan)) {
+ ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+ gateway->t38_state = T38_STATE_REJECTED;
+ control_params->request_response = AST_T38_REFUSED;
+
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+ } else {
+ gateway->t38_state = T38_STATE_NEGOTIATED;
+ control_params->request_response = AST_T38_NEGOTIATED;
+ }
+
+ ao2_ref(details, -1);
+ return f;
+ } else if (control_params->request_response == AST_T38_REQUEST_TERMINATE) {
+ /* the channel wishes to end our short relationship, we shall
+ * oblige */
+
+ ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name);
+
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+
+ gateway->t38_state = T38_STATE_REJECTED;
+ control_params->request_response = AST_T38_TERMINATED;
+
+ fax_gateway_indicate_t38(chan, active, control_params);
+
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ } else if (control_params->request_response == AST_T38_NEGOTIATED) {
+ ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name);
+
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+
+ ast_string_field_set(details, result, "SUCCESS");
+ ast_string_field_set(details, resultstr, "no gateway necessary");
+ ast_string_field_set(details, error, "NATIVE_T38");
+ set_channel_variables(chan, details);
+
+ ao2_ref(details, -1);
+ return f;
+ } else if (control_params->request_response == AST_T38_TERMINATED) {
+ ast_debug(1, "T.38 disabled on channel %s\n", active->name);
+
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+
+ ao2_ref(details, -1);
+ return &ast_null_frame;
+ }
+
+ ao2_ref(details, -1);
+ return f;
+}
+
+/*! \brief Destroy the gateway data structure when the framehook is detached
+ * \param data framehook data (gateway data)*/
+static void fax_gateway_framehook_destroy(void *data) {
+ struct fax_gateway *gateway = data;
+
+ if (gateway->s) {
+ switch (gateway->s->state) {
+ case AST_FAX_STATE_INITIALIZED:
+ case AST_FAX_STATE_OPEN:
+ case AST_FAX_STATE_ACTIVE:
+ case AST_FAX_STATE_COMPLETE:
+ if (gateway->s->tech->cancel_session) {
+ gateway->s->tech->cancel_session(gateway->s);
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ }
+
+ ao2_ref(gateway, -1);
+}
+
+/*! \brief T.30<->T.38 gateway framehook.
+ *
+ * Intercept packets on bridged channels and determine if a T.38 gateway is
+ * required. If a gateway is required, start a gateway and handle T.38
+ * negotiation if necessary.
+ *
+ * \param chan channel running the gateway
+ * \param f frame to handle may be NULL
+ * \param event framehook event
+ * \param data framehook data (struct fax_gateway *)
+ *
+ * \return processed frame or NULL when f is NULL or a null frame
+ */
+static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+ struct fax_gateway *gateway = data;
+ struct ast_channel *peer, *active;
+
+ /* restore audio formats when we are detached */
+ if (event == AST_FRAMEHOOK_EVENT_DETACHED) {
+ set_channel_variables(chan, gateway->s->details);
+
+ if (gateway->bridged) {
+ ast_set_read_format(chan, &gateway->chan_read_format);
+ ast_set_read_format(chan, &gateway->chan_write_format);
+
+ if ((peer = ast_bridged_channel(chan))) {
+ ast_set_read_format(peer, &gateway->peer_read_format);
+ ast_set_read_format(peer, &gateway->peer_write_format);
+ ast_channel_make_compatible(chan, peer);
+ }
+ }
+
+ return NULL;
+ }
+
+ if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED)) {
+ return NULL;
+ };
+
+ /* this frame was generated by the fax gateway, pass it on */
+ if (ast_test_flag(f, AST_FAX_FRFLAG_GATEWAY)) {
+ return f;
+ }
+
+ if (!(peer = ast_bridged_channel(chan))) {
+ /* not bridged, don't do anything */
+ return f;
+ }
+
+ if (!gateway->bridged && peer) {
+ /* don't start a gateway if neither channel can handle T.38 */
+ if (ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE && ast_channel_get_t38_state(peer) == T38_STATE_UNAVAILABLE) {
+ ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", chan->name, peer->name);
+ ast_framehook_detach(chan, gateway->framehook);
+ gateway->s->details->gateway_id = -1;
+
+ ast_string_field_set(gateway->s->details, result, "FAILED");
+ ast_string_field_set(gateway->s->details, resultstr, "neither channel supports T.38");
+ ast_string_field_set(gateway->s->details, error, "T38_NEG_ERROR");
+ set_channel_variables(chan, gateway->s->details);
+ return f;
+ }
+
+ gateway->timeout_start = ast_tvnow();
+
+ /* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
+ ast_format_copy(&gateway->chan_read_format, &chan->readformat);
+ ast_format_copy(&gateway->chan_write_format, &chan->readformat);
+
+ ast_format_copy(&gateway->peer_read_format, &peer->readformat);
+ ast_format_copy(&gateway->peer_write_format, &peer->readformat);
+
+ ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
+ ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR);
+
+ ast_set_read_format_by_id(peer, AST_FORMAT_SLINEAR);
+ ast_set_write_format_by_id(peer, AST_FORMAT_SLINEAR);
+
+ ast_channel_make_compatible(chan, peer);
+ gateway->bridged = 1;
+ }
+
+ if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) {
+ if (ast_tvdiff_ms(ast_tvnow(), gateway->timeout_start) > FAX_GATEWAY_TIMEOUT) {
+ ast_debug(1, "no fax activity between %s and %s after %d ms, disabling gateway\n", chan->name, peer->name, FAX_GATEWAY_TIMEOUT);
+ ast_framehook_detach(chan, gateway->framehook);
+ gateway->s->details->gateway_id = -1;
+
+ ast_string_field_set(gateway->s->details, result, "FAILED");
+ ast_string_field_build(gateway->s->details, resultstr, "no fax activity after %d ms", FAX_GATEWAY_TIMEOUT);
+ ast_string_field_set(gateway->s->details, error, "TIMEOUT");
+ set_channel_variables(chan, gateway->s->details);
+ return f;
+ }
+ }
+
+ /* only handle VOICE, MODEM, and CONTROL frames*/
+ switch (f->frametype) {
+ case AST_FRAME_VOICE:
+ switch (f->subclass.format.id) {
+ case AST_FORMAT_SLINEAR:
+ case AST_FORMAT_ALAW:
+ case AST_FORMAT_ULAW:
+ break;
+ default:
+ return f;
+ }
+ break;
+ case AST_FRAME_MODEM:
+ if (f->subclass.integer == AST_MODEM_T38) {
+ break;
+ }
+ return f;
+ case AST_FRAME_CONTROL:
+ if (f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+ break;
+ }
+ return f;
+ default:
+ return f;
+ }
+
+ /* detect the active channel */
+ switch (event) {
+ case AST_FRAMEHOOK_EVENT_WRITE:
+ active = peer;
+ break;
+ case AST_FRAMEHOOK_EVENT_READ:
+ active = chan;
+ break;
+ default:
+ ast_log(LOG_WARNING, "unhandled framehook event %i\n", event);
+ return f;
+ }
+
+ /* handle control frames */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+ return fax_gateway_detect_t38(gateway, chan, peer, active, f);
+ }
+
+ /* not in gateway mode yet, listen for CED */
+ /* XXX this should detect a v21 preamble instead of CED */
+ if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
+ return fax_gateway_detect_ced(gateway, chan, peer, active, f);
+ }
+
+ /* in gateway mode, gateway some packets */
+ if (gateway->t38_state == T38_STATE_NEGOTIATED) {
+ /* framehooks are called in __ast_read() before frame format
+ * translation is does, so we need to translate here */
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id != AST_FORMAT_SLINEAR)) {
+ if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
+ f = &ast_null_frame;
+ return f;
+ }
+ }
+
+ /* XXX we ignore the return value here, perhaps we should
+ * disable the gateway if a write fails. I am not sure how a
+ * write would fail, or even if a failure would be fatal so for
+ * now we'll just ignore the return value. */
+ gateway->s->tech->write(gateway->s, f);
+ f = &ast_null_frame;
+ return f;
+ }
+
+ return f;
+}
+
+/*! \brief Attach a gateway framehook object to a channel.
+ * \param chan the channel to attach to
+ * \param details fax session details
+ * \return the framehook id of the attached framehook or -1 on error
+ * \retval -1 error
+ */
+static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+ struct fax_gateway *gateway;
+ struct ast_framehook_interface fr_hook = {
+ .version = AST_FRAMEHOOK_INTERFACE_VERSION,
+ .event_cb = fax_gateway_framehook,
+ .destroy_cb = fax_gateway_framehook_destroy,
+ };
+
+ ast_string_field_set(details, result, "SUCCESS");
+ ast_string_field_set(details, resultstr, "gateway operation started successfully");
+ ast_string_field_set(details, error, "NO_ERROR");
+ set_channel_variables(chan, details);
+
+ /* set up the frame hook*/
+ gateway = fax_gateway_new(details);
+ if (!gateway) {
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error initializing gateway session");
+ ast_string_field_set(details, error, "INIT_ERROR");
+ set_channel_variables(chan, details);
+ report_fax_status(chan, details, "No Available Resource");
+ return -1;
+ }
+
+ fr_hook.data = gateway;
+ ast_channel_lock(chan);
+ gateway->framehook = ast_framehook_attach(chan, &fr_hook);
+ ast_channel_unlock(chan);
+
+ if (gateway->framehook < 0) {
+ ao2_ref(gateway, -1);
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error attaching gateway to channel");
+ ast_string_field_set(details, error, "INIT_ERROR");
+ set_channel_variables(chan, details);
+ return -1;
+ }
+
+ return gateway->framehook;
+}
+
/*! \brief hash callback for ao2 */
static int session_hash_cb(const void *obj, const int flags)
{
@@ -2501,19 +3264,15 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
while ((s = ao2_iterator_next(&i))) {
ao2_lock(s);
- if (!(filenames = generate_filenames_string(s->details, "", ", "))) {
- ast_log(LOG_ERROR, "error printing filenames for 'fax show sessions' command");
- ao2_unlock(s);
- ao2_ref(s, -1);
- ao2_iterator_destroy(&i);
- return CLI_FAILURE;
- }
+ filenames = generate_filenames_string(s->details, "", ", ");
ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n",
s->channame, s->tech->type, s->id,
(s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38",
- (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
- ast_fax_state_to_str(s->state), filenames);
+ (s->details->caps & AST_FAX_TECH_GATEWAY)
+ ? "gateway"
+ : (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
+ ast_fax_state_to_str(s->state), S_OR(filenames, ""));
ast_free(filenames);
ao2_unlock(s);
@@ -2626,6 +3385,9 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
}
if (!strcasecmp(data, "ecm")) {
ast_copy_string(buf, details->option.ecm ? "yes" : "no", len);
+ } else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") ||
+ !strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) {
+ ast_copy_string(buf, details->gateway_id != -1 ? "yes" : "no", len);
} else if (!strcasecmp(data, "error")) {
ast_copy_string(buf, details->error, len);
} else if (!strcasecmp(data, "filename")) {
@@ -2700,6 +3462,27 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
} else {
ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value);
}
+ } else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") ||
+ !strcasecmp(data, "t38_gateway") || !strcasecmp(data, "faxgateway")) {
+ const char *val = ast_skip_blanks(value);
+ if (ast_true(val)) {
+ if (details->gateway_id < 0) {
+ details->gateway_id = fax_gateway_attach(chan, details);
+ if (details->gateway_id < 0) {
+ ast_log(LOG_ERROR, "Error attaching T.38 gateway to channel %s.\n", chan->name);
+ res = -1;
+ } else {
+ ast_debug(1, "Attached T.38 gateway to channel %s.\n", chan->name);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", chan->name);
+ }
+ } else if (ast_false(val)) {
+ ast_framehook_detach(chan, details->gateway_id);
+ details->gateway_id = -1;
+ } else {
+ ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
+ }
} else if (!strcasecmp(data, "headerinfo")) {
ast_string_field_set(details, headerinfo, value);
} else if (!strcasecmp(data, "localstationid")) {
@@ -2789,6 +3572,7 @@ static int load_module(void)
ao2_ref(faxregistry.container, -1);
return AST_MODULE_LOAD_DECLINE;
}
+
ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli));
res = ast_custom_function_register(&acf_faxopt);
fax_logger_level = ast_logger_register_level("FAX");
diff --git a/res/res_fax_spandsp.c b/res/res_fax_spandsp.c
index 851382b68..02657583d 100644
--- a/res/res_fax_spandsp.c
+++ b/res/res_fax_spandsp.c
@@ -5,6 +5,22 @@
*
* Matthew Nicholson <mnicholson@digium.com>
*
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
+ * Created by Nethemba s.r.o. http://www.nethemba.com
+ * Sponsored by IPEX a.s. http://www.ipex.cz
+ *
+ * T.38-gateway integration into asterisk app_fax and rework
+ * 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za>
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin <mymail@verevkin.it>
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
+ *
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
@@ -21,6 +37,7 @@
* \brief Spandsp T.38 and G.711 FAX Resource
*
* \author Matthew Nicholson <mnicholson@digium.com>
+ * \author Gregory H. Nietsky <gregory@distrotech.co.za>
*
* This module registers the Spandsp FAX technology with the res_fax module.
*/
@@ -46,9 +63,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/timing.h"
#include "asterisk/astobj2.h"
#include "asterisk/res_fax.h"
+#include "asterisk/channel.h"
#define SPANDSP_FAX_SAMPLES 160
#define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
+#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
static void spandsp_fax_destroy(struct ast_fax_session *s);
@@ -57,6 +76,9 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
static int spandsp_fax_start(struct ast_fax_session *s);
static int spandsp_fax_cancel(struct ast_fax_session *s);
static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
+static int spandsp_fax_gateway_start(struct ast_fax_session *s);
+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
static char *spandsp_fax_cli_show_capabilities(int fd);
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
@@ -75,7 +97,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
*/
.version = "pre-20090220",
#endif
- .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
+ .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
.new_session = spandsp_fax_new,
.destroy_session = spandsp_fax_destroy,
.read = spandsp_fax_read,
@@ -114,6 +136,7 @@ static struct {
struct spandsp_pvt {
unsigned int ist38:1;
unsigned int isdone:1;
+ enum ast_t38_state ast_t38_state;
fax_state_t fax_state;
t38_terminal_state_t t38_state;
t30_state_t *t30_state;
@@ -121,6 +144,9 @@ struct spandsp_pvt {
struct spandsp_fax_stats *stats;
+ struct spandsp_fax_gw_stats *t38stats;
+ t38_gateway_state_t t38_gw_state;
+
struct ast_timer *timer;
AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
};
@@ -158,7 +184,9 @@ static void session_destroy(struct spandsp_pvt *p)
*/
static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
{
- struct spandsp_pvt *p = data;
+ int res = -1;
+ struct ast_fax_session *s = data;
+ struct spandsp_pvt *p = s->tech_pvt;
struct ast_frame fax_frame = {
.frametype = AST_FRAME_MODEM,
.subclass.integer = AST_MODEM_T38,
@@ -174,13 +202,23 @@ static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, c
AST_FRAME_SET_BUFFER(f, buf, 0, len);
if (!(f = ast_frisolate(f))) {
- return -1;
+ return res;
}
- /* no need to lock, this all runs in the same thread */
- AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY);
+ if (p->ast_t38_state == T38_STATE_NEGOTIATED) {
+ res = ast_write(s->chan, f);
+ } else {
+ res = ast_queue_frame(s->chan, f);
+ }
+ ast_frfree(f);
+ } else {
+ /* no need to lock, this all runs in the same thread */
+ AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
+ }
- return 0;
+ return res;
}
static int update_stats(struct spandsp_pvt *p, int completion_code)
@@ -422,6 +460,11 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
goto e_return;
}
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ s->state = AST_FAX_STATE_INITIALIZED;
+ return p;
+ }
+
AST_LIST_HEAD_INIT(&p->read_frames);
if (s->details->caps & AST_FAX_TECH_RECEIVE) {
@@ -450,7 +493,7 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
}
/* init t38 stuff */
- t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
+ t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s);
set_logging(&p->t38_state.logging, s->details);
}
@@ -475,7 +518,12 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
{
struct spandsp_pvt *p = s->tech_pvt;
- session_destroy(p);
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ spandsp_fax_gateway_cleanup(s);
+ } else {
+ session_destroy(p);
+ }
+
ast_free(p);
s->tech_pvt = NULL;
s->fd = -1;
@@ -536,6 +584,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
{
struct spandsp_pvt *p = s->tech_pvt;
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ return spandsp_fax_gateway_process(s, f);
+ }
+
/* XXX do we need to lock here? */
if (s->state == AST_FAX_STATE_COMPLETE) {
ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
@@ -549,6 +601,182 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
}
}
+/*! \brief generate T.30 packets sent to the T.30 leg of gateway
+ * \param chan T.30 channel
+ * \param data fax session structure
+ * \param len not used
+ * \param samples no of samples generated
+ * \return -1 on failure or 0 on sucess*/
+static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples)
+{
+ int res = -1;
+ struct ast_fax_session *s = data;
+ struct spandsp_pvt *p = s->tech_pvt;
+ uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)];
+ struct ast_frame *f;
+ struct ast_frame t30_frame = {
+ .frametype = AST_FRAME_VOICE,
+ .src = "res_fax_spandsp_g711",
+ .samples = samples,
+ .flags = AST_FAX_FRFLAG_GATEWAY,
+ };
+
+ AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t));
+
+ ast_format_set(&t30_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
+ if (!(f = ast_frisolate(&t30_frame))) {
+ return p->isdone ? -1 : res;
+ }
+
+ /* generate a T.30 packet */
+ if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) {
+ f->datalen = f->samples * sizeof(int16_t);
+ res = ast_write(chan, f);
+ }
+ ast_frfree(f);
+ return p->isdone ? -1 : res;
+}
+
+/*! \brief simple routine to allocate data to generator
+ * \param chan channel
+ * \param params generator data
+ * \return data to use in generator call*/
+static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) {
+ ao2_ref(params, +1);
+ return params;
+}
+
+static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) {
+ ao2_ref(data, -1);
+}
+
+/*! \brief activate a spandsp gateway based on the information in the given fax session
+ * \param s fax session
+ * \return -1 on error 0 on sucess*/
+static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
+ struct spandsp_pvt *p = s->tech_pvt;
+ struct ast_fax_t38_parameters *t38_param;
+ int i, modems = 0;
+ struct ast_channel *peer;
+ static struct ast_generator t30_gen = {
+ alloc: spandsp_fax_gw_gen_alloc,
+ release: spandsp_fax_gw_gen_release,
+ generate: spandsp_fax_gw_t30_gen,
+ };
+
+#if SPANDSP_RELEASE_DATE >= 20081012
+ /* for spandsp shaphots 0.0.6 and higher */
+ p->t38_core_state=&p->t38_gw_state.t38x.t38;
+#else
+ /* for spandsp release 0.0.5 */
+ p->t38_core_state=&p->t38_gw_state.t38;
+#endif
+
+ if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) {
+ return -1;
+ }
+
+ p->ist38 = 1;
+ p->ast_t38_state = ast_channel_get_t38_state(s->chan);
+ if (!(peer = ast_bridged_channel(s->chan))) {
+ ast_channel_unlock(s->chan);
+ return -1;
+ }
+ ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s);
+
+ set_logging(&p->t38_gw_state.logging, s->details);
+ set_logging(&p->t38_core_state->logging, s->details);
+
+ t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters;
+ t38_set_t38_version(p->t38_core_state, t38_param->version);
+ t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm);
+ t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp);
+ t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal);
+ t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr);
+ t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig);
+ t38_set_data_rate_management_method(p->t38_core_state,
+ (t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2);
+
+ t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE);
+ t38_set_sequence_number_handling(p->t38_core_state, TRUE);
+
+ if (AST_FAX_MODEM_V17 & s->details->modems) {
+ modems |= T30_SUPPORT_V17;
+ }
+ if (AST_FAX_MODEM_V27 & s->details->modems) {
+ modems |= T30_SUPPORT_V27TER;
+ }
+ if (AST_FAX_MODEM_V29 & s->details->modems) {
+ modems |= T30_SUPPORT_V29;
+ }
+ if (AST_FAX_MODEM_V34 & s->details->modems) {
+#if defined(T30_SUPPORT_V34)
+ modems |= T30_SUPPORT_V34;
+#elif defined(T30_SUPPORT_V34HDX)
+ modems |= T30_SUPPORT_V34HDX;
+#else
+ ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
+#endif
+ }
+
+ t38_gateway_set_supported_modems(&p->t38_gw_state, modems);
+
+ /* engage udptl nat on other side of T38 line
+ * (Asterisk changes media ports thus we send a few packets to reinitialize
+ * pinholes in NATs and FWs
+ */
+ for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) {
+#if SPANDSP_RELEASE_DATE >= 20091228
+ t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL);
+#elif SPANDSP_RELEASE_DATE >= 20081012
+ t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count);
+#else
+ t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count);
+#endif
+ }
+
+ s->state = AST_FAX_STATE_ACTIVE;
+
+ return 0;
+}
+
+/*! \brief process a frame from the bridge
+ * \param s fax session
+ * \param f frame to process
+ * \return 1 on sucess 0 on incorect packet*/
+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f)
+{
+ struct spandsp_pvt *p = s->tech_pvt;
+
+ /*invalid frame*/
+ if (!f->data.ptr || !f->datalen) {
+ return -1;
+ }
+
+ /* Process a IFP packet */
+ if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) {
+ return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
+ } else if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
+ return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples);
+ }
+
+ return -1;
+}
+
+/*! \brief gather data and clean up after gateway ends
+ * \param s fax session*/
+static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s)
+{
+ struct spandsp_pvt *p = s->tech_pvt;
+ t38_stats_t t38_stats;
+
+ t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats);
+
+ s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE;
+ s->details->pages_transferred = t38_stats.pages_transferred;
+ ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate);
+}
+
/*! \brief */
static int spandsp_fax_start(struct ast_fax_session *s)
{
@@ -556,6 +784,10 @@ static int spandsp_fax_start(struct ast_fax_session *s)
s->state = AST_FAX_STATE_OPEN;
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ return spandsp_fax_gateway_start(s);
+ }
+
if (p->ist38) {
#if SPANDSP_RELEASE_DATE >= 20080725
/* for spandsp shaphots 0.0.6 and higher */
@@ -625,6 +857,12 @@ static int spandsp_fax_start(struct ast_fax_session *s)
static int spandsp_fax_cancel(struct ast_fax_session *s)
{
struct spandsp_pvt *p = s->tech_pvt;
+
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ p->isdone = 1;
+ return 0;
+ }
+
t30_terminate(p->t30_state);
p->isdone = 1;
return 0;
@@ -653,7 +891,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
/*! \brief */
static char *spandsp_fax_cli_show_capabilities(int fd)
{
- ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
+ ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n");
return CLI_SUCCESS;
}
@@ -661,35 +899,48 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
{
struct spandsp_pvt *p = s->tech_pvt;
- t30_stats_t stats;
ao2_lock(s);
- ast_cli(fd, "%-22s : %d\n", "session", s->id);
- ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
- ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
- if (s->state != AST_FAX_STATE_UNINITIALIZED) {
- t30_get_transfer_statistics(p->t30_state, &stats);
- ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
- ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
- ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
- ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
+ if (s->details->caps & AST_FAX_TECH_GATEWAY) {
+ ast_cli(fd, "%-22s : %d\n", "session", s->id);
+ ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
+ ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
+ if (s->state != AST_FAX_STATE_UNINITIALIZED) {
+ t38_stats_t stats;
+ t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats);
+ ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
+ ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
+ ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
+ }
+ } else {
+ ast_cli(fd, "%-22s : %d\n", "session", s->id);
+ ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
+ ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
+ if (s->state != AST_FAX_STATE_UNINITIALIZED) {
+ t30_stats_t stats;
+ t30_get_transfer_statistics(p->t30_state, &stats);
+ ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
+ ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
+ ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
+ ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
#if SPANDSP_RELEASE_DATE >= 20090220
- ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
+ ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
#else
- ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
+ ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
#endif
- ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
+ ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
- ast_cli(fd, "\nData Statistics:\n");
+ ast_cli(fd, "\nData Statistics:\n");
#if SPANDSP_RELEASE_DATE >= 20090220
- ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
- ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
+ ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
+ ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
#else
- ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
- ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
+ ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
+ ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
#endif
- ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
- ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
+ ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
+ ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
+ }
}
ao2_unlock(s);
ast_cli(fd, "\n\n");
diff --git a/res/res_jabber.c b/res/res_jabber.c
index 2d0ffcb3a..61d436a92 100644
--- a/res/res_jabber.c
+++ b/res/res_jabber.c
@@ -32,7 +32,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
- <use>openssl</use>
+ <use type="external">openssl</use>
***/
#include "asterisk.h"
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index db7f8525c..cc98543c6 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -1150,7 +1150,9 @@ static void moh_rescan_files(void) {
i = ao2_iterator_init(mohclasses, 0);
while ((c = ao2_iterator_next(&i))) {
- moh_scan_files(c);
+ if (!strcasecmp(c->mode, "files")) {
+ moh_scan_files(c);
+ }
ao2_ref(c, -1);
}
@@ -1650,14 +1652,15 @@ static int load_moh_classes(int reload)
cfg = ast_config_load("musiconhold.conf", config_flags);
- if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+ if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
if (ast_check_realtime("musiconhold") && reload) {
ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
}
- if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
- moh_rescan_files();
- }
+ return 0;
+ }
+ if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ moh_rescan_files();
return 0;
}
diff --git a/res/res_timing_timerfd.c b/res/res_timing_timerfd.c
index ae5d2b411..3e686f810 100644
--- a/res/res_timing_timerfd.c
+++ b/res/res_timing_timerfd.c
@@ -162,7 +162,35 @@ static void timerfd_timer_ack(int handle, unsigned int quantity)
uint64_t expirations;
int read_result = 0;
+ struct timerfd_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
+ ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
+ return;
+ }
+
+ if (our_timer->saved_timer.it_value.tv_nsec == 0L) {
+ ast_log(LOG_DEBUG, "Reading attempt on idle timerfd.\n");
+ return;
+ }
+
do {
+ struct itimerspec timer_status;
+
+ if (timerfd_gettime(handle, &timer_status)) {
+ ast_log(LOG_ERROR, "Call to timerfd_gettime() error: %s\n", strerror(errno));
+ expirations = 0;
+ break;
+ }
+
+ if ((timer_status.it_value.tv_sec == 0) && (timer_status.it_value.tv_nsec == 0)) {
+ ast_log(LOG_DEBUG, "Call to timerfd_timer_ack() with disarmed timer - break now.\n");
+ expirations = 0;
+ break;
+ }
+
read_result = read(handle, &expirations, sizeof(expirations));
if (read_result == -1) {
if (errno == EINTR || errno == EAGAIN) {
diff --git a/tests/test_netsock2.c b/tests/test_netsock2.c
new file mode 100644
index 000000000..3bbf25770
--- /dev/null
+++ b/tests/test_netsock2.c
@@ -0,0 +1,125 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * Terry Wilson <twilson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Netsock2 Unit Tests
+ *
+ * \author Terry Wilson <twilson@digium.com>
+ *
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "")
+
+#include "asterisk/test.h"
+#include "asterisk/module.h"
+#include "asterisk/netsock2.h"
+#include "asterisk/logger.h"
+struct parse_test {
+ const char *address;
+ int expected_result;
+};
+
+AST_TEST_DEFINE(parsing)
+{
+ int res = AST_TEST_PASS;
+ struct parse_test test_vals[] = {
+ { "192.168.1.0", 1 },
+ { "10.255.255.254", 1 },
+ { "172.18.5.4", 1 },
+ { "8.8.4.4", 1 },
+ { "0.0.0.0", 1 },
+ { "127.0.0.1", 1 },
+ { "1.256.3.4", 0 },
+ { "256.0.0.1", 0 },
+ { "1.2.3.4:5060", 1 },
+ { "::ffff:5.6.7.8", 1 },
+ { "fdf8:f53b:82e4::53", 1 },
+ { "fe80::200:5aee:feaa:20a2", 1 },
+ { "2001::1", 1 },
+ { "2001:0000:4136:e378:8000:63bf:3fff:fdd2", 1 },
+ { "2001:0002:6c::430", 1 },
+ { "2001:10:240:ab::a", 1 },
+ { "2002:cb0a:3cdd:1::1", 1 },
+ { "2001:db8:8:4::2", 1 }, /* Documentation only, should never be used */
+ { "ff01:0:0:0:0:0:0:2", 1 }, /* Multicast */
+ { "[fdf8:f53b:82e4::53]", 1 },
+ { "[fe80::200:5aee:feaa:20a2]", 1 },
+ { "[2001::1]", 1 },
+ { "[2001:0000:4136:e378:8000:63bf:3fff:fdd2]:5060", 1 },
+ { "2001:0000:4136:e378:8000:63bf:3fff:fdd2:5060", 0 }, /* port, but no brackets */
+ { "[fe80::200:5aee:feaa:20a2%eth0]", 1 }, /* link-local with scope id */
+ { "[fe80::200::abcd", 0 }, /* multiple zero expansions */
+ };
+
+ size_t x;
+ struct ast_sockaddr addr = { { 0, 0, } };
+ int parse_result;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "parsing";
+ info->category = "/main/netsock2/";
+ info->summary = "netsock2 parsing unit test";
+ info->description =
+ "Test parsing of IPv4 and IPv6 network addresses";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ for (x = 0; x < ARRAY_LEN(test_vals); x++) {
+ if ((parse_result = ast_sockaddr_parse(&addr, test_vals[x].address, 0)) != test_vals[x].expected_result) {
+ ast_test_status_update(test, "On '%s' expected %d but got %d\n", test_vals[x].address, test_vals[x].expected_result, parse_result);
+ res = AST_TEST_FAIL;
+ }
+ if (parse_result) {
+ struct ast_sockaddr tmp_addr = { { 0, 0, } };
+ const char *tmp;
+
+ tmp = ast_sockaddr_stringify(&addr);
+ ast_sockaddr_parse(&tmp_addr, tmp, 0);
+ if (ast_sockaddr_cmp_addr(&addr, &tmp_addr)) {
+ ast_test_status_update(test, "Re-parsed stringification did not match: '%s' vs '%s'\n", ast_sockaddr_stringify(&addr), ast_sockaddr_stringify(&tmp_addr));
+ res = AST_TEST_FAIL;
+ }
+ }
+ }
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(parsing);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(parsing);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Netsock2 test module");