Merge 192.168.0.100:/repos/git/asterisk
This commit is contained in:
commit
9364aaccb6
7
CHANGES
7
CHANGES
|
@ -19,8 +19,8 @@ Text Messaging
|
|||
SIP MESSAGE and XMPP are currently supported. There are options in
|
||||
jabber.conf and sip.conf to allow enabling these features.
|
||||
-> jabber.conf: see the "sendtodialplan" and "context" options.
|
||||
-> sip.conf: see the "accept_outofcall_message" and "auth_message_requests"
|
||||
options.
|
||||
-> sip.conf: see the "accept_outofcall_message", "auth_message_requests"
|
||||
and "outofcall_message_context" options.
|
||||
The MESSAGE() dialplan function and MessageSend() application have been
|
||||
added to go along with this functionality. More detailed usage information
|
||||
can be found on the Asterisk wiki (http://wiki.asterisk.org/).
|
||||
|
@ -79,6 +79,9 @@ ConfBridge
|
|||
mixing audio at sample rates ranging from 8khz-96khz.
|
||||
* CONFBRIDGE dialplan function capable of creating dynamic ConfBridge user
|
||||
and bridge profiles on a channel.
|
||||
* CONFBRIDGE_INFO dialplan function capable of retreiving information
|
||||
about a conference such as locked status and number of parties, admins,
|
||||
and marked users.
|
||||
|
||||
Dialplan Variables
|
||||
------------------
|
||||
|
|
1
Makefile
1
Makefile
|
@ -557,6 +557,7 @@ installdirs:
|
|||
$(INSTALL) -d "$(DESTDIR)$(ASTDATADIR)/static-http"
|
||||
$(INSTALL) -d "$(DESTDIR)$(ASTMANDIR)/man8"
|
||||
$(INSTALL) -d "$(DESTDIR)$(AGI_DIR)"
|
||||
$(INSTALL) -d "$(DESTDIR)$(ASTDBDIR)"
|
||||
|
||||
bininstall: _all installdirs $(SUBDIRS_INSTALL)
|
||||
$(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/
|
||||
|
|
|
@ -106,6 +106,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<para>exten => 1,n,ConfBridge(1)</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="CONFBRIDGE_INFO" language="en_US">
|
||||
<synopsis>
|
||||
Get information about a ConfBridge conference.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="type" required="true">
|
||||
<para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
|
||||
</parameter>
|
||||
<parameter name="conf" required="true">
|
||||
<para>Conf refers to the name of the conference being referenced.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
|
||||
</description>
|
||||
</function>
|
||||
<manager name="ConfbridgeList" language="en_US">
|
||||
<synopsis>
|
||||
List participants in a conference.
|
||||
|
@ -618,7 +634,8 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
|
|||
if (other_conference_bridge_user == conference_bridge_user) {
|
||||
continue;
|
||||
}
|
||||
if (ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
|
||||
if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
|
||||
other_conference_bridge_user->playing_moh = 0;
|
||||
ast_moh_stop(other_conference_bridge_user->chan);
|
||||
ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
|
||||
}
|
||||
|
@ -664,6 +681,7 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
|
|||
*/
|
||||
if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
|
||||
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
|
||||
conference_bridge_user->playing_moh = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -692,6 +710,7 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
|
|||
*/
|
||||
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
|
||||
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
|
||||
conference_bridge_user->playing_moh = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -709,6 +728,7 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
|
|||
|
||||
/* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
|
||||
if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
|
||||
first_participant->playing_moh = 0;
|
||||
ast_moh_stop(first_participant->chan);
|
||||
ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
|
||||
}
|
||||
|
@ -924,6 +944,7 @@ static void leave_conference_bridge(struct conference_bridge *conference_bridge,
|
|||
ast_bridge_remove(conference_bridge->bridge, other_participant->chan);
|
||||
} else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
|
||||
ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL);
|
||||
other_participant->playing_moh = 1;
|
||||
ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
|
||||
}
|
||||
}
|
||||
|
@ -933,6 +954,7 @@ static void leave_conference_bridge(struct conference_bridge *conference_bridge,
|
|||
|
||||
if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
|
||||
ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL);
|
||||
first_participant->playing_moh = 1;
|
||||
ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
|
||||
}
|
||||
}
|
||||
|
@ -1192,14 +1214,22 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
|
||||
b_profile_name = args.b_profile_name;
|
||||
}
|
||||
conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile);
|
||||
if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
|
||||
ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
|
||||
res = -1;
|
||||
goto confbridge_cleanup;
|
||||
}
|
||||
|
||||
/* user profile name */
|
||||
if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
|
||||
u_profile_name = args.u_profile_name;
|
||||
}
|
||||
|
||||
conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile);
|
||||
if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
|
||||
ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
|
||||
res = -1;
|
||||
goto confbridge_cleanup;
|
||||
}
|
||||
quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
|
||||
|
||||
/* ask for a PIN immediately after finding user profile. This has to be
|
||||
|
@ -1301,7 +1331,13 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
/* Play the Join sound to both the conference and the user entering. */
|
||||
if (!quiet) {
|
||||
const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
|
||||
if (conference_bridge_user.playing_moh) {
|
||||
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);
|
||||
|
@ -1635,8 +1671,8 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
|
|||
|
||||
/* See if music on hold is playing */
|
||||
ao2_lock(conference_bridge);
|
||||
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
|
||||
/* Just us so MOH is probably indeed going, let's stop it */
|
||||
if (conference_bridge_user->playing_moh) {
|
||||
/* MOH is going, let's stop it */
|
||||
ast_moh_stop(bridge_channel->chan);
|
||||
}
|
||||
ao2_unlock(conference_bridge);
|
||||
|
@ -1646,7 +1682,7 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
|
|||
|
||||
/* See if music on hold needs to be started back up again */
|
||||
ao2_lock(conference_bridge);
|
||||
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
|
||||
if (conference_bridge_user->playing_moh) {
|
||||
ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
|
||||
}
|
||||
ao2_unlock(conference_bridge);
|
||||
|
@ -2059,6 +2095,12 @@ static struct ast_custom_function confbridge_function = {
|
|||
.write = func_confbridge_helper,
|
||||
};
|
||||
|
||||
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
|
||||
static struct ast_custom_function confbridge_info_function = {
|
||||
.name = "CONFBRIDGE_INFO",
|
||||
.read = func_confbridge_info,
|
||||
};
|
||||
|
||||
static int action_confbridgelist(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *actionid = astman_get_header(m, "ActionID");
|
||||
|
@ -2370,7 +2412,68 @@ static int action_confbridgestoprecord(struct mansession *s, const struct messag
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *parse = NULL;
|
||||
struct conference_bridge *bridge = NULL;
|
||||
struct conference_bridge_user *participant = NULL;
|
||||
struct conference_bridge tmp;
|
||||
int count = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(type);
|
||||
AST_APP_ARG(confno);
|
||||
);
|
||||
|
||||
/* parse all the required arguments and make sure they exist. */
|
||||
if (ast_strlen_zero(data)) {
|
||||
return -1;
|
||||
}
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
|
||||
return -1;
|
||||
}
|
||||
if (!ao2_container_count(conference_bridges)) {
|
||||
ast_log(LOG_ERROR, "No active conferneces.\n");
|
||||
return -1;
|
||||
}
|
||||
ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
|
||||
bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
|
||||
if (!bridge) {
|
||||
ast_log(LOG_ERROR, "Confernece '%s' not found.\n", args.confno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get the correct count for the type requested */
|
||||
ao2_lock(bridge);
|
||||
if (!strncasecmp(args.type, "parties", 7)) {
|
||||
AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
|
||||
count++;
|
||||
}
|
||||
} else if (!strncasecmp(args.type, "admins", 6)) {
|
||||
AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
|
||||
if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else if (!strncasecmp(args.type, "marked", 6)) {
|
||||
AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
|
||||
if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else if (!strncasecmp(args.type, "locked", 6)) {
|
||||
count = bridge->locked;
|
||||
} else {
|
||||
ao2_unlock(bridge);
|
||||
ao2_ref(bridge, -1);
|
||||
return -1;
|
||||
}
|
||||
snprintf(buf, len, "%d", count);
|
||||
ao2_unlock(bridge);
|
||||
ao2_ref(bridge, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Called when module is being unloaded */
|
||||
static int unload_module(void)
|
||||
|
@ -2378,6 +2481,7 @@ static int unload_module(void)
|
|||
int res = ast_unregister_application(app);
|
||||
|
||||
ast_custom_function_unregister(&confbridge_function);
|
||||
ast_custom_function_unregister(&confbridge_info_function);
|
||||
|
||||
ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
|
||||
|
||||
|
@ -2409,6 +2513,9 @@ static int load_module(void)
|
|||
if ((ast_custom_function_register(&confbridge_function))) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
if ((ast_custom_function_register(&confbridge_info_function))) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
if (!(record_tech.capabilities = ast_format_cap_alloc())) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
|
|
@ -97,19 +97,6 @@ static const char app[] = "Pickup";
|
|||
static const char app2[] = "PickupChan";
|
||||
/*! \todo This application should return a result code, like PICKUPRESULT */
|
||||
|
||||
/* Helper function that determines whether a channel is capable of being picked up */
|
||||
static int can_pickup(struct ast_channel *chan)
|
||||
{
|
||||
if (!chan->pbx && !chan->masq &&
|
||||
!ast_test_flag(chan, AST_FLAG_ZOMBIE) &&
|
||||
(chan->_state == AST_STATE_RINGING ||
|
||||
chan->_state == AST_STATE_RING ||
|
||||
chan->_state == AST_STATE_DOWN)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pickup_by_name_args {
|
||||
const char *name;
|
||||
size_t len;
|
||||
|
@ -121,7 +108,7 @@ static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags)
|
|||
struct pickup_by_name_args *args = data;
|
||||
|
||||
ast_channel_lock(target);
|
||||
if (!strncasecmp(target->name, args->name, args->len) && can_pickup(target)) {
|
||||
if (!strncasecmp(target->name, args->name, args->len) && ast_can_pickup(target)) {
|
||||
/* Return with the channel still locked on purpose */
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
@ -190,7 +177,7 @@ static int pickup_by_exten(struct ast_channel *chan, const char *exten, const ch
|
|||
|
||||
while ((target = ast_channel_iterator_next(iter))) {
|
||||
ast_channel_lock(target);
|
||||
if ((chan != target) && can_pickup(target)) {
|
||||
if ((chan != target) && ast_can_pickup(target)) {
|
||||
ast_log(LOG_NOTICE, "%s pickup by %s\n", target->name, chan->name);
|
||||
break;
|
||||
}
|
||||
|
@ -217,7 +204,7 @@ static int find_by_mark(void *obj, void *arg, void *data, int flags)
|
|||
|
||||
ast_channel_lock(target);
|
||||
tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
|
||||
if (tmp && !strcasecmp(tmp, mark) && can_pickup(target)) {
|
||||
if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
|
||||
/* Return with the channel still locked on purpose */
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
@ -249,7 +236,8 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
|
|||
struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
|
||||
|
||||
ast_channel_lock(target);
|
||||
if (chan != target && (chan->pickupgroup & target->callgroup) && can_pickup(target)) {
|
||||
if (chan != target && (chan->pickupgroup & target->callgroup)
|
||||
&& ast_can_pickup(target)) {
|
||||
/* Return with the channel still locked on purpose */
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
@ -316,7 +304,7 @@ static int find_by_part(void *obj, void *arg, void *data, int flags)
|
|||
|
||||
ast_channel_lock(target);
|
||||
if (len <= strlen(target->name) && !strncmp(target->name, part, len)
|
||||
&& can_pickup(target)) {
|
||||
&& ast_can_pickup(target)) {
|
||||
/* Return with the channel still locked on purpose */
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
|
|
@ -3482,7 +3482,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
if (o->stillgoing) { /* Keep track of important channels */
|
||||
stillgoing = 1;
|
||||
if (o->chan) {
|
||||
watchers[pos++] = o->chan;
|
||||
if (pos < AST_MAX_WATCHERS) {
|
||||
watchers[pos++] = o->chan;
|
||||
}
|
||||
if (!start)
|
||||
start = o;
|
||||
else
|
||||
|
|
|
@ -582,7 +582,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con
|
|||
|
||||
static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *action_names)
|
||||
{
|
||||
struct conf_menu_entry *menu_entry = NULL;
|
||||
struct conf_menu_entry *menu_entry = NULL, *cur = NULL;
|
||||
int res = 0;
|
||||
char *tmp_action_names = ast_strdupa(action_names);
|
||||
char *action = NULL;
|
||||
|
@ -691,6 +691,16 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* remove any list entry with an identical DTMF sequence for overrides */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
||||
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
ast_free(cur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
AST_LIST_INSERT_TAIL(&menu->entries, menu_entry, entry);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -215,6 +215,7 @@ struct conference_bridge_user {
|
|||
struct ast_bridge_features features; /*!< Bridge features structure */
|
||||
struct ast_bridge_tech_optimizations tech_args; /*!< Bridge technology optimizations for talk detection */
|
||||
unsigned int kicked:1; /*!< User has been kicked from the conference */
|
||||
unsigned int playing_moh:1; /*!< MOH is currently being played to the user */
|
||||
AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
|
||||
};
|
||||
|
||||
|
|
|
@ -217,6 +217,7 @@ static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, s
|
|||
*outchan = p->chan;
|
||||
}
|
||||
|
||||
/* Called with ast locked */
|
||||
static int local_setoption(struct ast_channel *ast, int option, void * data, int datalen)
|
||||
{
|
||||
int res = 0;
|
||||
|
@ -225,27 +226,22 @@ static int local_setoption(struct ast_channel *ast, int option, void * data, int
|
|||
ast_chan_write_info_t *write_info;
|
||||
|
||||
if (option != AST_OPTION_CHANNEL_WRITE) {
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_info = data;
|
||||
|
||||
if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
|
||||
ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get the tech pvt */
|
||||
ast_channel_lock(ast);
|
||||
if (!(p = ast->tech_pvt)) {
|
||||
ast_channel_unlock(ast);
|
||||
res = -1;
|
||||
goto setoption_cleanup;
|
||||
return -1;
|
||||
}
|
||||
ao2_ref(p, 1);
|
||||
ast_channel_unlock(ast);
|
||||
ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
|
||||
|
||||
/* get the channel we are supposed to write to */
|
||||
ao2_lock(p);
|
||||
|
@ -272,6 +268,7 @@ setoption_cleanup:
|
|||
if (otherchan) {
|
||||
ast_channel_unref(otherchan);
|
||||
}
|
||||
ast_channel_lock(ast); /* Lock back before we leave */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -348,6 +345,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
|
|||
return bridged;
|
||||
}
|
||||
|
||||
/* Called with ast locked */
|
||||
static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
|
||||
{
|
||||
struct local_pvt *p;
|
||||
|
@ -361,21 +359,18 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in
|
|||
}
|
||||
|
||||
/* for some reason the channel is not locked in channel.c when this function is called */
|
||||
ast_channel_lock(ast);
|
||||
if (!(p = ast->tech_pvt)) {
|
||||
ast_channel_unlock(ast);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao2_lock(p);
|
||||
if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) {
|
||||
ao2_unlock(p);
|
||||
ast_channel_unlock(ast);
|
||||
return -1;
|
||||
}
|
||||
ast_channel_ref(tmp);
|
||||
ao2_unlock(p);
|
||||
ast_channel_unlock(ast);
|
||||
ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */
|
||||
|
||||
ast_channel_lock(tmp);
|
||||
if (!(bridged = ast_bridged_channel(tmp))) {
|
||||
|
@ -394,6 +389,7 @@ query_cleanup:
|
|||
if (tmp) {
|
||||
tmp = ast_channel_unref(tmp);
|
||||
}
|
||||
ast_channel_lock(ast); /* Lock back before we leave */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -4262,15 +4262,23 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d
|
|||
int res = -1;
|
||||
struct sip_pvt *p = chan->tech_pvt;
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_FORMAT_READ:
|
||||
res = ast_rtp_instance_set_read_format(p->rtp, (struct ast_format *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_set_read_format(p->rtp, (struct ast_format *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_FORMAT_WRITE:
|
||||
res = ast_rtp_instance_set_write_format(p->rtp, (struct ast_format *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_set_write_format(p->rtp, (struct ast_format *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_MAKE_COMPATIBLE:
|
||||
res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
|
||||
if (p->rtp) {
|
||||
res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
|
||||
|
@ -4298,6 +4306,8 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d
|
|||
break;
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -4309,16 +4319,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
|||
struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
|
||||
char *cp;
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_T38_STATE:
|
||||
/* Make sure we got an ast_t38_state enum passed in */
|
||||
if (*datalen != sizeof(enum ast_t38_state)) {
|
||||
ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
sip_pvt_lock(p);
|
||||
|
||||
/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
|
||||
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
|
||||
switch (p->t38.state) {
|
||||
|
@ -4337,8 +4347,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
|||
}
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
*((enum ast_t38_state *) data) = state;
|
||||
res = 0;
|
||||
|
||||
|
@ -4370,6 +4378,8 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
|
|||
break;
|
||||
}
|
||||
|
||||
sip_pvt_unlock(p);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -5061,6 +5071,7 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
|
|||
}
|
||||
ast_rtp_instance_set_timeout(dialog->vrtp, global_rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->vrtp, global_rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->vrtp, global_rtpholdtimeout);
|
||||
|
||||
ast_rtp_instance_set_prop(dialog->vrtp, AST_RTP_PROPERTY_RTCP, 1);
|
||||
}
|
||||
|
@ -5071,12 +5082,14 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
|
|||
}
|
||||
ast_rtp_instance_set_timeout(dialog->trtp, global_rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->trtp, global_rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->trtp, global_rtpholdtimeout);
|
||||
|
||||
ast_rtp_instance_set_prop(dialog->trtp, AST_RTP_PROPERTY_RTCP, 1);
|
||||
}
|
||||
|
||||
ast_rtp_instance_set_timeout(dialog->rtp, global_rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->rtp, global_rtpkeepalive);
|
||||
|
||||
ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1);
|
||||
ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
|
||||
|
@ -5144,6 +5157,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
|
|||
ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
|
||||
ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->rtp, peer->rtpkeepalive);
|
||||
/* Set Frame packetization */
|
||||
ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(dialog->rtp), dialog->rtp, &dialog->prefs);
|
||||
dialog->autoframing = peer->autoframing;
|
||||
|
@ -5151,10 +5165,12 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
|
|||
if (dialog->vrtp) { /* Video */
|
||||
ast_rtp_instance_set_timeout(dialog->vrtp, peer->rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->vrtp, peer->rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->vrtp, peer->rtpkeepalive);
|
||||
}
|
||||
if (dialog->trtp) { /* Realtime text */
|
||||
ast_rtp_instance_set_timeout(dialog->trtp, peer->rtptimeout);
|
||||
ast_rtp_instance_set_hold_timeout(dialog->trtp, peer->rtpholdtimeout);
|
||||
ast_rtp_instance_set_keepalive(dialog->trtp, peer->rtpkeepalive);
|
||||
}
|
||||
|
||||
/* XXX TODO: get fields directly from peer only as they are needed using dialog->relatedpeer */
|
||||
|
@ -6344,7 +6360,11 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
|
|||
}
|
||||
if (p) {
|
||||
sip_pvt_lock(p);
|
||||
if (p->rtp) {
|
||||
if (p->t38.state == T38_ENABLED) {
|
||||
/* drop frame, can't sent VOICE frames while in T.38 mode */
|
||||
sip_pvt_unlock(p);
|
||||
break;
|
||||
} else if (p->rtp) {
|
||||
/* If channel is not up, activate early media session */
|
||||
if ((ast->_state != AST_STATE_UP) &&
|
||||
!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
|
||||
|
@ -6355,12 +6375,9 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
|
|||
transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
|
||||
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
|
||||
}
|
||||
} else if (p->t38.state == T38_ENABLED) {
|
||||
/* drop frame, can't sent VOICE frames while in T.38 mode */
|
||||
} else {
|
||||
p->lastrtptx = time(NULL);
|
||||
res = ast_rtp_instance_write(p->rtp, frame);
|
||||
}
|
||||
p->lastrtptx = time(NULL);
|
||||
res = ast_rtp_instance_write(p->rtp, frame);
|
||||
}
|
||||
sip_pvt_unlock(p);
|
||||
}
|
||||
|
@ -7274,12 +7291,19 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
|
|||
|
||||
/* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */
|
||||
if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
|
||||
ast_channel_lock(ast);
|
||||
if (strcmp(ast->exten, "fax")) {
|
||||
const char *target_context = S_OR(ast->macrocontext, ast->context);
|
||||
/* We need to unlock 'ast' here because
|
||||
* ast_exists_extension has the potential to start and
|
||||
* stop an autoservice on the channel. Such action is
|
||||
* prone to deadlock if the channel is locked.
|
||||
*/
|
||||
sip_pvt_unlock(p);
|
||||
ast_channel_unlock(ast);
|
||||
if (ast_exists_extension(ast, target_context, "fax", 1,
|
||||
S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) {
|
||||
ast_channel_lock(ast);
|
||||
sip_pvt_lock(p);
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
|
||||
pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
|
||||
if (ast_async_goto(ast, target_context, "fax", 1)) {
|
||||
|
@ -7287,10 +7311,10 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
|
|||
}
|
||||
fr = &ast_null_frame;
|
||||
} else {
|
||||
ast_channel_lock(ast);
|
||||
sip_pvt_lock(p);
|
||||
ast_log(LOG_NOTICE, "FAX CNG detected but no fax extension\n");
|
||||
}
|
||||
} else {
|
||||
ast_channel_unlock(ast);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15749,8 +15773,12 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
|
|||
p->callingpres = peer->callingpres;
|
||||
}
|
||||
ast_string_field_set(p, fullcontact, peer->fullcontact);
|
||||
if (!ast_strlen_zero(peer->context))
|
||||
if (!ast_strlen_zero(peer->context)) {
|
||||
ast_string_field_set(p, context, peer->context);
|
||||
}
|
||||
if (!ast_strlen_zero(peer->messagecontext)) {
|
||||
ast_string_field_set(p, messagecontext, peer->messagecontext);
|
||||
}
|
||||
ast_string_field_set(p, peersecret, peer->secret);
|
||||
ast_string_field_set(p, peermd5secret, peer->md5secret);
|
||||
ast_string_field_set(p, language, peer->language);
|
||||
|
@ -16080,6 +16108,10 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
|
|||
if (ast_strlen_zero(peer->secret) && ast_strlen_zero(peer->md5secret)) {
|
||||
ast_string_field_set(p, context, peer->context);
|
||||
}
|
||||
if (!ast_strlen_zero(peer->messagecontext)) {
|
||||
ast_string_field_set(p, messagecontext, peer->messagecontext);
|
||||
}
|
||||
ast_string_field_set(p, peername, peer->name);
|
||||
peer = unref_peer(peer, "from find_peer() in receive_message");
|
||||
}
|
||||
}
|
||||
|
@ -16100,7 +16132,19 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
|
|||
res = ast_msg_set_to(msg, "%s", to);
|
||||
res |= ast_msg_set_from(msg, "%s", get_in_brackets(from));
|
||||
res |= ast_msg_set_body(msg, "%s", ast_str_buffer(buf));
|
||||
res |= ast_msg_set_context(msg, "%s", p->context);
|
||||
|
||||
if (!ast_strlen_zero(p->messagecontext)) {
|
||||
res |= ast_msg_set_context(msg, "%s", p->messagecontext);
|
||||
} else if (!ast_strlen_zero(sip_cfg.messagecontext)) {
|
||||
res |= ast_msg_set_context(msg, "%s", sip_cfg.messagecontext);
|
||||
} else {
|
||||
res |= ast_msg_set_context(msg, "%s", p->context);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(p->peername)) {
|
||||
res |= ast_msg_set_var(msg, "SIP_PEERNAME", p->peername);
|
||||
}
|
||||
|
||||
res |= ast_msg_set_exten(msg, "%s", p->exten);
|
||||
|
||||
if (res) {
|
||||
|
@ -25368,11 +25412,19 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
|
|||
}
|
||||
|
||||
/* If we have no timers set, return now */
|
||||
if (!ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) {
|
||||
if (!ast_rtp_instance_get_keepalive(dialog->rtp) && !ast_rtp_instance_get_timeout(dialog->rtp) && !ast_rtp_instance_get_hold_timeout(dialog->rtp)) {
|
||||
dialog_unlink_rtpcheck(dialog);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check AUDIO RTP keepalives */
|
||||
if (dialog->lastrtptx && ast_rtp_instance_get_keepalive(dialog->rtp) &&
|
||||
(t > dialog->lastrtptx + ast_rtp_instance_get_keepalive(dialog->rtp))) {
|
||||
/* Need to send an empty RTP packet */
|
||||
dialog->lastrtptx = time(NULL);
|
||||
ast_rtp_instance_sendcng(dialog->rtp, 0);
|
||||
}
|
||||
|
||||
/*! \todo Check video RTP keepalives
|
||||
|
||||
Do we need to move the lastrtptx to the RTP structure to have one for audio and one
|
||||
|
@ -26671,6 +26723,7 @@ static void set_peer_defaults(struct sip_peer *peer)
|
|||
ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
|
||||
ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
|
||||
ast_string_field_set(peer, context, sip_cfg.default_context);
|
||||
ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext);
|
||||
ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
|
||||
ast_string_field_set(peer, language, default_language);
|
||||
ast_string_field_set(peer, mohinterpret, default_mohinterpret);
|
||||
|
@ -26965,6 +27018,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
|||
} else if (!strcasecmp(v->name, "context")) {
|
||||
ast_string_field_set(peer, context, v->value);
|
||||
ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT);
|
||||
} else if (!strcasecmp(v->name, "outofcall_message_context")) {
|
||||
ast_string_field_set(peer, messagecontext, v->value);
|
||||
} else if (!strcasecmp(v->name, "subscribecontext")) {
|
||||
ast_string_field_set(peer, subscribecontext, v->value);
|
||||
} else if (!strcasecmp(v->name, "fromdomain")) {
|
||||
|
@ -27351,7 +27406,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
|||
* specified, use that address instead. */
|
||||
/* XXX May need to revisit the final argument; does the realtime DB store whether
|
||||
* the original contact was over TLS or not? XXX */
|
||||
if (!ast_test_flag(&peer->flags[0], SIP_NAT_RPORT_PRESENT) || ast_sockaddr_isnull(&peer->addr)) {
|
||||
if (!ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) || ast_sockaddr_isnull(&peer->addr)) {
|
||||
__set_address_from_contact(fullcontact->str, &peer->addr, 0);
|
||||
}
|
||||
}
|
||||
|
@ -27653,6 +27708,7 @@ static int reload_config(enum channelreloadreason reason)
|
|||
sip_cfg.alwaysauthreject = DEFAULT_ALWAYSAUTHREJECT;
|
||||
sip_cfg.auth_options_requests = DEFAULT_AUTH_OPTIONS;
|
||||
sip_cfg.auth_message_requests = DEFAULT_AUTH_MESSAGE;
|
||||
sip_cfg.messagecontext[0] = '\0';
|
||||
sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
|
||||
sip_cfg.allowsubscribe = FALSE;
|
||||
sip_cfg.disallowed_methods = SIP_UNKNOWN;
|
||||
|
@ -27906,6 +27962,8 @@ static int reload_config(enum channelreloadreason reason)
|
|||
sip_cfg.auth_message_requests = ast_true(v->value) ? 1 : 0;
|
||||
} else if (!strcasecmp(v->name, "accept_outofcall_message")) {
|
||||
sip_cfg.accept_outofcall_message = ast_true(v->value) ? 1 : 0;
|
||||
} else if (!strcasecmp(v->name, "outofcall_message_context")) {
|
||||
ast_copy_string(sip_cfg.messagecontext, v->value, sizeof(sip_cfg.messagecontext));
|
||||
} else if (!strcasecmp(v->name, "mohinterpret")) {
|
||||
ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret));
|
||||
} else if (!strcasecmp(v->name, "mohsuggest")) {
|
||||
|
|
|
@ -1126,6 +1126,10 @@ static int callnums = 1;
|
|||
#define SKINNY_ALERT 0x24
|
||||
#define SKINNY_REORDER 0x25
|
||||
#define SKINNY_CALLWAITTONE 0x2D
|
||||
#define SKINNY_ZIPZIP 0x31
|
||||
#define SKINNY_ZIP 0x32
|
||||
#define SKINNY_BEEPBONK 0x33
|
||||
#define SKINNY_BARGIN 0x43
|
||||
#define SKINNY_NOTONE 0x7F
|
||||
|
||||
#define SKINNY_LAMP_OFF 1
|
||||
|
@ -1164,18 +1168,10 @@ static const char * const skinny_cxmodes[] = {
|
|||
|
||||
/* driver scheduler */
|
||||
static struct ast_sched_context *sched = NULL;
|
||||
static struct io_context *io;
|
||||
|
||||
/* Protect the monitoring thread, so only one process can kill or start it, and not
|
||||
when it's doing something critical. */
|
||||
AST_MUTEX_DEFINE_STATIC(monlock);
|
||||
/* Protect the network socket */
|
||||
AST_MUTEX_DEFINE_STATIC(netlock);
|
||||
|
||||
/* This is the thread for the monitor which checks for input on the channels
|
||||
which are not currently in use. */
|
||||
static pthread_t monitor_thread = AST_PTHREADT_NULL;
|
||||
|
||||
/* Wait up to 16 seconds for first digit */
|
||||
static int firstdigittimeout = 16000;
|
||||
|
||||
|
@ -1215,7 +1211,9 @@ struct skinny_subchannel {
|
|||
int blindxfer;
|
||||
int xferor;
|
||||
int substate;
|
||||
|
||||
int aa_sched;
|
||||
int aa_beep;
|
||||
int aa_mute;
|
||||
|
||||
AST_LIST_ENTRY(skinny_subchannel) list;
|
||||
struct skinny_subchannel *related;
|
||||
|
@ -1712,6 +1710,16 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
|
|||
return list;
|
||||
}
|
||||
|
||||
static int skinny_sched_del(int sched_id)
|
||||
{
|
||||
return ast_sched_del(sched, sched_id);
|
||||
}
|
||||
|
||||
static int skinny_sched_add(int when, ast_sched_cb callback, const void *data)
|
||||
{
|
||||
return ast_sched_add(sched, when, callback, data);
|
||||
}
|
||||
|
||||
/* It's quicker/easier to find the subchannel when we know the instance number too */
|
||||
static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
|
||||
{
|
||||
|
@ -2235,7 +2243,7 @@ static void transmit_speaker_mode(struct skinny_device *d, int mode)
|
|||
req->data.setspeaker.mode = htolel(mode);
|
||||
transmit_response(d, req);
|
||||
}
|
||||
/*
|
||||
|
||||
static void transmit_microphone_mode(struct skinny_device *d, int mode)
|
||||
{
|
||||
struct skinny_req *req;
|
||||
|
@ -2246,7 +2254,6 @@ static void transmit_microphone_mode(struct skinny_device *d, int mode)
|
|||
req->data.setmicrophone.mode = htolel(mode);
|
||||
transmit_response(d, req);
|
||||
}
|
||||
*/
|
||||
|
||||
static void transmit_callinfo(struct skinny_subchannel *sub)
|
||||
{
|
||||
|
@ -4044,7 +4051,13 @@ static void *skinny_ss(void *data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int skinny_autoanswer_cb(const void *data)
|
||||
{
|
||||
struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
|
||||
sub->aa_sched = 0;
|
||||
setsubstate(sub, SKINNY_CONNECTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
|
||||
{
|
||||
|
@ -4052,6 +4065,8 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
|
|||
struct skinny_subchannel *sub = ast->tech_pvt;
|
||||
struct skinny_line *l = sub->line;
|
||||
struct skinny_device *d = l->device;
|
||||
struct ast_var_t *current;
|
||||
int doautoanswer = 0;
|
||||
|
||||
if (!d->registered) {
|
||||
ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
|
||||
|
@ -4075,8 +4090,40 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
|
|||
ast_queue_control(ast, AST_CONTROL_BUSY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
AST_LIST_TRAVERSE(&ast->varshead, current, entries) {
|
||||
if (!(strcasecmp(ast_var_name(current),"SKINNY_AUTOANSWER"))) {
|
||||
if (d->hookstate == SKINNY_ONHOOK && !sub->aa_sched) {
|
||||
char buf[24];
|
||||
int aatime;
|
||||
char *stringp = buf, *curstr;
|
||||
ast_copy_string(buf, ast_var_value(current), sizeof(buf));
|
||||
curstr = strsep(&stringp, ":");
|
||||
ast_verb(3, "test %s\n", curstr);
|
||||
aatime = atoi(curstr);
|
||||
while ((curstr = strsep(&stringp, ":"))) {
|
||||
if (!(strcasecmp(curstr,"BEEP"))) {
|
||||
sub->aa_beep = 1;
|
||||
} else if (!(strcasecmp(curstr,"MUTE"))) {
|
||||
sub->aa_mute = 1;
|
||||
}
|
||||
}
|
||||
if (skinnydebug)
|
||||
ast_verb(3, "Sub %d - setting autoanswer time=%dms %s%s\n", sub->callid, aatime, sub->aa_beep?"BEEP ":"", sub->aa_mute?"MUTE":"");
|
||||
if (aatime) {
|
||||
//sub->aa_sched = ast_sched_add(sched, aatime, skinny_autoanswer_cb, sub);
|
||||
sub->aa_sched = skinny_sched_add(aatime, skinny_autoanswer_cb, sub);
|
||||
} else {
|
||||
doautoanswer = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setsubstate(sub, SUBSTATE_RINGIN);
|
||||
if (doautoanswer) {
|
||||
setsubstate(sub, SUBSTATE_CONNECTED);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -4637,6 +4684,13 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
|
|||
if (sub->substate == SUBSTATE_ONHOOK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != SUBSTATE_RINGIN && sub->aa_sched) {
|
||||
skinny_sched_del(sub->aa_sched);
|
||||
sub->aa_sched = 0;
|
||||
sub->aa_beep = 0;
|
||||
sub->aa_mute = 0;
|
||||
}
|
||||
|
||||
if ((state == SUBSTATE_RINGIN) && ((d->hookstate == SKINNY_OFFHOOK) || (AST_LIST_NEXT(AST_LIST_FIRST(&l->sub), list)))) {
|
||||
actualstate = SUBSTATE_CALLWAIT;
|
||||
|
@ -4789,6 +4843,7 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
|
|||
ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
|
||||
transmit_connect(d, sub);
|
||||
}
|
||||
transmit_ringer_mode(d, SKINNY_RING_OFF);
|
||||
transmit_activatecallplane(d, l);
|
||||
transmit_stop_tone(d, l->instance, sub->callid);
|
||||
transmit_callinfo(sub);
|
||||
|
@ -4798,6 +4853,12 @@ static void setsubstate(struct skinny_subchannel *sub, int state)
|
|||
if (!sub->rtp) {
|
||||
start_rtp(sub);
|
||||
}
|
||||
if (sub->aa_beep) {
|
||||
transmit_start_tone(d, SKINNY_ZIP, l->instance, sub->callid);
|
||||
}
|
||||
if (sub->aa_mute) {
|
||||
transmit_microphone_mode(d, SKINNY_MICOFF);
|
||||
}
|
||||
if (sub->substate == SUBSTATE_RINGIN || sub->substate == SUBSTATE_CALLWAIT) {
|
||||
ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
|
||||
}
|
||||
|
@ -6499,59 +6560,6 @@ static void *accept_thread(void *ignore)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *do_monitor(void *data)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* This thread monitors all the interfaces which are not yet in use
|
||||
(and thus do not have a separate thread) indefinitely */
|
||||
/* From here on out, we die whenever asked */
|
||||
for(;;) {
|
||||
pthread_testcancel();
|
||||
/* Wait for sched or io */
|
||||
res = ast_sched_wait(sched);
|
||||
if ((res < 0) || (res > 1000)) {
|
||||
res = 1000;
|
||||
}
|
||||
res = ast_io_wait(io, res);
|
||||
ast_mutex_lock(&monlock);
|
||||
if (res >= 0) {
|
||||
ast_sched_runq(sched);
|
||||
}
|
||||
ast_mutex_unlock(&monlock);
|
||||
}
|
||||
/* Never reached */
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
static int restart_monitor(void)
|
||||
{
|
||||
/* If we're supposed to be stopped -- stay stopped */
|
||||
if (monitor_thread == AST_PTHREADT_STOP)
|
||||
return 0;
|
||||
|
||||
ast_mutex_lock(&monlock);
|
||||
if (monitor_thread == pthread_self()) {
|
||||
ast_mutex_unlock(&monlock);
|
||||
ast_log(LOG_WARNING, "Cannot kill myself\n");
|
||||
return -1;
|
||||
}
|
||||
if (monitor_thread != AST_PTHREADT_NULL) {
|
||||
/* Wake up the thread */
|
||||
pthread_kill(monitor_thread, SIGURG);
|
||||
} else {
|
||||
/* Start a new monitor */
|
||||
if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
|
||||
ast_mutex_unlock(&monlock);
|
||||
ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&monlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skinny_devicestate(void *data)
|
||||
{
|
||||
struct skinny_line *l;
|
||||
|
@ -6591,7 +6599,6 @@ static struct ast_channel *skinny_request(const char *type, struct ast_format_ca
|
|||
if (!tmpc) {
|
||||
ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
|
||||
}
|
||||
restart_monitor();
|
||||
return tmpc;
|
||||
}
|
||||
|
||||
|
@ -7425,13 +7432,13 @@ static int load_module(void)
|
|||
sched = ast_sched_context_create();
|
||||
if (!sched) {
|
||||
ast_log(LOG_WARNING, "Unable to create schedule context\n");
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
io = io_context_create();
|
||||
if (!io) {
|
||||
ast_log(LOG_WARNING, "Unable to create I/O context\n");
|
||||
if (ast_sched_start_thread(sched)) {
|
||||
ast_sched_context_destroy(sched);
|
||||
sched = NULL;
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
/* And start the monitor for the first time */
|
||||
restart_monitor();
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
@ -7483,15 +7490,6 @@ static int unload_module(void)
|
|||
|
||||
delete_devices();
|
||||
|
||||
ast_mutex_lock(&monlock);
|
||||
if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
|
||||
pthread_cancel(monitor_thread);
|
||||
pthread_kill(monitor_thread, SIGURG);
|
||||
pthread_join(monitor_thread, NULL);
|
||||
}
|
||||
monitor_thread = AST_PTHREADT_STOP;
|
||||
ast_mutex_unlock(&monlock);
|
||||
|
||||
ast_mutex_lock(&netlock);
|
||||
if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
|
||||
pthread_cancel(accept_t);
|
||||
|
|
|
@ -691,6 +691,7 @@ struct sip_settings {
|
|||
int legacy_useroption_parsing; /*!< Whether to strip useroptions in URI via semicolons */
|
||||
int matchexternaddrlocally; /*!< Match externaddr/externhost setting against localnet setting */
|
||||
char regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */
|
||||
char messagecontext[AST_MAX_CONTEXT]; /*!< Default context for out of dialog msgs. */
|
||||
unsigned int disallowed_methods; /*!< methods that we should never try to use */
|
||||
int notifyringing; /*!< Send notifications on ringing */
|
||||
int notifyhold; /*!< Send notifications on hold */
|
||||
|
@ -939,6 +940,7 @@ struct sip_pvt {
|
|||
AST_STRING_FIELD(useragent); /*!< User agent in SIP request */
|
||||
AST_STRING_FIELD(exten); /*!< Extension where to start */
|
||||
AST_STRING_FIELD(context); /*!< Context for this call */
|
||||
AST_STRING_FIELD(messagecontext); /*!< Default context for outofcall messages. */
|
||||
AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */
|
||||
AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */
|
||||
AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */
|
||||
|
@ -1172,6 +1174,7 @@ struct sip_peer {
|
|||
AST_STRING_FIELD(description); /*!< Description of this peer */
|
||||
AST_STRING_FIELD(remotesecret); /*!< Remote secret (trunks, remote devices) */
|
||||
AST_STRING_FIELD(context); /*!< Default context for incoming calls */
|
||||
AST_STRING_FIELD(messagecontext); /*!< Default context for outofcall messages. */
|
||||
AST_STRING_FIELD(subscribecontext); /*!< Default context for subscriptions */
|
||||
AST_STRING_FIELD(username); /*!< Temporary username until registration */
|
||||
AST_STRING_FIELD(accountcode); /*!< Account code */
|
||||
|
|
|
@ -389,6 +389,11 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||
; call. By default, this option is enabled. When enabled, MESSAGE
|
||||
; requests are passed in to the dialplan.
|
||||
|
||||
;outofcall_message_context = messages ; Context all out of dialog msgs are sent to. When this
|
||||
; option is not set, the context used during peer matching
|
||||
; is used. This option can be defined at both the peer and
|
||||
; global level.
|
||||
|
||||
;auth_message_requests = yes ; Enabling this option will authenticate MESSAGE requests.
|
||||
; By default this option is enabled. However, it can be disabled
|
||||
; should an application desire to not load the Asterisk server with
|
||||
|
|
|
@ -47,8 +47,8 @@ int ast_db_del(const char *family, const char *key);
|
|||
* only keytree is NULL, all entries within the family will be purged.
|
||||
* It is an error for keytree to have a value when family is NULL.
|
||||
*
|
||||
* \retval 0 Entries were deleted
|
||||
* \retval -1 An error occurred
|
||||
* \retval >= 0 Number of records deleted
|
||||
*/
|
||||
int ast_db_deltree(const char *family, const char *keytree);
|
||||
|
||||
|
|
|
@ -576,10 +576,10 @@ struct ast_channel_tech {
|
|||
/*! \brief Fix up a channel: If a channel is consumed, this is called. Basically update any ->owner links */
|
||||
int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan);
|
||||
|
||||
/*! \brief Set a given option */
|
||||
/*! \brief Set a given option. Called with chan locked */
|
||||
int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen);
|
||||
|
||||
/*! \brief Query a given option */
|
||||
/*! \brief Query a given option. Called with chan locked */
|
||||
int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);
|
||||
|
||||
/*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */
|
||||
|
|
|
@ -119,12 +119,28 @@ const char *ast_pickup_ext(void);
|
|||
/*! \brief Bridge a call, optionally allowing redirection */
|
||||
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
|
||||
|
||||
/*!
|
||||
* \brief Test if a channel can be picked up.
|
||||
*
|
||||
* \param chan Channel to test if can be picked up.
|
||||
*
|
||||
* \note This function assumes that chan is locked.
|
||||
*
|
||||
* \return TRUE if channel can be picked up.
|
||||
*/
|
||||
int ast_can_pickup(struct ast_channel *chan);
|
||||
|
||||
/*! \brief Pickup a call */
|
||||
int ast_pickup_call(struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief Pickup a call target
|
||||
* \note This function assumes that target is locked
|
||||
* \brief Pickup a call target.
|
||||
*
|
||||
* \param chan channel that initiated pickup.
|
||||
* \param target channel to be picked up.
|
||||
*
|
||||
* \note This function assumes that target is locked.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on failure.
|
||||
*/
|
||||
|
|
|
@ -377,6 +377,8 @@ struct ast_rtp_engine {
|
|||
void (*stun_request)(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username);
|
||||
/*! Callback to get the transcodeable formats supported. result returned in ast_format_cap *result */
|
||||
void (*available_formats)(struct ast_rtp_instance *instance, struct ast_format_cap *to_endpoint, struct ast_format_cap *to_asterisk, struct ast_format_cap *result);
|
||||
/*! Callback to send CNG */
|
||||
int (*sendcng)(struct ast_rtp_instance *instance, int level);
|
||||
/*! Linked list information */
|
||||
AST_RWLIST_ENTRY(ast_rtp_engine) entry;
|
||||
};
|
||||
|
@ -1712,6 +1714,24 @@ void ast_rtp_instance_set_timeout(struct ast_rtp_instance *instance, int timeout
|
|||
*/
|
||||
void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int timeout);
|
||||
|
||||
/*!
|
||||
* \brief Set the RTP keepalive interval
|
||||
*
|
||||
* \param instance The RTP instance
|
||||
* \param period Value to set the keepalive interval to
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* \code
|
||||
* ast_rtp_instance_set_keepalive(instance, 5000);
|
||||
* \endcode
|
||||
*
|
||||
* This sets the RTP keepalive interval on 'instance' to be 5000.
|
||||
*
|
||||
* \since 1.8
|
||||
*/
|
||||
void ast_rtp_instance_set_keepalive(struct ast_rtp_instance *instance, int timeout);
|
||||
|
||||
/*!
|
||||
* \brief Get the RTP timeout value
|
||||
*
|
||||
|
@ -1750,6 +1770,25 @@ int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance);
|
|||
*/
|
||||
int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance);
|
||||
|
||||
/*!
|
||||
* \brief Get the RTP keepalive interval
|
||||
*
|
||||
* \param instance The RTP instance
|
||||
*
|
||||
* \retval period Keepalive interval value
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* \code
|
||||
* int interval = ast_rtp_instance_get_keepalive(instance);
|
||||
* \endcode
|
||||
*
|
||||
* This gets the RTP keepalive interval value for the RTP instance pointed to by 'instance'.
|
||||
*
|
||||
* \since 1.8
|
||||
*/
|
||||
int ast_rtp_instance_get_keepalive(struct ast_rtp_instance *instance);
|
||||
|
||||
/*!
|
||||
* \brief Get the RTP engine in use on an RTP instance
|
||||
*
|
||||
|
@ -1809,6 +1848,17 @@ struct ast_rtp_glue *ast_rtp_instance_get_active_glue(struct ast_rtp_instance *i
|
|||
*/
|
||||
struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance);
|
||||
|
||||
/*!
|
||||
* \brief Send a comfort noise packet to the RTP instance
|
||||
*
|
||||
* \param instance The RTP instance
|
||||
* \param level Magnitude of the noise level
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level);
|
||||
|
||||
int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *policy);
|
||||
struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance);
|
||||
|
||||
|
|
|
@ -682,7 +682,7 @@ void ast_enable_packet_fragmentation(int sock);
|
|||
*/
|
||||
int ast_mkdir(const char *path, int mode);
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(0[a]))
|
||||
#define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a]))
|
||||
|
||||
|
||||
/* Definition for Digest authorization */
|
||||
|
|
|
@ -7387,7 +7387,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
|
|||
config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
|
||||
if ((caller_warning || callee_warning) && config->play_warning) {
|
||||
long next_warn = config->play_warning;
|
||||
if (time_left_ms < config->play_warning) {
|
||||
if (time_left_ms < config->play_warning && config->warning_freq > 0) {
|
||||
/* At least one warning was played, which means we are returning after feature */
|
||||
long warns_passed = (config->play_warning - time_left_ms) / config->warning_freq;
|
||||
/* It is 'warns_passed * warning_freq' NOT '(warns_passed + 1) * warning_freq',
|
||||
|
@ -7608,28 +7608,42 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
|
|||
/*! \brief Sets an option on a channel */
|
||||
int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!chan->tech->setoption) {
|
||||
errno = ENOSYS;
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block)
|
||||
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
|
||||
|
||||
return chan->tech->setoption(chan, option, data, datalen);
|
||||
res = chan->tech->setoption(chan, option, data, datalen);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_channel_queryoption(struct ast_channel *chan, int option, void *data, int *datalen, int block)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!chan->tech->queryoption) {
|
||||
errno = ENOSYS;
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block)
|
||||
ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
|
||||
|
||||
return chan->tech->queryoption(chan, option, data, datalen);
|
||||
res = chan->tech->queryoption(chan, option, data, datalen);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct tonepair_def {
|
||||
|
|
299
main/db.c
299
main/db.c
|
@ -100,9 +100,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
</manager>
|
||||
***/
|
||||
|
||||
#define MAX_DB_FIELD 256
|
||||
|
||||
static DB *astdb;
|
||||
AST_MUTEX_DEFINE_STATIC(dblock);
|
||||
static ast_cond_t dbcond;
|
||||
typedef int (*process_keys_cb)(DBT *key, DBT *value, const char *filter, void *data);
|
||||
|
||||
static void db_sync(void);
|
||||
|
||||
|
@ -143,15 +146,84 @@ static inline int subkeymatch(const char *key, const char *suffix)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char *dbt_data2str(DBT *dbt)
|
||||
{
|
||||
char *data = "";
|
||||
|
||||
if (dbt->size) {
|
||||
data = dbt->data;
|
||||
data[dbt->size - 1] = '\0';
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline const char *dbt_data2str_full(DBT *dbt, const char *def)
|
||||
{
|
||||
return S_OR(dbt_data2str(dbt), def);
|
||||
}
|
||||
|
||||
static int process_db_keys(process_keys_cb cb, void *data, const char *filter, int sync)
|
||||
{
|
||||
DBT key = { 0, }, value = { 0, }, last_key = { 0, };
|
||||
int counter = 0;
|
||||
int res, last = 0;
|
||||
char last_key_s[MAX_DB_FIELD];
|
||||
|
||||
ast_mutex_lock(&dblock);
|
||||
if (dbinit()) {
|
||||
ast_mutex_unlock(&dblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Somehow, the database can become corrupted such that astdb->seq will continue looping through
|
||||
* the database indefinitely. The pointer to last_key.data ends up getting re-used by the BDB lib
|
||||
* so this specifically queries for the last entry, makes a copy of the key, and then uses it as
|
||||
* a sentinel to avoid repeatedly looping over the list. */
|
||||
|
||||
if (astdb->seq(astdb, &last_key, &value, R_LAST)) {
|
||||
/* Empty database */
|
||||
ast_mutex_unlock(&dblock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(last_key_s, last_key.data, MIN(last_key.size - 1, sizeof(last_key_s)));
|
||||
last_key_s[last_key.size - 1] = '\0';
|
||||
for (res = astdb->seq(astdb, &key, &value, R_FIRST);
|
||||
!res;
|
||||
res = astdb->seq(astdb, &key, &value, R_NEXT)) {
|
||||
/* The callback might delete the key, so we have to check it before calling */
|
||||
last = !strcmp(dbt_data2str_full(&key, "<bad key>"), last_key_s);
|
||||
counter += cb(&key, &value, filter, data);
|
||||
if (last) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
db_sync();
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static int db_deltree_cb(DBT *key, DBT *value, const char *filter, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (keymatch(dbt_data2str_full(key, "<bad key>"), filter)) {
|
||||
astdb->del(astdb, key, 0);
|
||||
res = 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_db_deltree(const char *family, const char *keytree)
|
||||
{
|
||||
char prefix[256];
|
||||
DBT key, data;
|
||||
char *keys;
|
||||
int res;
|
||||
int pass;
|
||||
int counter = 0;
|
||||
|
||||
char prefix[MAX_DB_FIELD];
|
||||
|
||||
if (family) {
|
||||
if (keytree) {
|
||||
snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
|
||||
|
@ -163,36 +235,13 @@ int ast_db_deltree(const char *family, const char *keytree)
|
|||
} else {
|
||||
prefix[0] = '\0';
|
||||
}
|
||||
|
||||
ast_mutex_lock(&dblock);
|
||||
if (dbinit()) {
|
||||
ast_mutex_unlock(&dblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
pass = 0;
|
||||
while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
|
||||
if (key.size) {
|
||||
keys = key.data;
|
||||
keys[key.size - 1] = '\0';
|
||||
} else {
|
||||
keys = "<bad key>";
|
||||
}
|
||||
if (keymatch(keys, prefix)) {
|
||||
astdb->del(astdb, &key, 0);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
db_sync();
|
||||
ast_mutex_unlock(&dblock);
|
||||
return counter;
|
||||
|
||||
return process_db_keys(db_deltree_cb, NULL, prefix, 1);
|
||||
}
|
||||
|
||||
int ast_db_put(const char *family, const char *keys, const char *value)
|
||||
{
|
||||
char fullkey[256];
|
||||
char fullkey[MAX_DB_FIELD];
|
||||
DBT key, data;
|
||||
int res, fullkeylen;
|
||||
|
||||
|
@ -214,12 +263,13 @@ int ast_db_put(const char *family, const char *keys, const char *value)
|
|||
ast_mutex_unlock(&dblock);
|
||||
if (res)
|
||||
ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
|
||||
{
|
||||
char fullkey[256] = "";
|
||||
char fullkey[MAX_DB_FIELD] = "";
|
||||
DBT key, data;
|
||||
int res, fullkeylen;
|
||||
|
||||
|
@ -263,7 +313,7 @@ int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
|
|||
|
||||
int ast_db_del(const char *family, const char *keys)
|
||||
{
|
||||
char fullkey[256];
|
||||
char fullkey[MAX_DB_FIELD];
|
||||
DBT key;
|
||||
int res, fullkeylen;
|
||||
|
||||
|
@ -319,7 +369,7 @@ static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct as
|
|||
static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
int res;
|
||||
char tmp[256];
|
||||
char tmp[MAX_DB_FIELD];
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
|
@ -402,13 +452,23 @@ static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struc
|
|||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static int db_show_cb(DBT *key, DBT *value, const char *filter, void *data)
|
||||
{
|
||||
struct ast_cli_args *a = data;
|
||||
const char *key_s = dbt_data2str_full(key, "<bad key>");
|
||||
const char *value_s = dbt_data2str_full(value, "<bad value>");
|
||||
|
||||
if (keymatch(key_s, filter)) {
|
||||
ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
char prefix[256];
|
||||
DBT key, data;
|
||||
char *keys, *values;
|
||||
int res;
|
||||
int pass;
|
||||
char prefix[MAX_DB_FIELD];
|
||||
int counter = 0;
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -435,45 +495,33 @@ static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct a
|
|||
} else {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
ast_mutex_lock(&dblock);
|
||||
if (dbinit()) {
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
if((counter = process_db_keys(db_show_cb, a, prefix, 0)) < 0) {
|
||||
ast_cli(a->fd, "Database unavailable\n");
|
||||
return CLI_SUCCESS;
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
pass = 0;
|
||||
while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
|
||||
if (key.size) {
|
||||
keys = key.data;
|
||||
keys[key.size - 1] = '\0';
|
||||
} else {
|
||||
keys = "<bad key>";
|
||||
}
|
||||
if (data.size) {
|
||||
values = data.data;
|
||||
values[data.size - 1]='\0';
|
||||
} else {
|
||||
values = "<bad value>";
|
||||
}
|
||||
if (keymatch(keys, prefix)) {
|
||||
ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
ast_cli(a->fd, "%d results found.\n", counter);
|
||||
return CLI_SUCCESS;
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static int db_showkey_cb(DBT *key, DBT *value, const char *filter, void *data)
|
||||
{
|
||||
struct ast_cli_args *a = data;
|
||||
const char *key_s = dbt_data2str_full(key, "<bad key>");
|
||||
const char *value_s = dbt_data2str_full(value, "<bad value>");
|
||||
|
||||
if (subkeymatch(key_s, filter)) {
|
||||
ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
char suffix[256];
|
||||
DBT key, data;
|
||||
char *keys, *values;
|
||||
int res;
|
||||
int pass;
|
||||
char suffix[MAX_DB_FIELD];
|
||||
int counter = 0;
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -493,48 +541,40 @@ static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struc
|
|||
} else {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
ast_mutex_lock(&dblock);
|
||||
if (dbinit()) {
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
if ((counter = process_db_keys(db_showkey_cb, a, suffix, 0)) < 0) {
|
||||
ast_cli(a->fd, "Database unavailable\n");
|
||||
return CLI_SUCCESS;
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
pass = 0;
|
||||
while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
|
||||
if (key.size) {
|
||||
keys = key.data;
|
||||
keys[key.size - 1] = '\0';
|
||||
} else {
|
||||
keys = "<bad key>";
|
||||
}
|
||||
if (data.size) {
|
||||
values = data.data;
|
||||
values[data.size - 1]='\0';
|
||||
} else {
|
||||
values = "<bad value>";
|
||||
}
|
||||
if (subkeymatch(keys, suffix)) {
|
||||
ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
ast_cli(a->fd, "%d results found.\n", counter);
|
||||
return CLI_SUCCESS;
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static int db_gettree_cb(DBT *key, DBT *value, const char *filter, void *data)
|
||||
{
|
||||
struct ast_db_entry **ret = data;
|
||||
struct ast_db_entry *cur;
|
||||
const char *key_s = dbt_data2str_full(key, "<bad key>");
|
||||
const char *value_s = dbt_data2str_full(value, "<bad value>");
|
||||
size_t key_slen = strlen(key_s) + 1, value_slen = strlen(value_s) + 1;
|
||||
|
||||
if (keymatch(key_s, filter) && (cur = ast_malloc(sizeof(*cur) + key_slen + value_slen))) {
|
||||
cur->next = *ret;
|
||||
cur->key = cur->data + value_slen;
|
||||
strcpy(cur->data, value_s);
|
||||
strcpy(cur->key, key_s);
|
||||
*ret = cur;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
|
||||
{
|
||||
char prefix[256];
|
||||
DBT key, data;
|
||||
char *keys, *values;
|
||||
int values_len;
|
||||
int res;
|
||||
int pass;
|
||||
struct ast_db_entry *last = NULL;
|
||||
struct ast_db_entry *cur, *ret=NULL;
|
||||
char prefix[MAX_DB_FIELD];
|
||||
struct ast_db_entry *ret = NULL;
|
||||
|
||||
if (!ast_strlen_zero(family)) {
|
||||
if (!ast_strlen_zero(keytree)) {
|
||||
|
@ -547,44 +587,13 @@ struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
|
|||
} else {
|
||||
prefix[0] = '\0';
|
||||
}
|
||||
ast_mutex_lock(&dblock);
|
||||
if (dbinit()) {
|
||||
ast_mutex_unlock(&dblock);
|
||||
|
||||
if (process_db_keys(db_gettree_cb, &ret, prefix, 0) < 0) {
|
||||
ast_log(LOG_WARNING, "Database unavailable\n");
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&data, 0, sizeof(data));
|
||||
pass = 0;
|
||||
while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
|
||||
if (key.size) {
|
||||
keys = key.data;
|
||||
keys[key.size - 1] = '\0';
|
||||
} else {
|
||||
keys = "<bad key>";
|
||||
}
|
||||
if (data.size) {
|
||||
values = data.data;
|
||||
values[data.size - 1] = '\0';
|
||||
} else {
|
||||
values = "<bad value>";
|
||||
}
|
||||
values_len = strlen(values) + 1;
|
||||
if (keymatch(keys, prefix) && (cur = ast_malloc(sizeof(*cur) + strlen(keys) + 1 + values_len))) {
|
||||
cur->next = NULL;
|
||||
cur->key = cur->data + values_len;
|
||||
strcpy(cur->data, values);
|
||||
strcpy(cur->key, keys);
|
||||
if (last) {
|
||||
last->next = cur;
|
||||
} else {
|
||||
ret = cur;
|
||||
}
|
||||
last = cur;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&dblock);
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ast_db_freetree(struct ast_db_entry *dbe)
|
||||
|
@ -637,7 +646,7 @@ static int manager_dbget(struct mansession *s, const struct message *m)
|
|||
char idText[256] = "";
|
||||
const char *family = astman_get_header(m, "Family");
|
||||
const char *key = astman_get_header(m, "Key");
|
||||
char tmp[256];
|
||||
char tmp[MAX_DB_FIELD];
|
||||
int res;
|
||||
|
||||
if (ast_strlen_zero(family)) {
|
||||
|
|
|
@ -131,6 +131,14 @@ int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's actually an IP address and not a name, there's no
|
||||
* need for a managed lookup.
|
||||
*/
|
||||
if (ast_sockaddr_parse(result, name, PARSE_PORT_FORBID)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name);
|
||||
|
||||
/* do a lookup now but add a manager so it will automagically get updated in the background */
|
||||
|
|
29
main/event.c
29
main/event.c
|
@ -381,12 +381,12 @@ static int match_sub_ie_val_to_event(const struct ast_event_ie_val *sub_ie_val,
|
|||
int res = 0;
|
||||
|
||||
AST_LIST_TRAVERSE(&check_ie_vals->ie_vals, event_ie_val, entry) {
|
||||
if (event_ie_val->ie_type == sub_ie_val->ie_type) {
|
||||
if (sub_ie_val->ie_type == event_ie_val->ie_type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!event_ie_val) {
|
||||
/* The did not find the event ie the subscriber cares about. */
|
||||
/* We did not find the event ie the subscriber cares about. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -444,12 +444,14 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ
|
|||
};
|
||||
const enum ast_event_type event_types[] = { type, AST_EVENT_ALL };
|
||||
int i;
|
||||
int want_specific_event;/* TRUE if looking for subscribers wanting specific parameters. */
|
||||
|
||||
if (type >= AST_EVENT_TOTAL) {
|
||||
ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
|
||||
return res;
|
||||
}
|
||||
|
||||
want_specific_event = 0;
|
||||
va_start(ap, type);
|
||||
for (ie_type = va_arg(ap, enum ast_event_ie_type);
|
||||
ie_type != AST_EVENT_IE_END;
|
||||
|
@ -492,6 +494,7 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ
|
|||
}
|
||||
|
||||
if (insert) {
|
||||
want_specific_event = 1;
|
||||
AST_LIST_INSERT_TAIL(&check_ie_vals.ie_vals, ie_value, entry);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unsupported PLTYPE(%d)\n", ie_value->ie_pltype);
|
||||
|
@ -501,17 +504,22 @@ enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type typ
|
|||
|
||||
for (i = 0; i < ARRAY_LEN(event_types); i++) {
|
||||
AST_RWDLLIST_RDLOCK(&ast_event_subs[event_types[i]]);
|
||||
AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) {
|
||||
AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
|
||||
if (!match_sub_ie_val_to_event(ie_val, &check_ie_vals)) {
|
||||
/* The current subscription ie did not match an event ie. */
|
||||
if (want_specific_event) {
|
||||
AST_RWDLLIST_TRAVERSE(&ast_event_subs[event_types[i]], sub, entry) {
|
||||
AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
|
||||
if (!match_sub_ie_val_to_event(ie_val, &check_ie_vals)) {
|
||||
/* The current subscription ie did not match an event ie. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ie_val) {
|
||||
/* Everything matched. A subscriber is looking for this event. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ie_val) {
|
||||
/* Everything matched. A subscriber is looking for this event. */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Just looking to see if there are ANY subscribers to the event type. */
|
||||
sub = AST_RWLIST_FIRST(&ast_event_subs[event_types[i]]);
|
||||
}
|
||||
AST_RWDLLIST_UNLOCK(&ast_event_subs[event_types[i]]);
|
||||
if (sub) {
|
||||
|
@ -1340,6 +1348,7 @@ struct ast_event *ast_event_get_cached(enum ast_event_type type, ...)
|
|||
void *data = va_arg(ap, void *);
|
||||
size_t datalen = va_arg(ap, size_t);
|
||||
ast_event_append_ie_raw(&cache_arg_event, ie_type, data, datalen);
|
||||
break;
|
||||
}
|
||||
case AST_EVENT_IE_PLTYPE_EXISTS:
|
||||
ast_log(LOG_WARNING, "PLTYPE_EXISTS not supported by this function\n");
|
||||
|
|
101
main/features.c
101
main/features.c
|
@ -577,7 +577,7 @@ static const struct ast_datastore_info dial_features_info = {
|
|||
.type = "dial-features",
|
||||
.destroy = dial_features_destroy,
|
||||
.duplicate = dial_features_duplicate,
|
||||
};
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot);
|
||||
|
@ -2898,8 +2898,12 @@ int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, con
|
|||
|
||||
/*! \brief Check if a feature exists */
|
||||
static int feature_check(struct ast_channel *chan, struct ast_flags *features, char *code) {
|
||||
char *chan_dynamic_features;
|
||||
ast_channel_lock(chan);
|
||||
chan_dynamic_features = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"),""));
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_CHECK, NULL);
|
||||
return feature_interpret_helper(chan, NULL, NULL, code, 0, chan_dynamic_features, features, FEATURE_INTERPRET_CHECK, NULL);
|
||||
}
|
||||
|
||||
static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
|
||||
|
@ -5747,17 +5751,42 @@ static int manager_park(struct mansession *s, const struct message *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The presence of this datastore on the channel indicates that
|
||||
* someone is attemting to pickup or has picked up the channel.
|
||||
* The purpose is to prevent a race between two channels
|
||||
* attempting to pickup the same channel.
|
||||
*/
|
||||
static const struct ast_datastore_info pickup_active = {
|
||||
.type = "pickup-active",
|
||||
};
|
||||
|
||||
int ast_can_pickup(struct ast_channel *chan)
|
||||
{
|
||||
if (!chan->pbx && !chan->masq && !ast_test_flag(chan, AST_FLAG_ZOMBIE)
|
||||
&& (chan->_state == AST_STATE_RINGING
|
||||
|| chan->_state == AST_STATE_RING
|
||||
/*
|
||||
* Check the down state as well because some SIP devices do not
|
||||
* give 180 ringing when they can just give 183 session progress
|
||||
* instead. Issue 14005. (Some ISDN switches as well for that
|
||||
* matter.)
|
||||
*/
|
||||
|| chan->_state == AST_STATE_DOWN)
|
||||
&& !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct ast_channel *target = obj;/*!< Potential pickup target */
|
||||
struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
|
||||
|
||||
ast_channel_lock(target);
|
||||
if (chan != target && (chan->pickupgroup & target->callgroup) &&
|
||||
!target->pbx &&
|
||||
((target->_state == AST_STATE_RINGING) || (target->_state == AST_STATE_RING)) &&
|
||||
!target->masq &&
|
||||
!ast_test_flag(target, AST_FLAG_ZOMBIE)) {
|
||||
if (chan != target && (chan->pickupgroup & target->callgroup)
|
||||
&& ast_can_pickup(target)) {
|
||||
/* Return with the channel still locked on purpose */
|
||||
return CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
@ -5808,23 +5837,30 @@ int ast_pickup_call(struct ast_channel *chan)
|
|||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Pickup a call target, Common Code.
|
||||
* \param chan channel that initiated pickup.
|
||||
* \param target channel.
|
||||
*
|
||||
* Answer calling channel, flag channel as answered on queue, masq channels together.
|
||||
*/
|
||||
int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
|
||||
{
|
||||
struct ast_party_connected_line connected_caller;
|
||||
struct ast_channel *chans[2] = { chan, target };
|
||||
struct ast_datastore *ds_pickup;
|
||||
const char *chan_name;/*!< A masquerade changes channel names. */
|
||||
const char *target_name;/*!< A masquerade changes channel names. */
|
||||
int res = -1;
|
||||
|
||||
ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
|
||||
ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
|
||||
target_name = ast_strdupa(target->name);
|
||||
ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, chan->name);
|
||||
|
||||
/* Mark the target to block any call pickup race. */
|
||||
ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
|
||||
if (!ds_pickup) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Unable to create channel datastore on '%s' for call pickup\n", target_name);
|
||||
return -1;
|
||||
}
|
||||
ast_channel_datastore_add(target, ds_pickup);
|
||||
|
||||
ast_party_connected_line_init(&connected_caller);
|
||||
ast_party_connected_line_copy(&connected_caller, &target->connected);
|
||||
ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
|
||||
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
|
||||
if (ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
|
||||
ast_channel_update_connected_line(chan, &connected_caller, NULL);
|
||||
|
@ -5832,37 +5868,48 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
|
|||
ast_party_connected_line_free(&connected_caller);
|
||||
|
||||
ast_channel_lock(chan);
|
||||
chan_name = ast_strdupa(chan->name);
|
||||
ast_connected_line_copy_from_caller(&connected_caller, &chan->caller);
|
||||
ast_channel_unlock(chan);
|
||||
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
|
||||
ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
|
||||
ast_party_connected_line_free(&connected_caller);
|
||||
|
||||
ast_cel_report_event(target, AST_CEL_PICKUP, NULL, NULL, chan);
|
||||
|
||||
if (ast_answer(chan)) {
|
||||
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
|
||||
return -1;
|
||||
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
|
||||
goto pickup_failed;
|
||||
}
|
||||
|
||||
if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
|
||||
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
|
||||
return -1;
|
||||
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
|
||||
goto pickup_failed;
|
||||
}
|
||||
|
||||
if (ast_channel_masquerade(target, chan)) {
|
||||
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
|
||||
return -1;
|
||||
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
|
||||
target_name);
|
||||
goto pickup_failed;
|
||||
}
|
||||
|
||||
/* If you want UniqueIDs, set channelvars in manager.conf to CHANNEL(uniqueid) */
|
||||
ast_manager_event_multichan(EVENT_FLAG_CALL, "Pickup", 2, chans,
|
||||
"Channel: %s\r\nTargetChannel: %s\r\n", chan->name, target->name);
|
||||
"Channel: %s\r\n"
|
||||
"TargetChannel: %s\r\n",
|
||||
chan_name, target_name);
|
||||
|
||||
/* Do the masquerade manually to make sure that is is completed. */
|
||||
ast_channel_unlock(target);
|
||||
/* Do the masquerade manually to make sure that it is completed. */
|
||||
ast_do_masquerade(target);
|
||||
ast_channel_lock(target);
|
||||
res = 0;
|
||||
|
||||
return 0;
|
||||
pickup_failed:
|
||||
ast_channel_lock(target);
|
||||
if (!ast_channel_datastore_remove(target, ds_pickup)) {
|
||||
ast_datastore_free(ds_pickup);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *app_bridge = "Bridge";
|
||||
|
|
|
@ -5480,6 +5480,39 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *g
|
|||
}
|
||||
}
|
||||
|
||||
static void process_output(struct mansession *s, struct ast_str *out, struct ast_variable *params, enum output_format format)
|
||||
{
|
||||
char *buf;
|
||||
size_t l;
|
||||
|
||||
if (!s->f)
|
||||
return;
|
||||
|
||||
/* Ensure buffer is NULL-terminated */
|
||||
fprintf(s->f, "%c", 0);
|
||||
fflush(s->f);
|
||||
|
||||
if ((l = ftell(s->f))) {
|
||||
if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s->fd, 0))) {
|
||||
ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
|
||||
} else {
|
||||
if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, buf, params, format);
|
||||
} else {
|
||||
ast_str_append(&out, 0, "%s", buf);
|
||||
}
|
||||
munmap(buf, l);
|
||||
}
|
||||
} else if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, "", params, format);
|
||||
}
|
||||
|
||||
fclose(s->f);
|
||||
s->f = NULL;
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
enum ast_http_method method,
|
||||
enum output_format format,
|
||||
|
@ -5629,29 +5662,7 @@ static int generic_http_callback(struct ast_tcptls_session_instance *ser,
|
|||
ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
|
||||
}
|
||||
|
||||
if (s.f != NULL) { /* have temporary output */
|
||||
char *buf;
|
||||
size_t l;
|
||||
|
||||
if ((l = ftell(s.f))) {
|
||||
if (MAP_FAILED == (buf = mmap(NULL, l + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) {
|
||||
ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
|
||||
} else {
|
||||
buf[l] = '\0';
|
||||
if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, buf, params, format);
|
||||
} else {
|
||||
ast_str_append(&out, 0, "%s", buf);
|
||||
}
|
||||
munmap(buf, l + 1);
|
||||
}
|
||||
} else if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, "", params, format);
|
||||
}
|
||||
fclose(s.f);
|
||||
s.f = NULL;
|
||||
s.fd = -1;
|
||||
}
|
||||
process_output(&s, out, params, format);
|
||||
|
||||
if (format == FORMAT_XML) {
|
||||
ast_str_append(&out, 0, "</ajax-response>\n");
|
||||
|
@ -5963,26 +5974,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
|
|||
"<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
|
||||
}
|
||||
|
||||
if (s.f != NULL) { /* have temporary output */
|
||||
char *buf;
|
||||
size_t l = ftell(s.f);
|
||||
|
||||
if (l) {
|
||||
if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) {
|
||||
if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, buf, params, format);
|
||||
} else {
|
||||
ast_str_append(&out, 0, "%s", buf);
|
||||
}
|
||||
munmap(buf, l);
|
||||
}
|
||||
} else if (format == FORMAT_XML || format == FORMAT_HTML) {
|
||||
xml_translate(&out, "", params, format);
|
||||
}
|
||||
fclose(s.f);
|
||||
s.f = NULL;
|
||||
s.fd = -1;
|
||||
}
|
||||
process_output(&s, out, params, format);
|
||||
|
||||
if (format == FORMAT_XML) {
|
||||
ast_str_append(&out, 0, "</ajax-response>\n");
|
||||
|
|
|
@ -121,8 +121,10 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
|
|||
int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
|
||||
{
|
||||
char *s = str;
|
||||
char *orig_str = str;/* Original string in case the port presence is incorrect. */
|
||||
char *host_end = NULL;/* Delay terminating the host in case the port presence is incorrect. */
|
||||
|
||||
ast_debug(5, "Splitting '%s' gives...\n", str);
|
||||
ast_debug(5, "Splitting '%s' into...\n", str);
|
||||
*host = NULL;
|
||||
*port = NULL;
|
||||
if (*s == '[') {
|
||||
|
@ -130,7 +132,8 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
|
|||
for (; *s && *s != ']'; ++s) {
|
||||
}
|
||||
if (*s == ']') {
|
||||
*s++ = '\0';
|
||||
host_end = s;
|
||||
++s;
|
||||
}
|
||||
if (*s == ':') {
|
||||
*port = s + 1;
|
||||
|
@ -148,11 +151,10 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
|
|||
}
|
||||
}
|
||||
if (*port) {
|
||||
**port = '\0';
|
||||
host_end = *port;
|
||||
++*port;
|
||||
}
|
||||
}
|
||||
ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port);
|
||||
|
||||
switch (flags & PARSE_PORT_MASK) {
|
||||
case PARSE_PORT_IGNORE:
|
||||
|
@ -160,18 +162,23 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
|
|||
break;
|
||||
case PARSE_PORT_REQUIRE:
|
||||
if (*port == NULL) {
|
||||
ast_log(LOG_WARNING, "missing port\n");
|
||||
ast_log(LOG_WARNING, "Port missing in %s\n", orig_str);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case PARSE_PORT_FORBID:
|
||||
if (*port != NULL) {
|
||||
ast_log(LOG_WARNING, "port disallowed\n");
|
||||
ast_log(LOG_WARNING, "Port disallowed in %s\n", orig_str);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Can terminate the host string now if needed. */
|
||||
if (host_end) {
|
||||
*host_end = '\0';
|
||||
}
|
||||
ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port ? *port : "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ struct ast_rtp_instance {
|
|||
int timeout;
|
||||
/*! RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */
|
||||
int holdtimeout;
|
||||
/*! RTP keepalive interval */
|
||||
int keepalive;
|
||||
/*! DTMF mode in use */
|
||||
enum ast_rtp_dtmf_mode dtmf_mode;
|
||||
/*! Glue currently in use */
|
||||
|
@ -1781,6 +1783,11 @@ void ast_rtp_instance_set_hold_timeout(struct ast_rtp_instance *instance, int ti
|
|||
instance->holdtimeout = timeout;
|
||||
}
|
||||
|
||||
void ast_rtp_instance_set_keepalive(struct ast_rtp_instance *instance, int interval)
|
||||
{
|
||||
instance->keepalive = interval;
|
||||
}
|
||||
|
||||
int ast_rtp_instance_get_timeout(struct ast_rtp_instance *instance)
|
||||
{
|
||||
return instance->timeout;
|
||||
|
@ -1791,6 +1798,11 @@ int ast_rtp_instance_get_hold_timeout(struct ast_rtp_instance *instance)
|
|||
return instance->holdtimeout;
|
||||
}
|
||||
|
||||
int ast_rtp_instance_get_keepalive(struct ast_rtp_instance *instance)
|
||||
{
|
||||
return instance->keepalive;
|
||||
}
|
||||
|
||||
struct ast_rtp_engine *ast_rtp_instance_get_engine(struct ast_rtp_instance *instance)
|
||||
{
|
||||
return instance->engine;
|
||||
|
@ -1850,6 +1862,15 @@ struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance)
|
|||
return instance->srtp;
|
||||
}
|
||||
|
||||
int ast_rtp_instance_sendcng(struct ast_rtp_instance *instance, int level)
|
||||
{
|
||||
if (instance->engine->sendcng) {
|
||||
return instance->engine->sendcng(instance, level);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate)
|
||||
{
|
||||
int x = mime_types_len;
|
||||
|
|
|
@ -297,7 +297,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
</agi>
|
||||
<agi name="hangup" language="en_US">
|
||||
<synopsis>
|
||||
Hangup the current channel.
|
||||
Hangup a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="channelname" />
|
||||
|
|
|
@ -345,6 +345,7 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
|
|||
|
||||
if (!(table = ast_calloc(1, sizeof(*table) + strlen(orig_tablename) + 1))) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate memory for new table structure\n");
|
||||
PQclear(result);
|
||||
AST_LIST_UNLOCK(&psql_tables);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -363,6 +364,7 @@ static struct tables *find_table(const char *database, const char *orig_tablenam
|
|||
|
||||
if (!(column = ast_calloc(1, sizeof(*column) + strlen(fname) + strlen(ftype) + 2))) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate column element for %s, %s\n", orig_tablename, fname);
|
||||
PQclear(result);
|
||||
destroy_table(table);
|
||||
AST_LIST_UNLOCK(&psql_tables);
|
||||
return NULL;
|
||||
|
@ -479,6 +481,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
|
|||
ast_mutex_lock(&pgsql_lock);
|
||||
|
||||
if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -494,8 +497,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
|
|||
ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
|
||||
|
||||
if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < numFields; i++)
|
||||
|
@ -523,8 +526,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
|
|||
ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database);
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
|
||||
return var;
|
||||
}
|
||||
|
@ -614,8 +617,22 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
|
|||
|
||||
if (pgsql_exec(database, table, ast_str_buffer(sql), &result) != 0) {
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
ExecStatusType result_status = PQresultStatus(result);
|
||||
if (result_status != PGRES_COMMAND_OK
|
||||
&& result_status != PGRES_TUPLES_OK
|
||||
&& result_status != PGRES_NONFATAL_ERROR) {
|
||||
ast_log(LOG_WARNING,
|
||||
"PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database);
|
||||
ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
|
||||
ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
|
||||
PQresultErrorMessage(result), PQresStatus(result_status));
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, ast_str_buffer(sql));
|
||||
|
||||
|
@ -628,8 +645,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
|
|||
ast_debug(1, "PostgreSQL RealTime: Found %d rows.\n", num_rows);
|
||||
|
||||
if (!(fieldnames = ast_calloc(1, numFields * sizeof(char *)))) {
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < numFields; i++)
|
||||
|
@ -659,8 +676,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
|
|||
ast_debug(1, "PostgreSQL RealTime: Could not find any rows in table %s.\n", table);
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
@ -763,6 +780,20 @@ static int update_pgsql(const char *database, const char *tablename, const char
|
|||
if (pgsql_exec(database, tablename, ast_str_buffer(sql), &result) != 0) {
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return -1;
|
||||
} else {
|
||||
ExecStatusType result_status = PQresultStatus(result);
|
||||
if (result_status != PGRES_COMMAND_OK
|
||||
&& result_status != PGRES_TUPLES_OK
|
||||
&& result_status != PGRES_NONFATAL_ERROR) {
|
||||
ast_log(LOG_WARNING,
|
||||
"PostgreSQL RealTime: Failed to query database. Check debug for more info.\n");
|
||||
ast_debug(1, "PostgreSQL RealTime: Query: %s\n", ast_str_buffer(sql));
|
||||
ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n",
|
||||
PQresultErrorMessage(result), PQresStatus(result_status));
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
numrows = atoi(PQcmdTuples(result));
|
||||
|
@ -950,6 +981,7 @@ static int store_pgsql(const char *database, const char *table, va_list ap)
|
|||
}
|
||||
|
||||
insertid = PQoidValue(result);
|
||||
PQclear(result);
|
||||
ast_mutex_unlock(&pgsql_lock);
|
||||
|
||||
ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid);
|
||||
|
|
|
@ -272,6 +272,7 @@ static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_ins
|
|||
static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username);
|
||||
static void ast_rtp_stop(struct ast_rtp_instance *instance);
|
||||
static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
|
||||
static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
|
||||
|
||||
/* RTP Engine Declaration */
|
||||
static struct ast_rtp_engine asterisk_rtp_engine = {
|
||||
|
@ -297,6 +298,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
|
|||
.stun_request = ast_rtp_stun_request,
|
||||
.stop = ast_rtp_stop,
|
||||
.qos = ast_rtp_qos_set,
|
||||
.sendcng = ast_rtp_sendcng,
|
||||
};
|
||||
|
||||
static inline int rtp_debug_test_addr(struct ast_sockaddr *addr)
|
||||
|
@ -2591,6 +2593,49 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos,
|
|||
return ast_set_qos(rtp->s, tos, cos, desc);
|
||||
}
|
||||
|
||||
/*! \brief generate comfort noice (CNG) */
|
||||
static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
|
||||
{
|
||||
unsigned int *rtpheader;
|
||||
int hdrlen = 12;
|
||||
int res;
|
||||
int payload;
|
||||
char data[256];
|
||||
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
|
||||
struct ast_sockaddr remote_address = { {0,} };
|
||||
|
||||
ast_rtp_instance_get_remote_address(instance, &remote_address);
|
||||
|
||||
if (ast_sockaddr_isnull(&remote_address)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(instance), 0, NULL, AST_RTP_CN);
|
||||
|
||||
level = 127 - (level & 0x7f);
|
||||
|
||||
rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
|
||||
|
||||
/* Get a pointer to the header */
|
||||
rtpheader = (unsigned int *)data;
|
||||
rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++));
|
||||
rtpheader[1] = htonl(rtp->lastts);
|
||||
rtpheader[2] = htonl(rtp->ssrc);
|
||||
data[12] = level;
|
||||
|
||||
res = rtp_sendto(instance, (void *) rtpheader, hdrlen + 1, 0, &remote_address);
|
||||
|
||||
if (res < 0) {
|
||||
ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s: %s\n", ast_sockaddr_stringify(&remote_address), strerror(errno));
|
||||
} else if (rtp_debug_test_addr(&remote_address)) {
|
||||
ast_verbose("Sent Comfort Noise RTP packet to %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
|
||||
ast_sockaddr_stringify(&remote_address),
|
||||
AST_RTP_CN, rtp->seqno, rtp->lastdigitts, res - hdrlen);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *rtp_do_debug_ip(struct ast_cli_args *a)
|
||||
{
|
||||
char *arg = ast_strdupa(a->argv[4]);
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* 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 AstDB 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/astdb.h"
|
||||
#include "asterisk/logger.h"
|
||||
|
||||
enum {
|
||||
FAMILY = 0,
|
||||
KEY = 1,
|
||||
VALUE = 2,
|
||||
};
|
||||
|
||||
/* Longest value we can support is 256 for family/key/ so, with
|
||||
* family = astdbtest and two slashes we are left with 244 bytes */
|
||||
static const char long_val[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||
|
||||
AST_TEST_DEFINE(put_get_del)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
const char *inputs[][3] = {
|
||||
{"family", "key", "value"},
|
||||
{"astdbtest", "a", "b"},
|
||||
{"astdbtest", "a", "a"},
|
||||
{"astdbtest", "b", "a"},
|
||||
{"astdbtest", "b", "b"},
|
||||
{"astdbtest", "b", "!@#$%^&*()|+-<>?"},
|
||||
{"astdbtest", long_val, "b"},
|
||||
{"astdbtest", "b", long_val},
|
||||
{"astdbtest", "!@#$%^&*()|+-<>?", "b"},
|
||||
};
|
||||
size_t x;
|
||||
char buf[sizeof(long_val)] = { 0, };
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "put_get_del";
|
||||
info->category = "/main/astdb/";
|
||||
info->summary = "ast_db_(put|get|del) unit test";
|
||||
info->description =
|
||||
"Ensures that the ast_db put, get, and del functions work";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (x = 0; x < ARRAY_LEN(inputs); x++) {
|
||||
if (ast_db_put(inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE])) {
|
||||
ast_test_status_update(test, "Failed to put %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
if (ast_db_get(inputs[x][FAMILY], inputs[x][KEY], buf, sizeof(buf))) {
|
||||
ast_test_status_update(test, "Failed to get %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]);
|
||||
res = AST_TEST_FAIL;
|
||||
} else if (strcmp(buf, inputs[x][VALUE])) {
|
||||
ast_test_status_update(test, "Failed to match key '%s/%s' value '%s' to '%s'\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE], buf);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
if (ast_db_del(inputs[x][FAMILY], inputs[x][KEY])) {
|
||||
ast_test_status_update(test, "Failed to del %s : %s\n", inputs[x][FAMILY], inputs[x][KEY]);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(gettree_deltree)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
const char *inputs[][3] = {
|
||||
#define BASE "astdbtest"
|
||||
#define SUB1 "one"
|
||||
#define SUB2 "two"
|
||||
#define FAM1 BASE "/" SUB1
|
||||
#define FAM2 BASE "/" SUB2
|
||||
{FAM1, "one", "blah"},
|
||||
{FAM1, "two", "bling"},
|
||||
{FAM1, "three", "blast"},
|
||||
{FAM2, "one", "blah"},
|
||||
{FAM2, "two", "bling"},
|
||||
{FAM2, "three", "blast"},
|
||||
};
|
||||
size_t x;
|
||||
struct ast_db_entry *dbes, *cur;
|
||||
int num_deleted;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "gettree_deltree";
|
||||
info->category = "/main/astdb/";
|
||||
info->summary = "ast_db_(gettree|deltree) unit test";
|
||||
info->description =
|
||||
"Ensures that the ast_db gettree and deltree functions work";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (x = 0; x < ARRAY_LEN(inputs); x++) {
|
||||
if (ast_db_put(inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE])) {
|
||||
ast_test_status_update(test, "Failed to put %s : %s : %s\n", inputs[x][FAMILY], inputs[x][KEY], inputs[x][VALUE]);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(dbes = ast_db_gettree(BASE, NULL))) {
|
||||
ast_test_status_update(test, "Failed to ast_db_gettree family %s\n", BASE);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
for (cur = dbes, x = 0; cur; cur = cur->next, x++) {
|
||||
int found = 0;
|
||||
size_t z;
|
||||
for (z = 0; z < ARRAY_LEN(inputs); z++) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "/%s/%s", inputs[z][FAMILY], inputs[z][KEY]);
|
||||
if (!strcmp(buf, cur->key) && !strcmp(inputs[z][VALUE], cur->data)) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ast_test_status_update(test, "inputs array has no entry for %s == %s\n", cur->key, cur->data);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (x != ARRAY_LEN(inputs)) {
|
||||
ast_test_status_update(test, "ast_db_gettree returned %zu entries when we expected %zu\n", x, ARRAY_LEN(inputs));
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
ast_db_freetree(dbes);
|
||||
|
||||
if (!(dbes = ast_db_gettree(BASE, SUB1))) {
|
||||
ast_test_status_update(test, "Failed to ast_db_gettree for %s/%s\n", BASE, SUB1);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
for (cur = dbes, x = 0; cur; cur = cur->next, x++) {
|
||||
int found = 0;
|
||||
size_t z;
|
||||
for (z = 0; z < ARRAY_LEN(inputs); z++) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "/%s/%s", inputs[z][FAMILY], inputs[z][KEY]);
|
||||
if (!strcmp(buf, cur->key) && !strcmp(inputs[z][VALUE], cur->data)) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ast_test_status_update(test, "inputs array has no entry for %s == %s\n", cur->key, cur->data);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (x != (ARRAY_LEN(inputs) / 2)) {
|
||||
ast_test_status_update(test, "ast_db_gettree returned %zu entries when we expected %zu\n", x, ARRAY_LEN(inputs) / 2);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
ast_db_freetree(dbes);
|
||||
|
||||
if ((num_deleted = ast_db_deltree(BASE, SUB2)) != ARRAY_LEN(inputs) / 2) {
|
||||
ast_test_status_update(test, "Failed to deltree %s/%s, expected %zu deletions and got %d\n", BASE, SUB2, ARRAY_LEN(inputs) / 2, num_deleted);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
if ((num_deleted = ast_db_deltree(BASE, NULL)) != ARRAY_LEN(inputs) / 2) {
|
||||
ast_test_status_update(test, "Failed to deltree %s, expected %zu deletions and got %d\n", BASE, ARRAY_LEN(inputs) / 2, num_deleted);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(put_get_del);
|
||||
AST_TEST_UNREGISTER(gettree_deltree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(put_get_del);
|
||||
AST_TEST_REGISTER(gettree_deltree);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "AstDB test module");
|
|
@ -312,7 +312,7 @@ AST_TEST_DEFINE(event_sub_test)
|
|||
.expected_count = 2,
|
||||
},
|
||||
[TEST_SUBS_CUSTOM_RAW] = {
|
||||
.expected_count = 1,
|
||||
.expected_count = 2,
|
||||
},
|
||||
[TEST_SUBS_CUSTOM_UINT] = {
|
||||
.expected_count = 1,
|
||||
|
@ -344,28 +344,13 @@ AST_TEST_DEFINE(event_sub_test)
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscription TEST_SUBS_ALL_STR:
|
||||
* - allocate normally
|
||||
* - subscribe to ALL events with a DEVICE STR IE check
|
||||
*/
|
||||
ast_test_status_update(test, "Adding TEST_SUBS_ALL_STR subscription\n");
|
||||
test_subs[TEST_SUBS_ALL_STR].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb,
|
||||
test_subs_class_type_str(TEST_SUBS_ALL_STR), &test_subs[TEST_SUBS_ALL_STR].data,
|
||||
AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
|
||||
ast_test_status_update(test, "Check that NO CUSTOM subscribers exist\n");
|
||||
sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM,
|
||||
AST_EVENT_IE_END);
|
||||
if (!test_subs[TEST_SUBS_ALL_STR].sub) {
|
||||
ast_test_status_update(test, "Failed to create TEST_SUBS_ALL_STR subscription\n");
|
||||
if (sub_res != AST_EVENT_SUB_NONE) {
|
||||
ast_test_status_update(test, "CUSTOM subscriptions should not exist! (%d)\n",
|
||||
sub_res);
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
if (strcmp(ast_event_subscriber_get_description(test_subs[TEST_SUBS_ALL_STR].sub),
|
||||
test_subs_class_type_str(TEST_SUBS_ALL_STR))) {
|
||||
ast_test_status_update(test,
|
||||
"Unexpected subscription description on TEST_SUBS_ALL_STR subscription\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -392,6 +377,39 @@ AST_TEST_DEFINE(event_sub_test)
|
|||
goto return_cleanup;
|
||||
}
|
||||
|
||||
ast_test_status_update(test, "Check that a CUSTOM subscriber exists\n");
|
||||
sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM,
|
||||
AST_EVENT_IE_END);
|
||||
if (sub_res != AST_EVENT_SUB_EXISTS) {
|
||||
ast_test_status_update(test, "A CUSTOM subscription should exist! (%d)\n",
|
||||
sub_res);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscription TEST_SUBS_ALL_STR:
|
||||
* - allocate normally
|
||||
* - subscribe to ALL events with a DEVICE STR IE check
|
||||
*/
|
||||
ast_test_status_update(test, "Adding TEST_SUBS_ALL_STR subscription\n");
|
||||
test_subs[TEST_SUBS_ALL_STR].sub = ast_event_subscribe(AST_EVENT_ALL, event_sub_cb,
|
||||
test_subs_class_type_str(TEST_SUBS_ALL_STR), &test_subs[TEST_SUBS_ALL_STR].data,
|
||||
AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "FOO/bar",
|
||||
AST_EVENT_IE_END);
|
||||
if (!test_subs[TEST_SUBS_ALL_STR].sub) {
|
||||
ast_test_status_update(test, "Failed to create TEST_SUBS_ALL_STR subscription\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
if (strcmp(ast_event_subscriber_get_description(test_subs[TEST_SUBS_ALL_STR].sub),
|
||||
test_subs_class_type_str(TEST_SUBS_ALL_STR))) {
|
||||
ast_test_status_update(test,
|
||||
"Unexpected subscription description on TEST_SUBS_ALL_STR subscription\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscription TEST_SUBS_CUSTOM_RAW:
|
||||
* - allocate normally
|
||||
|
@ -576,7 +594,7 @@ AST_TEST_DEFINE(event_sub_test)
|
|||
/*
|
||||
* Exercise the API call to check for existing subscriptions.
|
||||
*/
|
||||
ast_test_status_update(test, "Checking for subscribers to events\n");
|
||||
ast_test_status_update(test, "Checking for subscribers to specific events\n");
|
||||
|
||||
/* Check STR matching. */
|
||||
sub_res = ast_event_check_subscriber(AST_EVENT_CUSTOM,
|
||||
|
@ -678,6 +696,34 @@ AST_TEST_DEFINE(event_sub_test)
|
|||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
ast_test_status_update(test, "Special event posting test\n");
|
||||
|
||||
/*
|
||||
* Event to check if event is even posted.
|
||||
*
|
||||
* Matching subscriptions:
|
||||
* TEST_SUBS_CUSTOM_RAW
|
||||
*/
|
||||
event = ast_event_new(AST_EVENT_CUSTOM,
|
||||
AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, "Mula",
|
||||
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_RAW, "FOO/bar", sizeof("FOO/bar"),
|
||||
AST_EVENT_IE_END);
|
||||
if (!event) {
|
||||
ast_test_status_update(test, "Failed to create event\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
if (ast_event_queue(event)) {
|
||||
ast_event_destroy(event);
|
||||
event = NULL;
|
||||
ast_test_status_update(test, "Failed to queue event\n");
|
||||
res = AST_TEST_FAIL;
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
ast_test_status_update(test, "Sleeping a few seconds to allow event propagation...\n");
|
||||
sleep(3);
|
||||
|
||||
/*
|
||||
* Subscription TEST_SUBS_CUSTOM_ANY:
|
||||
* - allocate normally
|
||||
|
|
Reference in New Issue