diff --git a/apps/app_dial.c b/apps/app_dial.c index a071f5e0f47d1d6057f06603e621b9b0792f123c..20ea83e571586a2109b5cb5b13e2c2ab6cfb4d75 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -54,6 +54,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"; @@ -326,7 +327,6 @@ struct chanlist { struct chanlist *next; struct ast_channel *chan; uint64_t flags; - int forwards; }; @@ -347,8 +347,6 @@ static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, } } -#define AST_MAX_FORWARDS 8 - #define AST_MAX_WATCHERS 256 /* @@ -480,28 +478,22 @@ static void do_forward(struct chanlist *o, tech = "Local"; } /* Before processing channel, go ahead and check for forwarding */ - o->forwards++; - if (o->forwards < AST_MAX_FORWARDS) { - ast_verb(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_flag64(peerflags, OPT_IGNORE_FORWARDING)) { - ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff); - c = o->chan = NULL; - cause = AST_CAUSE_BUSY; - } else { - /* Setup parameters */ - c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause); - if (c) { - 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 { - ast_verb(3, "Too many forwards from %s\n", c->name); - cause = AST_CAUSE_CONGESTION; + ast_verb(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_flag64(peerflags, OPT_IGNORE_FORWARDING)) { + ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff); c = o->chan = NULL; + cause = AST_CAUSE_BUSY; + } else { + /* Setup parameters */ + c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause); + if (c) { + 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_flag64(o, DIAL_STILLGOING); @@ -1252,6 +1244,8 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags ); struct ast_flags64 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"); @@ -1333,7 +1327,13 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags struct ast_channel *tc; /* channel for this destination */ /* 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; @@ -1353,6 +1353,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); + tc = ast_request(tech, chan->nativeformats, numsubst, &cause); if (!tc) { /* If we can't, just go on to the next call */ @@ -1365,50 +1409,6 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags continue; } pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst); - if (!ast_strlen_zero(tc->call_forward)) { - char tmpchan[256]; - char *stuff; - char *tech; - ast_copy_string(tmpchan, tc->call_forward, sizeof(tmpchan)); - if ((stuff = strchr(tmpchan, '/'))) { - *stuff++ = '\0'; - tech = tmpchan; - } else { - snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tc->call_forward, tc->context); - stuff = tmpchan; - tech = "Local"; - } - tmp->forwards++; - if (tmp->forwards < AST_MAX_FORWARDS) { - ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", - chan->name, tech, stuff, tc->name); - ast_hangup(tc); - /* If we have been told to ignore forwards, just set this channel to null - * and continue processing extensions normally */ - if (ast_test_flag64(&opts, OPT_IGNORE_FORWARDING)) { - tc = NULL; - cause = AST_CAUSE_BUSY; - ast_verb(3, "Forwarding %s to '%s/%s' prevented.\n", - chan->name, tech, stuff); - } else { - tc = ast_request(tech, chan->nativeformats, stuff, &cause); - } - if (!tc) - 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, tc); - } else { - ast_verb(3, "Too many forwards from %s\n", tc->name); - ast_hangup(tc); - tc = NULL; - cause = AST_CAUSE_CONGESTION; - } - if (!tc) { - handle_cause(cause, &num); - ast_free(tmp); - continue; - } - } /* Setup outgoing SDP to match incoming one */ ast_rtp_make_compatible(tc, chan, !outgoing && !rest); @@ -1498,6 +1498,10 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags if (!outgoing) { strcpy(pa.status, "CHANUNAVAIL"); + if(fulldial == num_dialed) { + res = -1; + goto out; + } } else { /* Our status will at least be NOANSWER */ strcpy(pa.status, "NOANSWER"); @@ -1520,7 +1524,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, &pa, &num, &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 a6cf340be314ce578fe3297c04c7bb98d4bfb332..a1219e45f03de533b202d969d1cfc64da317e511 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -87,6 +87,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/event.h" #include "asterisk/astobj2.h" #include "asterisk/strings.h" +#include "asterisk/global_datastores.h" enum { QUEUE_STRATEGY_RINGALL = 0, @@ -2295,6 +2296,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) ast_free(o->chan->cid.cid_num); o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); @@ -2733,6 +2735,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)); tmpid[0] = 0; @@ -2802,7 +2805,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); ao2_unlock(qe->parent); @@ -2810,6 +2815,49 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce ao2_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; @@ -2842,6 +2890,8 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce if (use_weight) ao2_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); ao2_lock(qe->parent); if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { store_next_rr(qe, outgoing); diff --git a/channels/chan_local.c b/channels/chan_local.c index 99b23cdeee8f0ce78a40477c376f1ab07074346f..3f4cfb5fb48df46da02d54a2b8de978a093cbb6e 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -473,6 +473,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 8a20d3b91bd921ee8074f282dc408690a2084eb7..db760410c456c1640dbfea3e32ff4b4d13612dab 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -649,6 +649,7 @@ int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel * * \retval 0 success * \retval non-zero failure */ + 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 0000000000000000000000000000000000000000..72edabac5f089e805190a24b2cfcc5483e584733 --- /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 b9894e7ab4d6a925ce0b5bb527ecf8354db53a58..ca3d8dabd5013ba708dfa36e525ef639130fea8a 100644 --- a/main/Makefile +++ b/main/Makefile @@ -27,7 +27,7 @@ OBJS= io.o sched.o logger.o frame.o loader.o config.o channel.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 event.o adsistub.o audiohook.o \ - astobj2.o hashtab.o + astobj2.o hashtab.o global_datstores.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/global_datastores.c b/main/global_datastores.c new file mode 100644 index 0000000000000000000000000000000000000000..340e71de19f23f52727b5cf185fe49359b0f1e05 --- /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, +};