summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2014-12-09 15:45:19 +0000
committerJoshua Colp <jcolp@digium.com>2014-12-09 15:45:19 +0000
commit60ab564ad2aa326fddb3fb5e8ec582ef3d1db666 (patch)
treefd6336471df3225d81eaf2cdcbb057f84bac24f2
parentb6e18cae5ca7f3aedc313849d15573c2ea21f3cb (diff)
ari: Add support for specifying an originator channel when originating.
If an originator channel is specified when originating a channel the linked ID of it will be applied to the newly originated outgoing channel. This allows an association to be made between the two so it is known that the originator has dialed the originated channel. ASTERISK-24552 #close Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/4243/ ........ Merged revisions 429153 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@429154 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--CHANGES11
-rw-r--r--res/ari/resource_channels.c214
-rw-r--r--res/ari/resource_channels.h4
-rw-r--r--res/res_ari_channels.c14
-rw-r--r--rest-api/api-docs/channels.json16
5 files changed, 236 insertions, 23 deletions
diff --git a/CHANGES b/CHANGES
index a883f5156..b4f798f40 100644
--- a/CHANGES
+++ b/CHANGES
@@ -76,6 +76,17 @@ res_musiconhold
application (e.g. Queue or Dial) specified music.
------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.1.0 to Asterisk 13.2.0 ------------
+------------------------------------------------------------------------------
+
+ARI
+------------------
+ * The Originate operation now takes in an originator channel. The linked ID of
+ this originator channel is applied to the newly originated outgoing channel.
+ If using CEL this allows an association to be established between the two so
+ it can be recognized that the originator is dialing the originated channel.
+
+------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.0.0 to Asterisk 13.1.0 ------------
------------------------------------------------------------------------------
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 112607db6..e3ef9eb16 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h"
#include "asterisk/format_cache.h"
#include "asterisk/core_local.h"
+#include "asterisk/dial.h"
#include "resource_channels.h"
#include <limits.h>
@@ -723,6 +724,69 @@ void ast_ari_channels_list(struct ast_variable *headers,
ast_ari_response_ok(response, ast_json_ref(json));
}
+/*! \brief Structure used for origination */
+struct ari_origination {
+ /*! \brief Dialplan context */
+ char context[AST_MAX_CONTEXT];
+ /*! \brief Dialplan extension */
+ char exten[AST_MAX_EXTENSION];
+ /*! \brief Dialplan priority */
+ int priority;
+ /*! \brief Application data to pass to Stasis application */
+ char appdata[0];
+};
+
+/*! \brief Thread which dials and executes upon answer */
+static void *ari_originate_dial(void *data)
+{
+ struct ast_dial *dial = data;
+ struct ari_origination *origination = ast_dial_get_user_data(dial);
+ enum ast_dial_result res;
+
+ res = ast_dial_run(dial, NULL, 0);
+ if (res != AST_DIAL_RESULT_ANSWERED) {
+ goto end;
+ }
+
+ if (!ast_strlen_zero(origination->appdata)) {
+ struct ast_app *app = pbx_findapp("Stasis");
+
+ if (app) {
+ ast_verb(4, "Launching Stasis(%s) on %s\n", origination->appdata,
+ ast_channel_name(ast_dial_answered(dial)));
+ pbx_exec(ast_dial_answered(dial), app, origination->appdata);
+ } else {
+ ast_log(LOG_WARNING, "No such application 'Stasis'\n");
+ }
+ } else {
+ struct ast_channel *answered = ast_dial_answered(dial);
+
+ if (!ast_strlen_zero(origination->context)) {
+ ast_channel_context_set(answered, origination->context);
+ }
+
+ if (!ast_strlen_zero(origination->exten)) {
+ ast_channel_exten_set(answered, origination->exten);
+ }
+
+ if (origination->priority > 0) {
+ ast_channel_priority_set(answered, origination->priority);
+ }
+
+ if (ast_pbx_run(answered)) {
+ ast_log(LOG_ERROR, "Failed to start PBX on %s\n", ast_channel_name(answered));
+ } else {
+ /* PBX will have taken care of hanging up, so we steal the answered channel so dial doesn't do it */
+ ast_dial_answered_steal(dial);
+ }
+ }
+
+end:
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ return NULL;
+}
+
static void ari_channels_handle_originate_with_id(const char *args_endpoint,
const char *args_extension,
const char *args_context,
@@ -734,23 +798,27 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
struct ast_variable *variables,
const char *args_channel_id,
const char *args_other_channel_id,
+ const char *args_originator,
struct ast_ari_response *response)
{
char *dialtech;
char dialdevice[AST_CHANNEL_NAME];
+ struct ast_dial *dial;
char *caller_id = NULL;
char *cid_num = NULL;
char *cid_name = NULL;
- int timeout = 30000;
RAII_VAR(struct ast_format_cap *, cap,
ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
char *stuff;
+ struct ast_channel *other = NULL;
struct ast_channel *chan;
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
struct ast_assigned_ids assignedids = {
.uniqueid = args_channel_id,
.uniqueid2 = args_other_channel_id,
};
+ struct ari_origination *origination;
+ pthread_t thread;
if (!cap) {
ast_ari_response_alloc_failed(response);
@@ -783,24 +851,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
return;
}
- if (args_timeout > 0) {
- timeout = args_timeout * 1000;
- } else if (args_timeout == -1) {
- timeout = -1;
- }
-
- if (!ast_strlen_zero(args_caller_id)) {
- caller_id = ast_strdupa(args_caller_id);
- ast_callerid_parse(caller_id, &cid_name, &cid_num);
-
- if (ast_is_shrinkable_phonenumber(cid_num)) {
- ast_shrink_phone_number(cid_num);
- }
- }
-
if (!ast_strlen_zero(args_app)) {
- const char *app = "Stasis";
-
RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);
if (!appdata) {
@@ -813,23 +864,125 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
ast_str_append(&appdata, 0, ",%s", args_app_args);
}
- /* originate a channel, putting it into an application */
- if (ast_pbx_outgoing_app(dialtech, cap, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, variables, NULL, &chan, &assignedids)) {
+ origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1);
+ if (!origination) {
ast_ari_response_alloc_failed(response);
return;
}
+
+ strcpy(origination->appdata, ast_str_buffer(appdata));
} else if (!ast_strlen_zero(args_extension)) {
- /* originate a channel, sending it to an extension */
- if (ast_pbx_outgoing_exten(dialtech, cap, dialdevice, timeout, S_OR(args_context, "default"), args_extension, args_priority ? args_priority : 1, NULL, 0, cid_num, cid_name, variables, NULL, &chan, 0, &assignedids)) {
+ origination = ast_calloc(1, sizeof(*origination) + 1);
+ if (!origination) {
ast_ari_response_alloc_failed(response);
return;
}
+
+ ast_copy_string(origination->context, S_OR(args_context, "default"), sizeof(origination->context));
+ ast_copy_string(origination->exten, args_extension, sizeof(origination->exten));
+ origination->priority = args_priority ? args_priority : 1;
+ origination->appdata[0] = '\0';
} else {
ast_ari_response_error(response, 400, "Bad Request",
"Application or extension must be specified");
return;
}
+ dial = ast_dial_create();
+ if (!dial) {
+ ast_ari_response_alloc_failed(response);
+ ast_free(origination);
+ return;
+ }
+ ast_dial_set_user_data(dial, origination);
+
+ if (ast_dial_append(dial, dialtech, dialdevice, &assignedids)) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ return;
+ }
+
+ if (args_timeout > 0) {
+ ast_dial_set_global_timeout(dial, args_timeout * 1000);
+ } else if (args_timeout == -1) {
+ ast_dial_set_global_timeout(dial, -1);
+ } else {
+ ast_dial_set_global_timeout(dial, 30000);
+ }
+
+ if (!ast_strlen_zero(args_caller_id)) {
+ caller_id = ast_strdupa(args_caller_id);
+ ast_callerid_parse(caller_id, &cid_name, &cid_num);
+
+ if (ast_is_shrinkable_phonenumber(cid_num)) {
+ ast_shrink_phone_number(cid_num);
+ }
+ }
+
+ if (!ast_strlen_zero(args_originator)) {
+ other = ast_channel_get_by_name(args_originator);
+ if (!other) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "Provided originator channel was not found");
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ return;
+ }
+ }
+
+ if (ast_dial_prerun(dial, other, cap)) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ ast_channel_cleanup(other);
+ return;
+ }
+
+ ast_channel_cleanup(other);
+
+ chan = ast_dial_get_channel(dial, 0);
+ if (!chan) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ return;
+ }
+
+ if (!ast_strlen_zero(cid_num) || !ast_strlen_zero(cid_name)) {
+ struct ast_party_connected_line connected;
+
+ /*
+ * It seems strange to set the CallerID on an outgoing call leg
+ * to whom we are calling, but this function's callers are doing
+ * various Originate methods. This call leg goes to the local
+ * user. Once the called party answers, the dialplan needs to
+ * be able to access the CallerID from the CALLERID function as
+ * if the called party had placed this call.
+ */
+ ast_set_callerid(chan, cid_num, cid_name, cid_num);
+
+ ast_party_connected_line_set_init(&connected, ast_channel_connected(chan));
+ if (!ast_strlen_zero(cid_num)) {
+ connected.id.number.valid = 1;
+ connected.id.number.str = (char *) cid_num;
+ connected.id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+ }
+ if (!ast_strlen_zero(cid_name)) {
+ connected.id.name.valid = 1;
+ connected.id.name.str = (char *) cid_name;
+ connected.id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
+ }
+ ast_channel_set_connected_line(chan, &connected, NULL);
+ }
+
+ ast_channel_lock(chan);
+ if (variables) {
+ ast_set_variables(chan, variables);
+ }
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
+
if (!ast_strlen_zero(args_app)) {
struct ast_channel *local_peer;
@@ -846,8 +999,21 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
ast_channel_unlock(chan);
- ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
+ /* Before starting the async dial bump the ref in case the dial quickly goes away and takes
+ * the reference with it
+ */
+ ast_channel_ref(chan);
+
+ if (ast_pthread_create_detached(&thread, NULL, ari_originate_dial, dial)) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ } else {
+ ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
+ }
+
ast_channel_unref(chan);
+ return;
}
void ast_ari_channels_originate_with_id(struct ast_variable *headers,
@@ -883,6 +1049,7 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
variables,
args->channel_id,
args->other_channel_id,
+ args->originator,
response);
}
@@ -919,6 +1086,7 @@ void ast_ari_channels_originate(struct ast_variable *headers,
variables,
args->channel_id,
args->other_channel_id,
+ args->originator,
response);
}
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 104e1bdb3..627f9c97a 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -74,6 +74,8 @@ struct ast_ari_channels_originate_args {
const char *channel_id;
/*! The unique id to assign the second channel when using local channels. */
const char *other_channel_id;
+ /*! The unique id of the channel which is originating this one. */
+ const char *originator;
};
/*!
* \brief Body parsing function for /channels.
@@ -133,6 +135,8 @@ struct ast_ari_channels_originate_with_id_args {
struct ast_json *variables;
/*! The unique id to assign the second channel when using local channels. */
const char *other_channel_id;
+ /*! The unique id of the channel which is originating this one. */
+ const char *originator;
};
/*!
* \brief Body parsing function for /channels/{channelId}.
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index 08edc64dc..8cc25f1d8 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -148,6 +148,10 @@ int ast_ari_channels_originate_parse_body(
if (field) {
args->other_channel_id = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "originator");
+ if (field) {
+ args->originator = ast_json_string_get(field);
+ }
return 0;
}
@@ -202,6 +206,9 @@ static void ast_ari_channels_originate_cb(
if (strcmp(i->name, "otherChannelId") == 0) {
args.other_channel_id = (i->value);
} else
+ if (strcmp(i->name, "originator") == 0) {
+ args.originator = (i->value);
+ } else
{}
}
/* Look for a JSON request entity */
@@ -354,6 +361,10 @@ int ast_ari_channels_originate_with_id_parse_body(
if (field) {
args->other_channel_id = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "originator");
+ if (field) {
+ args->originator = ast_json_string_get(field);
+ }
return 0;
}
@@ -405,6 +416,9 @@ static void ast_ari_channels_originate_with_id_cb(
if (strcmp(i->name, "otherChannelId") == 0) {
args.other_channel_id = (i->value);
} else
+ if (strcmp(i->name, "originator") == 0) {
+ args.originator = (i->value);
+ } else
{}
}
for (i = path_vars; i; i = i->next) {
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index 67634740a..f9c8d8647 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -112,6 +112,14 @@
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "originator",
+ "description": "The unique id of the channel which is originating this one.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
}
],
"errorResponses": [
@@ -244,6 +252,14 @@
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "originator",
+ "description": "The unique id of the channel which is originating this one.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
}
],
"errorResponses": [