aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2011-06-17 08:11:11 +0200
committerPatrick McHardy <kaber@trash.net>2011-06-17 08:11:11 +0200
commit9364aaccb699c6d19ac2cbe760c208b34ba7838a (patch)
tree28c0aef25a69f14117caf64e09fc9597d2c915ea
parent84c94e92c153057470194aadf7ae22a2569f1bd4 (diff)
parent62b90dd0a4ea809bdb11bc27288b6acf717edcd8 (diff)
Merge 192.168.0.100:/repos/git/asterisk
-rw-r--r--CHANGES7
-rw-r--r--Makefile1
-rw-r--r--apps/app_confbridge.c119
-rw-r--r--apps/app_directed_pickup.c24
-rw-r--r--apps/app_queue.c4
-rw-r--r--apps/confbridge/conf_config_parser.c12
-rw-r--r--apps/confbridge/include/confbridge.h1
-rw-r--r--channels/chan_local.c22
-rw-r--r--channels/chan_sip.c100
-rw-r--r--channels/chan_skinny.c160
-rw-r--r--channels/sip/include/sip.h3
-rw-r--r--configs/sip.conf.sample5
-rw-r--r--include/asterisk/astdb.h2
-rw-r--r--include/asterisk/channel.h4
-rw-r--r--include/asterisk/features.h20
-rw-r--r--include/asterisk/rtp_engine.h50
-rw-r--r--include/asterisk/utils.h2
-rw-r--r--main/channel.c20
-rw-r--r--main/db.c299
-rw-r--r--main/dnsmgr.c8
-rw-r--r--main/event.c29
-rw-r--r--main/features.c99
-rw-r--r--main/manager.c78
-rw-r--r--main/netsock2.c19
-rw-r--r--main/rtp_engine.c21
-rw-r--r--res/res_agi.c2
-rw-r--r--res/res_config_pgsql.c44
-rw-r--r--res/res_rtp_asterisk.c45
-rw-r--r--tests/test_db.c222
-rw-r--r--tests/test_event.c90
30 files changed, 1101 insertions, 411 deletions
diff --git a/CHANGES b/CHANGES
index a01077970..1dfadcbf7 100644
--- a/CHANGES
+++ b/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
------------------
diff --git a/Makefile b/Makefile
index b0d016709..0615d60c8 100644
--- a/Makefile
+++ b/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)/
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 7cede133e..177ee3717 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -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;
}
diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c
index 38f24effc..63a7621ff 100644
--- a/apps/app_directed_pickup.c
+++ b/apps/app_directed_pickup.c
@@ -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;
}
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 146cf63d6..be929c1dc 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -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
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index dbec32bba..3d1f31326 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -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;
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 4b6c0615e..de467b5f7 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -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 */
};
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 0ceb432b0..3c16fc51a 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -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;
}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index bcb4ea73a..4c0e2a66c 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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")) {
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index b90ba7eba..ea7d16c9e 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -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);
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 0eb8be350..e8dba3067 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -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 */
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 49277d64f..21235a6ef 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -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
diff --git a/include/asterisk/astdb.h b/include/asterisk/astdb.h
index 4c1fa5890..cfbebbc30 100644
--- a/include/asterisk/astdb.h
+++ b/include/asterisk/astdb.h
@@ -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);
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 5798e92bb..031c6f7e9 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -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() */
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index 4ea941383..7e749f2e0 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -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.
*/
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index f8bf74931..e04303ca6 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -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;
};
@@ -1713,6 +1715,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
*
* \param instance The RTP instance
@@ -1751,6 +1771,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
*
* \param instance The 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);
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 823af26c4..44eeb5f8e 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -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 */
diff --git a/main/channel.c b/main/channel.c
index 9c9d6ca5a..57629dbc5 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -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 {
diff --git a/main/db.c b/main/db.c
index ce8a9069c..a0efc8c05 100644
--- a/main/db.c
+++ b/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;
}
-int ast_db_deltree(const char *family, const char *keytree)
+static const char *dbt_data2str(DBT *dbt)
{
- char prefix[256];
- DBT key, data;
- char *keys;
- int res;
- int pass;
+ 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[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;
- }
- 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++;
- }
+ return CLI_SUCCESS;
}
- 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;
- }
- 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++;
- }
+ return CLI_SUCCESS;
}
- 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;
- }
- 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;
- }
+ return NULL;
}
- 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)) {
diff --git a/main/dnsmgr.c b/main/dnsmgr.c
index 23e1ab6ef..999bd92f4 100644
--- a/main/dnsmgr.c
+++ b/main/dnsmgr.c
@@ -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 */
diff --git a/main/event.c b/main/event.c
index 246821eac..fad8e66ee 100644
--- a/main/event.c
+++ b/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");
diff --git a/main/features.c b/main/features.c
index 81421f17b..728118188 100644
--- a/main/features.c
+++ b/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);
+ res = 0;
+
+pickup_failed:
ast_channel_lock(target);
+ if (!ast_channel_datastore_remove(target, ds_pickup)) {
+ ast_datastore_free(ds_pickup);
+ }
- return 0;
+ return res;
}
static char *app_bridge = "Bridge";
diff --git a/main/manager.c b/main/manager.c
index ea2e2a975..c4d1e1101 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -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");
diff --git a/main/netsock2.c b/main/netsock2.c
index 25f15a2fc..d6561fba2 100644
--- a/main/netsock2.c
+++ b/main/netsock2.c
@@ -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;
}
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 3c5ef8b36..a1c460578 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -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;
diff --git a/res/res_agi.c b/res/res_agi.c
index 53aa6804f..b57c498df 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -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" />
diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c
index caafb52df..bbc683a47 100644
--- a/res/res_config_pgsql.c
+++ b/res/res_config_pgsql.c
@@ -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);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index dd71342d1..60f7edacf 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -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]);
diff --git a/tests/test_db.c b/tests/test_db.c
new file mode 100644
index 000000000..105a6ab1e
--- /dev/null
+++ b/tests/test_db.c
@@ -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");
diff --git a/tests/test_event.c b/tests/test_event.c
index 5f0c1003c..e6f345133 100644
--- a/tests/test_event.c
+++ b/tests/test_event.c
@@ -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");
- 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");
+ 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;
}
/*
@@ -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