aboutsummaryrefslogtreecommitdiffstats
path: root/main
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 /main
parent84c94e92c153057470194aadf7ae22a2569f1bd4 (diff)
parent62b90dd0a4ea809bdb11bc27288b6acf717edcd8 (diff)
Merge 192.168.0.100:/repos/git/asterisk
Diffstat (limited to 'main')
-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
8 files changed, 340 insertions, 233 deletions
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;