diff options
-rw-r--r-- | apps/app_dial.c | 148 | ||||
-rw-r--r-- | apps/app_queue.c | 52 | ||||
-rw-r--r-- | channels/chan_local.c | 1 | ||||
-rw-r--r-- | include/asterisk/channel.h | 6 | ||||
-rw-r--r-- | include/asterisk/global_datastores.h | 36 | ||||
-rw-r--r-- | main/Makefile | 2 | ||||
-rw-r--r-- | main/channel.c | 17 | ||||
-rw-r--r-- | main/global_datastores.c | 78 |
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, +}; |