diff --git a/apps/app_dial.c b/apps/app_dial.c index 0390cfe7f9..895d4b8833 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -58,7 +58,6 @@ ASTERISK_REGISTER_FILE() #include "asterisk/manager.h" #include "asterisk/privacy.h" #include "asterisk/stringfields.h" -#include "asterisk/global_datastores.h" #include "asterisk/dsp.h" #include "asterisk/aoc.h" #include "asterisk/ccss.h" @@ -68,6 +67,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/stasis_channels.h" #include "asterisk/bridge_after.h" #include "asterisk/features_config.h" +#include "asterisk/max_forwards.h" /*** DOCUMENTATION @@ -881,6 +881,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num, ast_channel_lock_both(in, o->chan); ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); + ast_max_forwards_decrement(o->chan); ast_channel_unlock(in); ast_channel_unlock(o->chan); /* When a call is forwarded, we don't want to track new interfaces @@ -2074,7 +2075,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ); struct ast_flags64 opts = { 0, }; char *opt_args[OPT_ARG_ARRAY_SIZE]; - struct ast_datastore *datastore = NULL; int fulldial = 0, num_dialed = 0; int ignore_cc = 0; char device_name[AST_CHANNEL_NAME]; @@ -2101,6 +2101,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast * \note This will not have any malloced strings so do not free it. */ struct ast_party_caller caller; + int max_forwards; /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */ ast_channel_lock(chan); @@ -2111,8 +2112,16 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", ""); pbx_builtin_setvar_helper(chan, "DIALEDTIME", ""); ast_channel_stage_snapshot_done(chan); + max_forwards = ast_max_forwards_get(chan); ast_channel_unlock(chan); + if (max_forwards <= 0) { + ast_log(LOG_WARNING, "Cannot place outbound call from channel '%s'. Max forwards exceeded\n", + ast_channel_name(chan)); + pbx_builtin_setvar_helper(chan, "DIALSTATUS", "BUSY"); + return -1; + } + if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n"); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); @@ -2314,9 +2323,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast char *tech = strsep(&number, "/"); size_t tech_len; size_t number_len; - /* find if we already dialed this interface */ - struct ast_dialed_interface *di; - AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces; num_dialed++; if (ast_strlen_zero(number)) { @@ -2360,7 +2366,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast /* Request the peer */ ast_channel_lock(chan); - datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* * Seed the chanlist's connected line information with previously * acquired connected line info from the incoming channel. The @@ -2370,61 +2375,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan)); ast_channel_unlock(chan); - if (datastore) - dialed_interfaces = datastore->data; - else { - if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for dialed interfaces. Aborting!\n"); - chanlist_free(tmp); - goto out; - } - datastore->inheritance = DATASTORE_INHERIT_FOREVER; - - if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { - ast_datastore_free(datastore); - chanlist_free(tmp); - goto out; - } - - datastore->data = dialed_interfaces; - AST_LIST_HEAD_INIT(dialed_interfaces); - - ast_channel_lock(chan); - ast_channel_datastore_add(chan, datastore); - ast_channel_unlock(chan); - } - - AST_LIST_LOCK(dialed_interfaces); - AST_LIST_TRAVERSE(dialed_interfaces, di, list) { - if (!strcasecmp(di->interface, tmp->interface)) { - ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n", - di->interface); - break; - } - } - AST_LIST_UNLOCK(dialed_interfaces); - if (di) { - fulldial++; - chanlist_free(tmp); - continue; - } - - /* It is always ok to dial a Local interface. We only keep track of - * which "real" interfaces have been dialed. The Local channel will - * inherit this list so that if it ends up dialing a real interface, - * it won't call one that has already been called. */ - if (strcasecmp(tmp->tech, "Local")) { - if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) { - chanlist_free(tmp); - goto out; - } - strcpy(di->interface, tmp->interface); - - AST_LIST_LOCK(dialed_interfaces); - AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); - AST_LIST_UNLOCK(dialed_interfaces); - } - tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), NULL, chan, tmp->number, &cause); if (!tc) { /* If we can't, just go on to the next call */ @@ -2465,6 +2415,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(chan, tc); ast_channel_datastore_inherit(chan, tc); + ast_max_forwards_decrement(tc); ast_channel_appl_set(tc, "AppDial"); ast_channel_data_set(tc, "(Outgoing Line)"); @@ -2680,18 +2631,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result, dtmf_progress, ignore_cc, &forced_clid, &stored_clid); - /* The ast_channel_datastore_remove() function could fail here if the - * datastore was moved to another channel during a masquerade. If this is - * the case, don't free the datastore here because later, when the channel - * to which the datastore was moved hangs up, it will attempt to free this - * datastore again, causing a crash - */ - ast_channel_lock(chan); - datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL); /* make sure we weren't cleaned up already */ - if (datastore && !ast_channel_datastore_remove(chan, datastore)) { - ast_datastore_free(datastore); - } - ast_channel_unlock(chan); if (!peer) { if (result) { res = result; diff --git a/apps/app_followme.c b/apps/app_followme.c index 4a2e569df0..5fd5d15ba5 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -64,6 +64,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/dsp.h" #include "asterisk/app.h" #include "asterisk/stasis_channels.h" +#include "asterisk/max_forwards.h" /*** DOCUMENTATION @@ -1069,6 +1070,7 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller)); ast_channel_inherit_variables(caller, outbound); ast_channel_datastore_inherit(caller, outbound); + ast_max_forwards_decrement(outbound); ast_channel_language_set(outbound, ast_channel_language(caller)); ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER); ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller)); @@ -1304,12 +1306,23 @@ static int app_exec(struct ast_channel *chan, const char *data) AST_APP_ARG(options); ); char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE]; + int max_forwards; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app); return -1; } + ast_channel_lock(chan); + max_forwards = ast_max_forwards_get(chan); + ast_channel_unlock(chan); + + if (max_forwards <= 0) { + ast_log(LOG_WARNING, "Unable to execute FollowMe on channel %s. Max forwards exceeded\n", + ast_channel_name(chan)); + return -1; + } + argstr = ast_strdupa((char *) data); AST_STANDARD_APP_ARGS(args, argstr); diff --git a/apps/app_queue.c b/apps/app_queue.c index a82632d8e5..0b8204c33b 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -98,7 +98,6 @@ ASTERISK_REGISTER_FILE() #include "asterisk/stringfields.h" #include "asterisk/astobj2.h" #include "asterisk/strings.h" -#include "asterisk/global_datastores.h" #include "asterisk/taskprocessor.h" #include "asterisk/aoc.h" #include "asterisk/callerid.h" @@ -113,6 +112,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/mixmonitor.h" #include "asterisk/core_unreal.h" #include "asterisk/bridge_basic.h" +#include "asterisk/max_forwards.h" /*! * \par Please read before modifying this file. @@ -4301,6 +4301,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies /* Inherit specially named variables from parent channel */ ast_channel_inherit_variables(qe->chan, tmp->chan); ast_channel_datastore_inherit(qe->chan, tmp->chan); + ast_max_forwards_decrement(tmp->chan); /* Presense of ADSI CPE on outgoing channel follows ours */ ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan)); @@ -4794,6 +4795,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte ast_channel_lock_both(o->chan, in); ast_channel_inherit_variables(in, o->chan); ast_channel_datastore_inherit(in, o->chan); + ast_max_forwards_decrement(o->chan); if (o->pending_connected_update) { /* @@ -6275,10 +6277,7 @@ static void setup_mixmonitor(struct queue_ent *qe, const char *filename) * * Here is the process of this function * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue() - * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this - * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this - * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also - * during each iteration, we call calc_metric to determine which members should be rung when. + * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. * 3. Call ring_one to place a call to the appropriate member(s) * 4. Call wait_for_answer to wait for an answer. If no one answers, return. * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered. @@ -6331,13 +6330,8 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a int block_connected_line = 0; int callcompletedinsl; struct ao2_iterator memi; - struct ast_datastore *datastore; struct queue_end_bridge *queue_end_bridge = NULL; - ast_channel_lock(qe->chan); - datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); - ast_channel_unlock(qe->chan); - memset(&bridge_config, 0, sizeof(bridge_config)); tmpid[0] = 0; time(&now); @@ -6424,73 +6418,12 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a 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; - AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces; if (!tmp) { ao2_ref(cur, -1); ao2_iterator_destroy(&memi); ao2_unlock(qe->parent); goto out; } - if (!datastore) { - if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { - callattempt_free(tmp); - ao2_ref(cur, -1); - ao2_iterator_destroy(&memi); - ao2_unlock(qe->parent); - goto out; - } - datastore->inheritance = DATASTORE_INHERIT_FOREVER; - if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { - callattempt_free(tmp); - ao2_ref(cur, -1); - ao2_iterator_destroy(&memi); - ao2_unlock(qe->parent); - goto out; - } - datastore->data = dialed_interfaces; - AST_LIST_HEAD_INIT(dialed_interfaces); - - ast_channel_lock(qe->chan); - ast_channel_datastore_add(qe->chan, datastore); - ast_channel_unlock(qe->chan); - } else - dialed_interfaces = datastore->data; - - AST_LIST_LOCK(dialed_interfaces); - AST_LIST_TRAVERSE(dialed_interfaces, di, list) { - if (!strcasecmp(cur->interface, di->interface)) { - ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", - di->interface); - break; - } - } - AST_LIST_UNLOCK(dialed_interfaces); - - if (di) { - callattempt_free(tmp); - ao2_ref(cur, -1); - continue; - } - - /* It is always ok to dial a Local interface. We only keep track of - * which "real" interfaces have been dialed. The Local channel will - * inherit this list so that if it ends up dialing a real interface, - * it won't call one that has already been called. */ - if (strncasecmp(cur->interface, "Local/", 6)) { - if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { - callattempt_free(tmp); - ao2_ref(cur, -1); - ao2_iterator_destroy(&memi); - ao2_unlock(qe->parent); - goto out; - } - strcpy(di->interface, cur->interface); - - AST_LIST_LOCK(dialed_interfaces); - AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); - AST_LIST_UNLOCK(dialed_interfaces); - } /* * Seed the callattempt's connected line information with previously @@ -6549,16 +6482,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, ringing); - /* The ast_channel_datastore_remove() function could fail here if the - * datastore was moved to another channel during a masquerade. If this is - * the case, don't free the datastore here because later, when the channel - * to which the datastore was moved hangs up, it will attempt to free this - * datastore again, causing a crash - */ - ast_channel_lock(qe->chan); - if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { - ast_datastore_free(datastore); - } + ast_channel_unlock(qe->chan); ao2_lock(qe->parent); if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) { @@ -7750,12 +7674,22 @@ static int queue_exec(struct ast_channel *chan, const char *data) struct queue_ent qe = { 0 }; struct ast_flags opts = { 0, }; char *opt_args[OPT_ARG_ARRAY_SIZE]; + int max_forwards; if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n"); return -1; } + ast_channel_lock(chan); + max_forwards = ast_max_forwards_get(chan); + ast_channel_unlock(chan); + + if (max_forwards <= 0) { + ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan)); + return -1; + } + parse = ast_strdupa(data); AST_STANDARD_APP_ARGS(args, parse); diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 77e18aefa6..b051d89245 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -46,6 +46,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/global_datastores.h" #include "asterisk/bridge_basic.h" #include "asterisk/bridge_after.h" +#include "asterisk/max_forwards.h" /*** DOCUMENTATION @@ -391,6 +392,16 @@ ASTERISK_REGISTER_FILE() R/0 Returns caller URL + + R/W Get or set the maximum number of call forwards for this channel. + + This number describes the number of times a call may be forwarded by this channel + before the call fails. "Forwards" in this case refers to redirects by phones as well + as calls to local channels. + + Note that this has no relation to the SIP Max-Forwards header. + + @@ -583,6 +594,10 @@ static int func_channel_read(struct ast_channel *chan, const char *function, } } ast_channel_unlock(chan); + } else if (!strcasecmp(data, "max_forwards")) { + ast_channel_lock(chan); + snprintf(buf, len, "%d", ast_max_forwards_get(chan)); + ast_channel_unlock(chan); } else if (!ast_channel_tech(chan) || !ast_channel_tech(chan)->func_channel_read || ast_channel_tech(chan)->func_channel_read(chan, function, data, buf, len)) { ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data); ret = -1; @@ -743,6 +758,16 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio store->media = ast_true(value) ? 1 : 0; } ast_channel_unlock(chan); + } else if (!strcasecmp(data, "max_forwards")) { + int max_forwards; + if (sscanf(value, "%d", &max_forwards) != 1) { + ast_log(LOG_WARNING, "Unable to set max forwards to '%s'\n", value); + ret = -1; + } else { + ast_channel_lock(chan); + ret = ast_max_forwards_set(chan, max_forwards); + ast_channel_unlock(chan); + } } else if (!ast_channel_tech(chan)->func_channel_write || ast_channel_tech(chan)->func_channel_write(chan, function, data, value)) { ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", diff --git a/include/asterisk/global_datastores.h b/include/asterisk/global_datastores.h index 16267a894e..2946ede849 100644 --- a/include/asterisk/global_datastores.h +++ b/include/asterisk/global_datastores.h @@ -26,14 +26,8 @@ #include "asterisk/channel.h" -extern const struct ast_datastore_info dialed_interface_info; extern const struct ast_datastore_info secure_call_info; -struct ast_dialed_interface { - AST_LIST_ENTRY(ast_dialed_interface) list; - char interface[1]; -}; - struct ast_secure_call_store { unsigned int signaling:1; unsigned int media:1; diff --git a/include/asterisk/max_forwards.h b/include/asterisk/max_forwards.h new file mode 100644 index 0000000000..3130b4b64d --- /dev/null +++ b/include/asterisk/max_forwards.h @@ -0,0 +1,78 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2015, Digium, Inc. + * + * Mark Michelson + * + * 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. + */ + +#ifndef MAX_FORWARDS_H + +struct ast_channel; + +/*! + * \brief Set the starting max forwards for a particular channel. + * + * \pre chan is locked + * + * \param starting_count The value to set the max forwards to. + * \param chan The channel on which to set the max forwards. + * \retval 0 Success + * \retval 1 Failure + */ +int ast_max_forwards_set(struct ast_channel *chan, int starting_count); + +/*! + * \brief Get the current max forwards for a particular channel. + * + * If the channel has not had max forwards set on it, then the channel + * will have the default max forwards set on it and that value will + * be returned. + * + * \pre chan is locked + * + * \param chan The channel to get the max forwards for. + * \return The current max forwards count on the channel + */ +int ast_max_forwards_get(struct ast_channel *chan); + +/*! + * \brief Decrement the max forwards count for a particular channel. + * + * If the channel has not had max forwards set on it, then the channel + * will have the default max forwards set on it and that value will + * not be decremented. + * + * \pre chan is locked + * + * \chan The channel for which the max forwards value should be decremented + * \retval 0 Success + * \retval -1 Failure + */ +int ast_max_forwards_decrement(struct ast_channel *chan); + +/*! + * \brief Reset the max forwards on a channel to its starting value. + * + * If the channel has not had max forwards set on it, then the channel + * will have the default max forwards set on it. + * + * \pre chan is locked. + * + * \param chan The channel on which to reset the max forwards count. + * \retval 0 Success + * \retval -1 Failure + */ +int ast_max_forwards_reset(struct ast_channel *chan); + +#endif /* MAX_FORWARDS_H */ diff --git a/main/ccss.c b/main/ccss.c index c1b3372dce..51edae745b 100644 --- a/main/ccss.c +++ b/main/ccss.c @@ -2237,9 +2237,7 @@ static void call_destructor_with_no_monitor(const char * const monitor_type, voi * Note that it is not necessarily erroneous to add the same * device to the tree twice. If the same device is called by * two different extension during the same call, then - * that is a legitimate situation. Of course, I'm pretty sure - * the dialed_interfaces global datastore will not allow that - * to happen anyway. + * that is a legitimate situation. * * \param device_name The name of the device being added to the tree * \param dialstring The dialstring used to dial the device being added diff --git a/main/channel.c b/main/channel.c index 4e418b6ea9..db126db763 100644 --- a/main/channel.c +++ b/main/channel.c @@ -75,6 +75,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/bridge.h" #include "asterisk/test.h" #include "asterisk/stasis_channels.h" +#include "asterisk/max_forwards.h" /*** DOCUMENTATION ***/ @@ -5621,6 +5622,7 @@ static void call_forward_inherit(struct ast_channel *new_chan, struct ast_channe ast_channel_lock_both(parent, new_chan); ast_channel_inherit_variables(parent, new_chan); ast_channel_datastore_inherit(parent, new_chan); + ast_max_forwards_decrement(new_chan); ast_channel_unlock(new_chan); ast_channel_unlock(parent); } @@ -5740,6 +5742,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c ast_channel_lock_both(oh->parent_channel, chan); ast_channel_inherit_variables(oh->parent_channel, chan); ast_channel_datastore_inherit(oh->parent_channel, chan); + ast_max_forwards_decrement(chan); ast_channel_unlock(oh->parent_channel); ast_channel_unlock(chan); } diff --git a/main/dial.c b/main/dial.c index f0cf127375..b935b6d8b6 100644 --- a/main/dial.c +++ b/main/dial.c @@ -44,6 +44,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/app.h" #include "asterisk/causes.h" #include "asterisk/stasis_channels.h" +#include "asterisk/max_forwards.h" /*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */ struct ast_dial { @@ -299,6 +300,19 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe .uniqueid2 = channel->assignedid2, }; + if (chan) { + int max_forwards; + + ast_channel_lock(chan); + max_forwards = ast_max_forwards_get(chan); + ast_channel_unlock(chan); + + if (max_forwards <= 0) { + ast_log(LOG_WARNING, "Cannot dial from channel '%s'. Max forwards exceeded\n", + ast_channel_name(chan)); + } + } + /* Copy device string over */ ast_copy_string(numsubst, channel->device, sizeof(numsubst)); @@ -337,6 +351,7 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe if (chan) { ast_channel_inherit_variables(chan, channel->owner); ast_channel_datastore_inherit(chan, channel->owner); + ast_max_forwards_decrement(channel->owner); /* Copy over callerid information */ ast_party_redirecting_copy(ast_channel_redirecting(channel->owner), ast_channel_redirecting(chan)); diff --git a/main/features.c b/main/features.c index 971fb4a025..4acd8aab25 100644 --- a/main/features.c +++ b/main/features.c @@ -78,6 +78,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/stasis.h" #include "asterisk/stasis_channels.h" #include "asterisk/features_config.h" +#include "asterisk/max_forwards.h" /*** DOCUMENTATION @@ -420,22 +421,6 @@ static void add_features_datastores(struct ast_channel *caller, struct ast_chann add_features_datastore(callee, &config->features_callee, &config->features_caller); } -static void clear_dialed_interfaces(struct ast_channel *chan) -{ - struct ast_datastore *di_datastore; - - ast_channel_lock(chan); - if ((di_datastore = ast_channel_datastore_find(chan, &dialed_interface_info, NULL))) { - if (option_debug) { - ast_log(LOG_DEBUG, "Removing dialed interfaces datastore on %s since we're bridging\n", ast_channel_name(chan)); - } - if (!ast_channel_datastore_remove(chan, di_datastore)) { - ast_datastore_free(di_datastore); - } - } - ast_channel_unlock(chan); -} - static void bridge_config_set_limits_warning_values(struct ast_bridge_config *config, struct ast_bridge_features_limits *limits) { if (config->end_sound) { @@ -572,20 +557,13 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer, ast_channel_log("Pre-bridge PEER Channel info", peer); #endif - /* - * If we are bridging a call, stop worrying about forwarding - * loops. We presume that if a call is being bridged, that the - * humans in charge know what they're doing. If they don't, - * well, what can we do about that? - */ - clear_dialed_interfaces(chan); - clear_dialed_interfaces(peer); - res = 0; ast_channel_lock(chan); + ast_max_forwards_reset(chan); res |= ast_bridge_features_ds_append(chan, &config->features_caller); ast_channel_unlock(chan); ast_channel_lock(peer); + ast_max_forwards_reset(peer); res |= ast_bridge_features_ds_append(peer, &config->features_callee); ast_channel_unlock(peer); diff --git a/main/global_datastores.c b/main/global_datastores.c index dd1e0278e4..8ba769d3d2 100644 --- a/main/global_datastores.c +++ b/main/global_datastores.c @@ -32,62 +32,6 @@ ASTERISK_REGISTER_FILE() #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); - dialed_interface_destroy(new_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, -}; static void secure_call_store_destroy(void *data) { diff --git a/main/max_forwards.c b/main/max_forwards.c new file mode 100644 index 0000000000..8f1d4eed1c --- /dev/null +++ b/main/max_forwards.c @@ -0,0 +1,165 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2015, Digium, Inc. + * + * Mark Michelson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not mfrectly 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, mfstributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include "asterisk/max_forwards.h" +#include "asterisk/channel.h" + +#define DEFAULT_MAX_FORWARDS 20 + +/*! + * \brief Channel datastore data for max forwards + */ +struct max_forwards { + /*! The starting count. Used to allow resetting to the original value */ + int starting_count; + /*! The current count. When this reaches 0, you're outta luck */ + int current_count; +}; + +static struct max_forwards *max_forwards_alloc(int starting_count, int current_count) +{ + struct max_forwards *mf; + + mf = ast_malloc(sizeof(*mf)); + if (!mf) { + return NULL; + } + + mf->starting_count = starting_count; + mf->current_count = current_count; + + return mf; +} + +static void *max_forwards_duplicate(void *data) +{ + struct max_forwards *mf = data; + + return max_forwards_alloc(mf->starting_count, mf->current_count); +} + +static void max_forwards_destroy(void *data) +{ + ast_free(data); +} + +const struct ast_datastore_info max_forwards_info = { + .type = "mfaled-interface", + .duplicate = max_forwards_duplicate, + .destroy = max_forwards_destroy, +}; + +static struct ast_datastore *max_forwards_datastore_alloc(struct ast_channel *chan, + int starting_count) +{ + struct ast_datastore *mf_datastore; + struct max_forwards *mf; + + mf_datastore = ast_datastore_alloc(&max_forwards_info, NULL); + if (!mf_datastore) { + return NULL; + } + mf_datastore->inheritance = DATASTORE_INHERIT_FOREVER; + + mf = max_forwards_alloc(starting_count, starting_count); + if (!mf) { + ast_datastore_free(mf_datastore); + return NULL; + } + mf_datastore->data = mf; + + ast_channel_datastore_add(chan, mf_datastore); + + return mf_datastore; +} + +static struct ast_datastore *max_forwards_datastore_find_or_alloc(struct ast_channel *chan) +{ + struct ast_datastore *mf_datastore; + + mf_datastore = ast_channel_datastore_find(chan, &max_forwards_info, NULL); + if (!mf_datastore) { + mf_datastore = max_forwards_datastore_alloc(chan, DEFAULT_MAX_FORWARDS); + } + + return mf_datastore; +} + +int ast_max_forwards_set(struct ast_channel *chan, int starting_count) +{ + struct ast_datastore *mf_datastore; + struct max_forwards *mf; + + mf_datastore = max_forwards_datastore_find_or_alloc(chan); + if (!mf_datastore) { + return -1; + } + + mf = mf_datastore->data; + mf->starting_count = mf->current_count = starting_count; + + return 0; +} + +int ast_max_forwards_get(struct ast_channel *chan) +{ + struct ast_datastore *mf_datastore; + struct max_forwards *mf; + + mf_datastore = max_forwards_datastore_find_or_alloc(chan); + if (!mf_datastore) { + return -1; + } + + mf = mf_datastore->data; + return mf->current_count; +} + +int ast_max_forwards_decrement(struct ast_channel *chan) +{ + struct ast_datastore *mf_datastore; + struct max_forwards *mf; + + mf_datastore = max_forwards_datastore_find_or_alloc(chan); + if (!mf_datastore) { + return -1; + } + + mf = mf_datastore->data; + --mf->current_count; + + return 0; +} + +int ast_max_forwards_reset(struct ast_channel *chan) +{ + struct ast_datastore *mf_datastore; + struct max_forwards *mf; + + mf_datastore = max_forwards_datastore_find_or_alloc(chan); + if (!mf_datastore) { + return -1; + } + + mf = mf_datastore->data; + mf->current_count = mf->starting_count; + + return 0; +} diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c index a4ac157e4e..49f7892120 100644 --- a/res/res_pjsip_diversion.c +++ b/res/res_pjsip_diversion.c @@ -248,6 +248,7 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect pjsip_name_addr *name_addr; pjsip_sip_uri *uri; pjsip_param *param; + pjsip_fromto_hdr *old_hdr; struct ast_party_id *id = &data->from; pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri; @@ -273,6 +274,10 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect pj_list_insert_before(&hdr->other_param, param); hdr->uri = (pjsip_uri *) name_addr; + old_hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &diversion_name, NULL); + if (old_hdr) { + pj_list_erase(old_hdr); + } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr); }