dect
/
asterisk
Archived
13
0
Fork 0

Merge branch 'master' of 192.168.0.100:/repos/git/asterisk

This commit is contained in:
Patrick McHardy 2011-07-06 04:52:35 +02:00
commit 916e420bf0
55 changed files with 3249 additions and 714 deletions

43
CHANGES
View File

@ -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
------------------

View File

@ -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.
===========================================================
===========================================================

View File

@ -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;

View File

@ -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

View File

@ -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">

View File

@ -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->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 ((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",
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 (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)
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 (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 (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;

View File

@ -26,7 +26,7 @@
*/
/*** MODULEINFO
<use>res_agi</use>
<use type="module">res_agi</use>
***/
#include "asterisk.h"

View File

@ -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>

View File

@ -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++;
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -34,7 +34,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
<depend>res_jabber</depend>
<use>openssl</use>
<use type="external">openssl</use>
***/
#include "asterisk.h"

View File

@ -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)

View File

@ -30,7 +30,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
<depend>res_jabber</depend>
<use>openssl</use>
<use type="external">openssl</use>
***/
#include "asterisk.h"

View File

@ -30,7 +30,7 @@
*/
/*** MODULEINFO
<use>res_pktccops</use>
<use type="module">res_pktccops</use>
***/
#include "asterisk.h"

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;
}

View File

@ -33,7 +33,7 @@
/*** MODULEINFO
<depend>speex</depend>
<depend>speex_preprocess</depend>
<use>speexdsp</use>
<use type="external">speexdsp</use>
***/
#include "asterisk.h"

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

20
contrib/scripts/file.convert.sh Executable file
View File

@ -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

View File

@ -23,7 +23,7 @@
*/
/*** MODULEINFO
<use>crypto</use>
<use type="external">crypto</use>
***/
#include "asterisk.h"

View File

@ -33,7 +33,7 @@
/*** MODULEINFO
<depend>speex</depend>
<depend>speex_preprocess</depend>
<use>speexdsp</use>
<use type="external">speexdsp</use>
***/
#include "asterisk.h"

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
@ -199,6 +204,23 @@ static inline char *ast_sockaddr_stringify(const struct ast_sockaddr *addr)
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_DEFAULT);
}
/*!
* \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
*
@ -212,6 +234,23 @@ static inline char *ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_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
*
@ -226,6 +265,24 @@ static inline char *ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_HOST);
}
/*!
* \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
*
@ -409,6 +466,20 @@ int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr);
*/
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
*

View File

@ -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);

View File

@ -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 */

View File

@ -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 = {

View File

@ -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);
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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 &&

View File

@ -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);
} 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) {
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);
ast_channel_unlock(chan);
return res;
}
/* 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;
}
}
/* 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 {
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;
}
}
ast_channel_unlock(chan);
return res;
}

View File

@ -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;

View File

@ -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 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 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 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\n",
errno == ENOENT ? 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 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\n",
v->value, cxt, v->lineno);
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);
}
}
}

View File

@ -23,7 +23,7 @@
/*** MODULEINFO
<depend>zlib</depend>
<use>crypto</use>
<use type="external">crypto</use>
***/
#include "asterisk.h"

File diff suppressed because it is too large Load Diff

View File

@ -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");

View File

@ -32,7 +32,7 @@
/*** MODULEINFO
<depend>iksemel</depend>
<use>openssl</use>
<use type="external">openssl</use>
***/
#include "asterisk.h"

View File

@ -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;
}

View File

@ -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) {

125
tests/test_netsock2.c Normal file
View File

@ -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");