diff options
author | zuul <zuul@gerrit.asterisk.org> | 2016-05-31 12:39:53 -0500 |
---|---|---|
committer | Gerrit Code Review <gerrit2@gerrit.digium.api> | 2016-05-31 12:39:53 -0500 |
commit | 84a93b0d671f9bb8836fdb5aaa228f68e199a7d5 (patch) | |
tree | a6d8e3069966570277871cf8fa14dec64b31741f /res/ari | |
parent | dffbb2d7c31481df2aa4b3624b90a131507a7a5f (diff) | |
parent | 88d997913faabe81f8b9e7bdaa56742be0d669b9 (diff) |
Merge "ARI: Re-implement the ARI dial command, allowing for early bridging."
Diffstat (limited to 'res/ari')
-rw-r--r-- | res/ari/resource_channels.c | 139 |
1 files changed, 114 insertions, 25 deletions
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c index b42581c84..0f18b2dc1 100644 --- a/res/ari/resource_channels.c +++ b/res/ari/resource_channels.c @@ -48,6 +48,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/format_cache.h" #include "asterisk/core_local.h" #include "asterisk/dial.h" +#include "asterisk/max_forwards.h" #include "resource_channels.h" #include <limits.h> @@ -1503,6 +1504,67 @@ static void *ari_channel_thread(void *data) return NULL; } +struct ast_datastore_info dialstring_info = { + .type = "ARI Dialstring", + .destroy = ast_free_ptr, +}; + +/*! + * \brief Save dialstring onto a channel datastore + * + * This will later be retrieved when it comes time to actually dial the channel + * + * \param chan The channel on which to save the dialstring + * \param dialstring The dialstring to save + * \retval 0 SUCCESS! + * \reval -1 Failure :( + */ +static int save_dialstring(struct ast_channel *chan, const char *dialstring) +{ + struct ast_datastore *datastore; + + datastore = ast_datastore_alloc(&dialstring_info, NULL); + if (!datastore) { + return -1; + } + + datastore->data = ast_strdup(dialstring); + if (!datastore->data) { + ast_datastore_free(datastore); + return -1; + } + + ast_channel_lock(chan); + if (ast_channel_datastore_add(chan, datastore)) { + ast_channel_unlock(chan); + ast_datastore_free(datastore); + return -1; + } + ast_channel_unlock(chan); + + return 0; +} + +/*! + * \brief Retrieve the dialstring from the channel datastore + * + * \pre chan is locked + * \param chan Channel that was previously created in ARI + * \retval NULL Failed to find datastore + * \retval non-NULL The dialstring + */ +static char *restore_dialstring(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + datastore = ast_channel_datastore_find(chan, &dialstring_info, NULL); + if (!datastore) { + return NULL; + } + + return datastore->data; +} + void ast_ari_channels_create(struct ast_variable *headers, struct ast_ari_channels_create_args *args, struct ast_ari_response *response) @@ -1562,6 +1624,12 @@ void ast_ari_channels_create(struct ast_variable *headers, return; } + if (save_dialstring(chan_data->chan, stuff)) { + ast_ari_response_alloc_failed(response); + chan_data_destroy(chan_data); + return; + } + snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan_data->chan)); if (ast_pthread_create_detached(&thread, NULL, ari_channel_thread, chan_data)) { @@ -1580,8 +1648,8 @@ void ast_ari_channels_dial(struct ast_variable *headers, { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, caller, NULL, ast_channel_cleanup); - struct ast_channel *callee; - struct ast_dial *dial; + RAII_VAR(struct ast_channel *, callee, NULL, ast_channel_cleanup); + char *dialstring; control = find_control(response, args->channel_id); if (control == NULL) { @@ -1598,43 +1666,64 @@ void ast_ari_channels_dial(struct ast_variable *headers, return; } - if (ast_channel_state(callee) != AST_STATE_DOWN) { - ast_channel_unref(callee); + if (ast_channel_state(callee) != AST_STATE_DOWN + && ast_channel_state(callee) != AST_STATE_RESERVED) { ast_ari_response_error(response, 409, "Conflict", "Channel is not in the 'Down' state"); return; } - dial = ast_dial_create(); - if (!dial) { - ast_channel_unref(callee); - ast_ari_response_alloc_failed(response); - return; + /* XXX This is straight up copied from main/dial.c. It's probably good + * to separate this to some common method. + */ + if (caller) { + ast_channel_lock_both(caller, callee); + } else { + ast_channel_lock(callee); } - if (ast_dial_append_channel(dial, callee) < 0) { - ast_channel_unref(callee); - ast_dial_destroy(dial); - ast_ari_response_alloc_failed(response); + dialstring = restore_dialstring(callee); + if (!dialstring) { + ast_channel_unlock(callee); + if (caller) { + ast_channel_unlock(caller); + } + ast_ari_response_error(response, 409, "Conflict", + "Dialing a channel not created by ARI"); return; } - - /* From this point, we don't have to unref the callee channel on - * failure paths because the dial owns the reference to the called - * channel and will unref the channel for us + /* Make a copy of the dialstring just in case some jerk tries to hang up the + * channel before we can actually dial */ + dialstring = ast_strdupa(dialstring); - if (ast_dial_prerun(dial, caller, NULL)) { - ast_dial_destroy(dial); - ast_ari_response_alloc_failed(response); - return; + ast_channel_stage_snapshot(callee); + if (caller) { + ast_channel_inherit_variables(caller, callee); + ast_channel_datastore_inherit(caller, callee); + ast_max_forwards_decrement(callee); + + /* Copy over callerid information */ + ast_party_redirecting_copy(ast_channel_redirecting(callee), ast_channel_redirecting(caller)); + + ast_channel_dialed(callee)->transit_network_select = ast_channel_dialed(caller)->transit_network_select; + + ast_connected_line_copy_from_caller(ast_channel_connected(callee), ast_channel_caller(caller)); + + ast_channel_language_set(callee, ast_channel_language(caller)); + ast_channel_req_accountcodes(callee, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER); + if (ast_strlen_zero(ast_channel_musicclass(callee))) + ast_channel_musicclass_set(callee, ast_channel_musicclass(caller)); + + ast_channel_adsicpe_set(callee, ast_channel_adsicpe(caller)); + ast_channel_transfercapability_set(callee, ast_channel_transfercapability(caller)); + ast_channel_unlock(caller); } - ast_dial_set_user_data(dial, control); - ast_dial_set_global_timeout(dial, args->timeout * 1000); + ast_channel_stage_snapshot_done(callee); + ast_channel_unlock(callee); - if (stasis_app_control_dial(control, dial)) { - ast_dial_destroy(dial); + if (stasis_app_control_dial(control, dialstring, args->timeout)) { ast_ari_response_alloc_failed(response); return; } |