aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_dial.c148
-rw-r--r--apps/app_queue.c52
-rw-r--r--channels/chan_local.c1
-rw-r--r--include/asterisk/channel.h6
-rw-r--r--include/asterisk/global_datastores.h36
-rw-r--r--main/Makefile2
-rw-r--r--main/channel.c17
-rw-r--r--main/global_datastores.c78
8 files changed, 267 insertions, 73 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 73d2d4c15..56a4336af 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h"
#include "asterisk/privacy.h"
#include "asterisk/stringfields.h"
+#include "asterisk/global_datastores.h"
static char *app = "Dial";
@@ -303,7 +304,6 @@ AST_APP_OPTIONS(dial_exec_options, {
struct dial_localuser {
struct ast_channel *chan;
unsigned int flags;
- int forwards;
struct dial_localuser *next;
};
@@ -322,8 +322,6 @@ static void hanguptree(struct dial_localuser *outgoing, struct ast_channel *exce
}
}
-#define AST_MAX_FORWARDS 8
-
#define AST_MAX_WATCHERS 256
#define HANDLE_CAUSE(cause, chan) do { \
@@ -488,30 +486,23 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
tech = "Local";
}
/* Before processing channel, go ahead and check for forwarding */
- o->forwards++;
- if (o->forwards < AST_MAX_FORWARDS) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
- /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
- if (ast_test_flag(peerflags, OPT_IGNORE_FORWARDING)) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
- c = o->chan = NULL;
- cause = AST_CAUSE_BUSY;
- } else {
- /* Setup parameters */
- if ((c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause))) {
- if (single)
- ast_channel_make_compatible(o->chan, in);
- ast_channel_inherit_variables(in, o->chan);
- } else
- ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
- }
- } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
+ /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
+ if (ast_test_flag(peerflags, OPT_IGNORE_FORWARDING)) {
if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", c->name);
- cause = AST_CAUSE_CONGESTION;
+ ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
c = o->chan = NULL;
+ cause = AST_CAUSE_BUSY;
+ } else {
+ /* Setup parameters */
+ if ((c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause))) {
+ if (single)
+ ast_channel_make_compatible(o->chan, in);
+ ast_channel_inherit_variables(in, o->chan);
+ ast_channel_datastore_inherit(in, o->chan);
+ } else
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
}
if (!c) {
ast_clear_flag(o, DIAL_STILLGOING);
@@ -847,6 +838,8 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
);
struct ast_flags opts = { 0, };
char *opt_args[OPT_ARG_ARRAY_SIZE];
+ struct ast_datastore *datastore;
+ int fulldial = 0, num_dialed = 0;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
@@ -1107,7 +1100,13 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
struct dial_localuser *tmp;
/* Get a technology/[device:]number pair */
char *number = cur;
+ char *interface = ast_strdupa(number);
char *tech = strsep(&number, "/");
+ /* find if we already dialed this interface */
+ int dialed = 0;
+ struct ast_dialed_interface *di;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
+ num_dialed++;
if (!number) {
ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
goto out;
@@ -1125,6 +1124,50 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
}
ast_copy_string(numsubst, number, sizeof(numsubst));
/* Request the peer */
+ if (!(datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) {
+ if(!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n");
+ free(tmp);
+ goto out;
+ }
+ else {
+ datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+ if((dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
+ datastore->data = dialed_interfaces;
+ AST_LIST_HEAD_INIT(dialed_interfaces);
+ ast_channel_datastore_add(chan, datastore);
+ } else {
+ free(tmp);
+ goto out;
+ }
+ }
+ } else
+ dialed_interfaces = datastore->data;
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
+ /* XXX case sensitive??? */
+ if(!strcasecmp(di->interface, interface)) {
+ dialed = 1;
+ break;
+ }
+ }
+ if(!dialed && strcasecmp(tech, "Local")) {
+ if(!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
+ AST_LIST_UNLOCK(dialed_interfaces);
+ free(tmp);
+ goto out;
+ }
+ strcpy(di->interface, interface);
+ AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
+ } else {
+ AST_LIST_UNLOCK(dialed_interfaces);
+ ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n", di->interface);
+ fulldial++;
+ free(tmp);
+ continue;
+ }
+ AST_LIST_UNLOCK(dialed_interfaces);
+
tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
if (!tmp->chan) {
/* If we can't, just go on to the next call */
@@ -1135,51 +1178,8 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
free(tmp);
continue;
}
- pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
- if (!ast_strlen_zero(tmp->chan->call_forward)) {
- char tmpchan[256];
- char *stuff;
- char *tech;
- ast_copy_string(tmpchan, tmp->chan->call_forward, sizeof(tmpchan));
- if ((stuff = strchr(tmpchan, '/'))) {
- *stuff++ = '\0';
- tech = tmpchan;
- } else {
- snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
- stuff = tmpchan;
- tech = "Local";
- }
- tmp->forwards++;
- if (tmp->forwards < AST_MAX_FORWARDS) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
- ast_hangup(tmp->chan);
- /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
- if (ast_test_flag(&opts, OPT_IGNORE_FORWARDING)) {
- tmp->chan = NULL;
- cause = AST_CAUSE_BUSY;
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", chan->name, tech, stuff);
- } else {
- tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
- }
- if (!tmp->chan)
- ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
- else
- ast_channel_inherit_variables(chan, tmp->chan);
- } else {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tmp->chan->name);
- ast_hangup(tmp->chan);
- tmp->chan = NULL;
- cause = AST_CAUSE_CONGESTION;
- }
- if (!tmp->chan) {
- HANDLE_CAUSE(cause, chan);
- free(tmp);
- continue;
- }
- }
+
+ pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
/* Setup outgoing SDP to match incoming one */
ast_rtp_make_compatible(tmp->chan, chan, !outgoing && !rest);
@@ -1284,6 +1284,10 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
if (!outgoing) {
strcpy(status, "CHANUNAVAIL");
+ if(fulldial == num_dialed) {
+ res = -1;
+ goto out;
+ }
} else {
/* Our status will at least be NOANSWER */
strcpy(status, "NOANSWER");
@@ -1306,7 +1310,9 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
time(&start_time);
peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result);
-
+
+ ast_channel_datastore_remove(chan, datastore);
+ ast_channel_datastore_free(datastore);
if (!peer) {
if (result) {
res = result;
diff --git a/apps/app_queue.c b/apps/app_queue.c
index db4a574ba..842998d9e 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -93,6 +93,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
+#include "asterisk/global_datastores.h"
enum {
QUEUE_STRATEGY_RINGALL = 0,
@@ -2133,6 +2134,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
numnochan++;
} else {
ast_channel_inherit_variables(in, o->chan);
+ ast_channel_datastore_inherit(in, o->chan);
if (o->chan->cid.cid_num)
free(o->chan->cid.cid_num);
o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
@@ -2500,6 +2502,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
int forwardsallowed = 1;
int callcompletedinsl;
struct ao2_iterator memi;
+ struct ast_datastore *datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
memset(&bridge_config, 0, sizeof(bridge_config));
time(&now);
@@ -2555,7 +2558,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
memi = ao2_iterator_init(qe->parent->members, 0);
while ((cur = ao2_iterator_next(&memi))) {
struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
-
+ struct ast_dialed_interface *di;
+ int dialed = 0;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
if (!tmp) {
ao2_ref(cur, -1);
ast_mutex_unlock(&qe->parent->lock);
@@ -2563,6 +2568,49 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
AST_LIST_UNLOCK(&queues);
goto out;
}
+ if (!datastore) {
+ if(!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
+ ao2_ref(cur, -1);
+ ast_mutex_unlock(&qe->parent->lock);
+ if(use_weight)
+ AST_LIST_UNLOCK(&queues);
+ free(tmp);
+ goto out;
+ }
+ datastore->inheritance = DATASTORE_INHERIT_FOREVER;
+ dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces));
+ datastore->data = dialed_interfaces;
+ AST_LIST_HEAD_INIT(dialed_interfaces);
+ ast_channel_datastore_add(qe->chan, datastore);
+ } else
+ dialed_interfaces = datastore->data;
+ AST_LIST_LOCK(dialed_interfaces);
+ AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
+ /* XXX case sensitive ?? */
+ if(!strcasecmp(cur->interface, di->interface)) {
+ dialed = 1;
+ break;
+ }
+ }
+ if (!dialed && strncasecmp(cur->interface, "Local/", 6)) {
+ if(!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
+ ao2_ref(cur, -1);
+ AST_LIST_UNLOCK(dialed_interfaces);
+ ast_mutex_unlock(&qe->parent->lock);
+ if(use_weight)
+ AST_LIST_UNLOCK(&queues);
+ free(tmp);
+ goto out;
+ }
+ strcpy(di->interface, cur->interface);
+ AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
+ } else {
+ AST_LIST_UNLOCK(dialed_interfaces);
+ ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", di->interface);
+ free(tmp);
+ continue;
+ }
+ AST_LIST_UNLOCK(dialed_interfaces);
tmp->stillgoing = -1;
tmp->member = cur;
tmp->oldstatus = cur->status;
@@ -2593,6 +2641,8 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (use_weight)
AST_LIST_UNLOCK(&queues);
lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
+ ast_channel_datastore_remove(qe->chan, datastore);
+ ast_channel_datastore_free(datastore);
ast_mutex_lock(&qe->parent->lock);
if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
store_next(qe, outgoing);
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 1144278f0..6985d8f75 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -467,6 +467,7 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout)
AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
}
}
+ ast_channel_datastore_inherit(p->owner, p->chan);
/* Start switch on sub channel */
if (!(res = ast_pbx_start(p->chan)))
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 7ee4bef11..d6c9063f9 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -118,6 +118,7 @@ extern "C" {
#include "asterisk/stringfields.h"
#include "asterisk/compiler.h"
+#define DATASTORE_INHERIT_FOREVER INT_MAX
#define AST_MAX_FDS 8
/*
@@ -148,6 +149,7 @@ struct ast_generator {
/*! \brief Structure for a data store type */
struct ast_datastore_info {
const char *type; /*!< Type of data store */
+ void *(*duplicate)(void *data); /*!< Duplicate item data (used for inheritance) */
void (*destroy)(void *data); /*!< Destroy function */
};
@@ -156,6 +158,7 @@ struct ast_datastore {
char *uid; /*!< Unique data store identifier */
void *data; /*!< Contained data */
const struct ast_datastore_info *info; /*!< Data store type information */
+ unsigned int inheritance; /*!Number of levels this item will continue to be inherited */
AST_LIST_ENTRY(ast_datastore) entry; /*!< Used for easy linking */
};
@@ -575,6 +578,9 @@ struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_inf
/*! \brief Free a channel datastore structure */
int ast_channel_datastore_free(struct ast_datastore *datastore);
+/*! \brief Inherit datastores from a parent to a child. */
+int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to);
+
/*! \brief Add a datastore to a channel */
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore);
diff --git a/include/asterisk/global_datastores.h b/include/asterisk/global_datastores.h
new file mode 100644
index 000000000..72edabac5
--- /dev/null
+++ b/include/asterisk/global_datastores.h
@@ -0,0 +1,36 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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 globally accessible channel datastores
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#ifndef _ASTERISK_GLOBAL_DATASTORE_H
+#define _ASTERISK_GLOBAL_DATASTORE_H
+
+#include "asterisk/channel.h"
+
+extern const struct ast_datastore_info dialed_interface_info;
+
+struct ast_dialed_interface {
+ AST_LIST_ENTRY(ast_dialed_interface) list;
+ char interface[1];
+};
+
+#endif
diff --git a/main/Makefile b/main/Makefile
index 10353da39..4f405b538 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -26,7 +26,7 @@ OBJS= io.o sched.o logger.o frame.o loader.o config.o channel.o \
utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
- strcompat.o threadstorage.o dial.o astobj2.o
+ strcompat.o threadstorage.o dial.o astobj2.o global_datastores.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static
diff --git a/main/channel.c b/main/channel.c
index 25b4900db..5d1662580 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1317,6 +1317,23 @@ int ast_channel_datastore_free(struct ast_datastore *datastore)
return res;
}
+int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
+{
+ struct ast_datastore *datastore = NULL, *datastore2;
+
+ AST_LIST_TRAVERSE(&from->datastores, datastore, entry) {
+ if (datastore->inheritance > 0) {
+ datastore2 = ast_channel_datastore_alloc(datastore->info, datastore->uid);
+ if (datastore2) {
+ datastore2->data = datastore->info->duplicate(datastore->data);
+ datastore2->inheritance = datastore->inheritance == DATASTORE_INHERIT_FOREVER ? DATASTORE_INHERIT_FOREVER : datastore->inheritance - 1;
+ AST_LIST_INSERT_TAIL(&to->datastores, datastore2, entry);
+ }
+ }
+ }
+ return 0;
+}
+
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
{
int res = 0;
diff --git a/main/global_datastores.c b/main/global_datastores.c
new file mode 100644
index 000000000..340e71de1
--- /dev/null
+++ b/main/global_datastores.c
@@ -0,0 +1,78 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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 globally-accessible datastore information and callbacks
+ *
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk/global_datastores.h"
+#include "asterisk/linkedlists.h"
+
+static void dialed_interface_destroy(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interface_list = data;
+
+ if (!dialed_interface_list)
+ return;
+
+ AST_LIST_LOCK(dialed_interface_list);
+ while ((di = AST_LIST_REMOVE_HEAD(dialed_interface_list, list)))
+ ast_free(di);
+ AST_LIST_UNLOCK(dialed_interface_list);
+
+ AST_LIST_HEAD_DESTROY(dialed_interface_list);
+ ast_free(dialed_interface_list);
+}
+
+static void *dialed_interface_duplicate(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *old_list;
+ AST_LIST_HEAD(, ast_dialed_interface) *new_list = NULL;
+
+ if(!(old_list = data))
+ return NULL;
+
+ if(!(new_list = ast_calloc(1, sizeof(*new_list))))
+ return NULL;
+
+ AST_LIST_HEAD_INIT(new_list);
+ AST_LIST_LOCK(old_list);
+ AST_LIST_TRAVERSE(old_list, di, list) {
+ struct ast_dialed_interface *di2 = ast_calloc(1, sizeof(*di2) + strlen(di->interface));
+ if(!di2) {
+ AST_LIST_UNLOCK(old_list);
+ return NULL;
+ }
+ strcpy(di2->interface, di->interface);
+ AST_LIST_INSERT_TAIL(new_list, di2, list);
+ }
+ AST_LIST_UNLOCK(old_list);
+
+ return new_list;
+}
+
+const struct ast_datastore_info dialed_interface_info = {
+ .type ="dialed-interface",
+ .destroy = dialed_interface_destroy,
+ .duplicate = dialed_interface_duplicate,
+};