summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/app_dial.c156
-rw-r--r--apps/app_page.c28
-rw-r--r--apps/app_queue.c412
-rw-r--r--apps/app_record.c3
4 files changed, 374 insertions, 225 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 46014c639..55700e9b3 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -704,6 +704,8 @@ struct chanlist {
const char *tech;
/*! Channel device addressing. (Stored in stuff[]) */
const char *number;
+ /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
+ char *orig_chan_name;
uint64_t flags;
/*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
struct ast_party_connected_line connected;
@@ -722,6 +724,7 @@ static void chanlist_free(struct chanlist *outgoing)
{
ast_party_connected_line_free(&outgoing->connected);
ast_aoc_destroy_decoded(outgoing->aoc_s_rate_list);
+ ast_free(outgoing->orig_chan_name);
ast_free(outgoing);
}
@@ -1047,6 +1050,34 @@ static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out
}
}
+/*!
+ * \internal
+ * \brief Update connected line on chan from peer.
+ * \since 13.6.0
+ *
+ * \param chan Channel to get connected line updated.
+ * \param peer Channel providing connected line information.
+ * \param is_caller Non-zero if chan is the calling channel.
+ *
+ * \return Nothing
+ */
+static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
+{
+ struct ast_party_connected_line connected_caller;
+
+ ast_party_connected_line_init(&connected_caller);
+
+ ast_channel_lock(peer);
+ ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
+ ast_channel_unlock(peer);
+ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+ if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
+ && ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
+ ast_channel_update_connected_line(chan, &connected_caller, NULL);
+ }
+ ast_party_connected_line_free(&connected_caller);
+}
+
static struct ast_channel *wait_for_answer(struct ast_channel *in,
struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
char *opt_args[],
@@ -1067,7 +1098,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
int single = outgoing && !AST_LIST_NEXT(outgoing, node);
int caller_entertained = outgoing
&& ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
- struct ast_party_connected_line connected_caller;
struct ast_str *featurecode = ast_str_alloca(AST_FEATURE_MAX_LEN + 1);
int cc_recall_core_id;
int is_cc_recall;
@@ -1075,7 +1105,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
int num_ringing = 0;
struct timeval start = ast_tvnow();
- ast_party_connected_line_init(&connected_caller);
if (single) {
/* Turn off hold music, etc */
if (!caller_entertained) {
@@ -1096,15 +1125,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
if (!ast_test_flag64(outgoing, OPT_IGNORE_CONNECTEDLINE)
&& !ast_test_flag64(outgoing, DIAL_CALLERID_ABSENT)) {
- ast_channel_lock(outgoing->chan);
- ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(outgoing->chan));
- ast_channel_unlock(outgoing->chan);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(outgoing->chan, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(outgoing->chan, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
- }
- ast_party_connected_line_free(&connected_caller);
+ update_connected_line_from_peer(in, outgoing->chan, 1);
}
}
@@ -1158,22 +1179,21 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
if (ast_test_flag64(o, DIAL_STILLGOING) && ast_channel_state(c) == AST_STATE_UP) {
if (!peer) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
- if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
+ if (o->orig_chan_name
+ && strcmp(o->orig_chan_name, ast_channel_name(c))) {
+ /*
+ * The channel name changed so we must generate COLP update.
+ * Likely because a call pickup channel masqueraded in.
+ */
+ update_connected_line_from_peer(in, c, 1);
+ } else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
}
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
- ast_channel_lock(c);
- ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(c));
- ast_channel_unlock(c);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(c, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(c, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
- }
- ast_party_connected_line_free(&connected_caller);
+ update_connected_line_from_peer(in, c, 1);
}
}
if (o->aoc_s_rate_list) {
@@ -1228,19 +1248,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
do_forward(o, &num, peerflags, single, caller_entertained, &orig,
forced_clid, stored_clid);
- if (single && o->chan
- && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
- && !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
- ast_channel_lock(o->chan);
- ast_connected_line_copy_from_caller(&connected_caller,
- ast_channel_caller(o->chan));
- ast_channel_unlock(o->chan);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
+ if (o->chan) {
+ ast_free(o->orig_chan_name);
+ o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
+ if (single
+ && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)
+ && !ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
+ update_connected_line_from_peer(in, o->chan, 1);
}
- ast_party_connected_line_free(&connected_caller);
}
continue;
}
@@ -1264,22 +1279,21 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
/* This is our guy if someone answered. */
if (!peer) {
ast_verb(3, "%s answered %s\n", ast_channel_name(c), ast_channel_name(in));
- if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
+ if (o->orig_chan_name
+ && strcmp(o->orig_chan_name, ast_channel_name(c))) {
+ /*
+ * The channel name changed so we must generate COLP update.
+ * Likely because a call pickup channel masqueraded in.
+ */
+ update_connected_line_from_peer(in, c, 1);
+ } else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
}
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
- ast_channel_lock(c);
- ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(c));
- ast_channel_unlock(c);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(c, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(c, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
- }
- ast_party_connected_line_free(&connected_caller);
+ update_connected_line_from_peer(in, c, 1);
}
}
if (o->aoc_s_rate_list) {
@@ -2524,6 +2538,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
ast_channel_stage_snapshot_done(tc);
+ /* Save the original channel name to detect call pickup masquerading in. */
+ tmp->orig_chan_name = ast_strdup(ast_channel_name(tc));
+
ast_channel_unlock(tc);
ast_channel_unlock(chan);
@@ -2707,7 +2724,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
chans[0] = chan;
chans[1] = peer;
- /* we need to stream the announcment while monitoring the caller for a hangup */
+ /* we need to stream the announcement while monitoring the caller for a hangup */
/* stream the file */
res = ast_streamfile(peer, opt_args[OPT_ARG_ANNOUNCE], ast_channel_language(peer));
@@ -2731,34 +2748,50 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
active_chan = ast_waitfor_n(chans, 2, &ms);
if (active_chan) {
+ struct ast_channel *other_chan;
struct ast_frame *fr = ast_read(active_chan);
+
if (!fr) {
ast_autoservice_chan_hangup_peer(chan, peer);
res = -1;
goto done;
}
- switch(fr->frametype) {
- case AST_FRAME_DTMF_END:
- digit = fr->subclass.integer;
- if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
- ast_stopstream(peer);
- res = ast_senddigit(chan, digit, 0);
+ switch (fr->frametype) {
+ case AST_FRAME_DTMF_END:
+ digit = fr->subclass.integer;
+ if (active_chan == peer && strchr(AST_DIGIT_ANY, res)) {
+ ast_stopstream(peer);
+ res = ast_senddigit(chan, digit, 0);
+ }
+ break;
+ case AST_FRAME_CONTROL:
+ switch (fr->subclass.integer) {
+ case AST_CONTROL_HANGUP:
+ ast_frfree(fr);
+ ast_autoservice_chan_hangup_peer(chan, peer);
+ res = -1;
+ goto done;
+ case AST_CONTROL_CONNECTED_LINE:
+ /* Pass COLP update to the other channel. */
+ if (active_chan == chan) {
+ other_chan = peer;
+ } else {
+ other_chan = chan;
}
- break;
- case AST_FRAME_CONTROL:
- switch (fr->subclass.integer) {
- case AST_CONTROL_HANGUP:
- ast_frfree(fr);
- ast_autoservice_chan_hangup_peer(chan, peer);
- res = -1;
- goto done;
- default:
- break;
+ if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)
+ && ast_channel_connected_line_macro(active_chan,
+ other_chan, fr, other_chan == chan, 1)) {
+ ast_indicate_data(other_chan, fr->subclass.integer,
+ fr->data.ptr, fr->datalen);
}
break;
default:
- /* Ignore all others */
break;
+ }
+ break;
+ default:
+ /* Ignore all others */
+ break;
}
ast_frfree(fr);
}
@@ -2786,7 +2819,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
if (ast_pbx_start(peer)) {
ast_autoservice_chan_hangup_peer(chan, peer);
}
- hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
if (continue_exec)
*continue_exec = 1;
res = 0;
diff --git a/apps/app_page.c b/apps/app_page.c
index cea75cb5e..10a96b61b 100644
--- a/apps/app_page.c
+++ b/apps/app_page.c
@@ -249,12 +249,18 @@ static void page_state_callback(struct ast_dial *dial)
static int page_exec(struct ast_channel *chan, const char *data)
{
- char *tech, *resource, *tmp;
- char confbridgeopts[128], originator[AST_CHANNEL_NAME];
+ char *tech;
+ char *resource;
+ char *tmp;
+ char *predial_callee = NULL;
+ char confbridgeopts[128];
+ char originator[AST_CHANNEL_NAME];
struct page_options options = { { 0, }, { 0, } };
unsigned int confid = ast_random();
struct ast_app *app;
- int res = 0, pos = 0, i = 0;
+ int res = 0;
+ int pos = 0;
+ int i = 0;
struct ast_dial **dial_list;
unsigned int num_dials;
int timeout = 0;
@@ -310,6 +316,15 @@ static int page_exec(struct ast_channel *chan, const char *data)
return -1;
}
+ /* PREDIAL: Preprocess any callee gosub arguments. */
+ if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
+ && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
+ ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLEE]);
+ predial_callee =
+ (char *) ast_app_expand_sub_args(chan, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+ }
+
+ /* PREDIAL: Run gosub on the caller's channel */
if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLER)
&& !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLER])) {
ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLER]);
@@ -360,9 +375,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
/* Set ANSWER_EXEC as global option */
ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, confbridgeopts);
- if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
- && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
- ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+ if (predial_callee) {
+ ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, predial_callee);
}
if (timeout) {
@@ -383,6 +397,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
dial_list[pos++] = dial;
}
+ ast_free(predial_callee);
+
if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
res = ast_streamfile(chan, "beep", ast_channel_language(chan));
if (!res)
diff --git a/apps/app_queue.c b/apps/app_queue.c
index d04080c27..e043e182a 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -549,7 +549,7 @@ ASTERISK_REGISTER_FILE()
Count number of members answering a queue.
</synopsis>
<syntax>
- <parameter name="queuename" required="true" />
+ <parameter name="queuename" required="false" />
<parameter name="option" required="true">
<enumlist>
<enum name="logged">
@@ -565,13 +565,22 @@ ASTERISK_REGISTER_FILE()
<para>Returns the total number of members for the specified queue.</para>
</enum>
<enum name="penalty">
- <para>Gets or sets queue member penalty.</para>
+ <para>Gets or sets queue member penalty. If
+ <replaceable>queuename</replaceable> is not specified
+ when setting the penalty then the penalty is set in all queues
+ the interface is a member.</para>
</enum>
<enum name="paused">
- <para>Gets or sets queue member paused status.</para>
+ <para>Gets or sets queue member paused status. If
+ <replaceable>queuename</replaceable> is not specified
+ when setting the paused status then the paused status is set
+ in all queues the interface is a member.</para>
</enum>
<enum name="ringinuse">
- <para>Gets or sets queue member ringinuse.</para>
+ <para>Gets or sets queue member ringinuse. If
+ <replaceable>queuename</replaceable> is not specified
+ when setting ringinuse then ringinuse is set
+ in all queues the interface is a member.</para>
</enum>
</enumlist>
</parameter>
@@ -579,10 +588,8 @@ ASTERISK_REGISTER_FILE()
</syntax>
<description>
<para>Allows access to queue counts [R] and member information [R/W].</para>
- <para>
- <replaceable>queuename</replaceable> is required for all operations
- <replaceable>interface</replaceable> is required for all member operations.
- </para>
+ <para><replaceable>queuename</replaceable> is required for all read operations.</para>
+ <para><replaceable>interface</replaceable> is required for all member operations.</para>
</description>
<see-also>
<ref type="application">Queue</ref>
@@ -1480,6 +1487,8 @@ struct callattempt {
/*! TRUE if the call is still active */
unsigned int stillgoing:1;
struct ast_aoc_decoded *aoc_s_rate_list;
+ /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
+ char *orig_chan_name;
};
@@ -3962,6 +3971,7 @@ static void callattempt_free(struct callattempt *doomed)
ao2_ref(doomed->member, -1);
}
ast_party_connected_line_free(&doomed->connected);
+ ast_free(doomed->orig_chan_name);
ast_free(doomed);
}
@@ -4293,6 +4303,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
}
+ /* Save the original channel name to detect call pickup masquerading in. */
+ tmp->orig_chan_name = ast_strdup(ast_channel_name(tmp->chan));
+
ast_channel_unlock(tmp->chan);
ast_channel_unlock(qe->chan);
@@ -4572,6 +4585,34 @@ static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, cha
return;
}
+/*!
+ * \internal
+ * \brief Update connected line on chan from peer.
+ * \since 13.6.0
+ *
+ * \param chan Channel to get connected line updated.
+ * \param peer Channel providing connected line information.
+ * \param is_caller Non-zero if chan is the calling channel.
+ *
+ * \return Nothing
+ */
+static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
+{
+ struct ast_party_connected_line connected_caller;
+
+ ast_party_connected_line_init(&connected_caller);
+
+ ast_channel_lock(peer);
+ ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
+ ast_channel_unlock(peer);
+ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+ if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
+ && ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
+ ast_channel_update_connected_line(chan, &connected_caller, NULL);
+ }
+ ast_party_connected_line_free(&connected_caller);
+}
+
#define AST_MAX_WATCHERS 256
/*!
* \brief Wait for a member to answer the call
@@ -4606,12 +4647,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
#ifdef HAVE_EPOLL
struct callattempt *epollo;
#endif
- struct ast_party_connected_line connected_caller;
char *inchan_name;
struct timeval start_time_tv = ast_tvnow();
- ast_party_connected_line_init(&connected_caller);
-
ast_channel_lock(qe->chan);
inchan_name = ast_strdupa(ast_channel_name(qe->chan));
ast_channel_unlock(qe->chan);
@@ -4689,22 +4727,21 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
if (!peer) {
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
- if (!o->block_connected_update) {
+ if (o->orig_chan_name
+ && strcmp(o->orig_chan_name, ochan_name)) {
+ /*
+ * The channel name changed so we must generate COLP update.
+ * Likely because a call pickup channel masqueraded in.
+ */
+ update_connected_line_from_peer(in, o->chan, 1);
+ } else if (!o->block_connected_update) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
}
} else if (!o->dial_callerid_absent) {
- ast_channel_lock(o->chan);
- ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
- ast_channel_unlock(o->chan);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
- }
- ast_party_connected_line_free(&connected_caller);
+ update_connected_line_from_peer(in, o->chan, 1);
}
}
if (o->aoc_s_rate_list) {
@@ -4785,6 +4822,9 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
}
+ ast_free(o->orig_chan_name);
+ o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
+
ast_channel_req_accountcodes(o->chan, in, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
if (!ast_channel_redirecting(o->chan)->from.number.valid
@@ -4860,22 +4900,21 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
- if (!o->block_connected_update) {
+ if (o->orig_chan_name
+ && strcmp(o->orig_chan_name, ochan_name)) {
+ /*
+ * The channel name changed so we must generate COLP update.
+ * Likely because a call pickup channel masqueraded in.
+ */
+ update_connected_line_from_peer(in, o->chan, 1);
+ } else if (!o->block_connected_update) {
if (o->pending_connected_update) {
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
ast_channel_update_connected_line(in, &o->connected, NULL);
}
} else if (!o->dial_callerid_absent) {
- ast_channel_lock(o->chan);
- ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
- ast_channel_unlock(o->chan);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
- ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
- ast_channel_update_connected_line(in, &connected_caller, NULL);
- }
- ast_party_connected_line_free(&connected_caller);
+ update_connected_line_from_peer(in, o->chan, 1);
}
}
if (o->aoc_s_rate_list) {
@@ -5575,6 +5614,10 @@ struct queue_stasis_data {
struct local_optimization caller_optimize;
/*! Local channel optimization details for the member */
struct local_optimization member_optimize;
+ /*! Member channel */
+ struct ast_channel *member_channel;
+ /*! Caller channel */
+ struct ast_channel *caller_channel;
};
/*!
@@ -5592,6 +5635,9 @@ static void queue_stasis_data_destructor(void *obj)
ao2_cleanup(queue_data->member);
queue_unref(queue_data->queue);
ast_string_field_free_memory(queue_data);
+
+ ao2_ref(queue_data->member_channel, -1);
+ ao2_ref(queue_data->caller_channel, -1);
}
/*!
@@ -5638,6 +5684,16 @@ static struct queue_stasis_data *queue_stasis_data_alloc(struct queue_ent *qe,
queue_data->caller_pos = qe->opos;
ao2_ref(mem, +1);
queue_data->member = mem;
+
+ /*
+ * During transfers it's possible for both the member and/or caller
+ * channel(s) to not be available. Adding a reference here ensures
+ * that the channels remain until app_queue is completely done with
+ * them.
+ */
+ queue_data->member_channel = ao2_bump(peer);
+ queue_data->caller_channel = ao2_bump(qe->chan);
+
return queue_data;
}
@@ -6009,7 +6065,9 @@ static void handle_hangup(void *userdata, struct stasis_subscription *sub,
}
chan = ast_channel_get_by_name(channel_blob->snapshot->name);
- if (chan && ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME)) {
+ if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
+ !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
+ !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
/* Channel that is hanging up is doing it as part of a transfer.
* We'll get a transfer event later
*/
@@ -6983,6 +7041,55 @@ static int publish_queue_member_pause(struct call_queue *q, struct member *membe
return 0;
}
+/*!
+ * \internal
+ * \brief Set the pause status of the specific queue member.
+ *
+ * \param q Which queue the member belongs.
+ * \param mem Queue member being paused/unpaused.
+ * \param reason Why is this happening (Can be NULL/empty for no reason given.)
+ * \param paused Set to 1 if the member is being paused or 0 to unpause.
+ *
+ * \pre The q is locked on entry.
+ *
+ * \return Nothing
+ */
+static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
+{
+ if (mem->paused == paused) {
+ ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
+ (paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
+ }
+
+ if (mem->realtime) {
+ if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
+ ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
+ (paused ? "" : "un"), q->name, mem->interface);
+ }
+ }
+
+ mem->paused = paused;
+ ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
+ AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
+
+ if (queue_persistent_members) {
+ dump_queue_members(q);
+ }
+
+ if (is_member_available(q, mem)) {
+ ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE,
+ "Queue:%s_avail", q->name);
+ } else if (!num_available_members(q)) {
+ ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE,
+ "Queue:%s_avail", q->name);
+ }
+
+ ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"),
+ "%s", S_OR(reason, ""));
+
+ publish_queue_member_pause(q, mem, reason);
+}
+
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
{
int found = 0;
@@ -6996,55 +7103,30 @@ static int set_member_paused(const char *queuename, const char *interface, const
struct member *mem;
if ((mem = interface_exists(q, interface))) {
- if (mem->paused == paused) {
- ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
+ /*
+ * Before we do the PAUSE/UNPAUSE, log if this was a
+ * PAUSEALL/UNPAUSEALL but only on the first found entry.
+ */
+ ++found;
+ if (found == 1
+ && ast_strlen_zero(queuename)) {
+ /*
+ * XXX In all other cases, we use the queue name,
+ * but since this affects all queues, we cannot.
+ */
+ ast_queue_log("NONE", "NONE", mem->membername,
+ (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
}
- if (mem->realtime) {
- if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
- ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
- ao2_ref(mem, -1);
- ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
- continue;
- }
- }
-
- mem->paused = paused;
- ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
- AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
- found++;
-
- /* Before we do the PAUSE/UNPAUSE log, if this was a PAUSEALL/UNPAUSEALL, log that here, but only on the first found entry. */
- if (found == 1) {
-
- /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
- if (ast_strlen_zero(queuename)) {
- ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
- }
- }
-
- if (queue_persistent_members) {
- dump_queue_members(q);
- }
-
- if (is_member_available(q, mem)) {
- ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
- } else if (!num_available_members(q)) {
- ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
- }
-
- ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
-
- publish_queue_member_pause(q, mem, reason);
+ set_queue_member_pause(q, mem, reason, paused);
ao2_ref(mem, -1);
}
- }
- if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
- ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
- break;
+ if (!ast_strlen_zero(queuename)) {
+ ao2_unlock(q);
+ queue_t_unref(q, "Done with iterator");
+ break;
+ }
}
ao2_unlock(q);
@@ -7090,6 +7172,31 @@ static int set_member_penalty_help_members(struct call_queue *q, const char *int
return foundinterface;
}
+/*!
+ * \internal
+ * \brief Set the ringinuse value of the specific queue member.
+ *
+ * \param q Which queue the member belongs.
+ * \param mem Queue member being set.
+ * \param ringinuse Set to 1 if the member is called when inuse.
+ *
+ * \pre The q is locked on entry.
+ *
+ * \return Nothing
+ */
+static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
+{
+ if (mem->realtime) {
+ update_realtime_member_field(mem, q->name, realtime_ringinuse_field,
+ ringinuse ? "1" : "0");
+ }
+
+ mem->ringinuse = ringinuse;
+
+ ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
+ queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
+}
+
static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
{
struct member *mem;
@@ -7098,17 +7205,7 @@ static int set_member_ringinuse_help_members(struct call_queue *q, const char *i
ao2_lock(q);
if ((mem = interface_exists(q, interface))) {
foundinterface++;
- if (mem->realtime) {
- char rtringinuse[80];
-
- sprintf(rtringinuse, "%i", ringinuse);
- update_realtime_member_field(mem, q->name, realtime_ringinuse_field, rtringinuse);
- }
-
- mem->ringinuse = ringinuse;
-
- ast_queue_log(q->name, "NONE", interface, "RINGINUSE", "%d", ringinuse);
- queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
+ set_queue_member_ringinuse(q, mem, ringinuse);
ao2_ref(mem, -1);
}
ao2_unlock(q);
@@ -8019,12 +8116,29 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
return 0;
}
+static struct member *get_interface_helper(struct call_queue *q, const char *interface)
+{
+ struct member *m;
+
+ if (ast_strlen_zero(interface)) {
+ ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
+ return NULL;
+ }
+
+ m = interface_exists(q, interface);
+ if (!m) {
+ ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
+ interface, q->name);
+ }
+ return m;
+}
+
/*!
* \brief Get number either busy / free / ready or total members of a specific queue
* \brief Get or set member properties penalty / paused / ringinuse
* \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
* \retval -1 on error
-*/
+ */
static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int count = 0;
@@ -8041,14 +8155,18 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
buf[0] = '\0';
if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
+ ast_log(LOG_ERROR,
+ "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
+ cmd);
return -1;
}
AST_STANDARD_APP_ARGS(args, data);
- if (args.argc < 2) {
- ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
+ if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
+ ast_log(LOG_ERROR,
+ "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
+ cmd);
return -1;
}
@@ -8087,27 +8205,29 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
+ } else if (!strcasecmp(args.option, "count")) {
count = ao2_container_count(q->members);
- } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
- ((m = interface_exists(q, args.interface)))) {
- count = m->penalty;
- ao2_ref(m, -1);
- } else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
- ((m = interface_exists(q, args.interface)))) {
- count = m->paused;
- ao2_ref(m, -1);
- } else if ( (!strcasecmp(args.option, "ignorebusy") || !strcasecmp(args.option, "ringinuse")) &&
- !ast_strlen_zero(args.interface) &&
- ((m = interface_exists(q, args.interface)))) {
- count = m->ringinuse;
- ao2_ref(m, -1);
- } else if (!ast_strlen_zero(args.interface)) {
- ast_log(LOG_ERROR, "Queue member interface %s not in queue %s\n",
- args.interface, args.queuename);
+ } else if (!strcasecmp(args.option, "penalty")) {
+ m = get_interface_helper(q, args.interface);
+ if (m) {
+ count = m->penalty;
+ ao2_ref(m, -1);
+ }
+ } else if (!strcasecmp(args.option, "paused")) {
+ m = get_interface_helper(q, args.interface);
+ if (m) {
+ count = m->paused;
+ ao2_ref(m, -1);
+ }
+ } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
+ || !strcasecmp(args.option, "ringinuse"))) {
+ m = get_interface_helper(q, args.interface);
+ if (m) {
+ count = m->ringinuse;
+ ao2_ref(m, -1);
+ }
} else {
- ast_log(LOG_ERROR, "Unknown option %s provided to %s, valid values are: "
- "logged, free, ready, count, penalty, paused, ringinuse\n", args.option, cmd);
+ ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
}
ao2_unlock(q);
queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
@@ -8124,9 +8244,6 @@ static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, ch
static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
int memvalue;
- struct call_queue *q;
- struct member *m;
- char rtvalue[80];
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(queuename);
@@ -8135,65 +8252,48 @@ static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, c
);
if (ast_strlen_zero(data)) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
+ ast_log(LOG_ERROR,
+ "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
+ cmd);
return -1;
}
AST_STANDARD_APP_ARGS(args, data);
- if (args.argc < 3) {
- ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ if (ast_strlen_zero(args.option)
+ || ast_strlen_zero(args.interface)) {
+ ast_log(LOG_ERROR,
+ "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
+ cmd);
return -1;
}
- if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
- ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
- return -1;
- }
+ /*
+ * If queuename is empty then the option will be
+ * set for the interface in all queues.
+ */
memvalue = atoi(value);
if (!strcasecmp(args.option, "penalty")) {
- /* if queuename = NULL then penalty will be set for interface in all the queues.*/
if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
- ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+ ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
return -1;
}
- } else if ((q = find_load_queue_rt_friendly(args.queuename))) {
- ao2_lock(q);
- if ((m = interface_exists(q, args.interface))) {
- sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
- if (!strcasecmp(args.option, "paused")) {
- if (m->realtime) {
- update_realtime_member_field(m, q->name, args.option, rtvalue);
- }
- m->paused = (memvalue <= 0) ? 0 : 1;
- ast_devstate_changed(m->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
- AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, args.interface);
-
- } else if ((!strcasecmp(args.option, "ignorebusy")) || (!strcasecmp(args.option, "ringinuse"))) {
- if (m->realtime) {
- update_realtime_member_field(m, q->name, args.option, rtvalue);
- }
-
- m->ringinuse = (memvalue <= 0) ? 0 : 1;
- } else {
- ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ringinuse/ignorebusy are valid\n");
- ao2_ref(m, -1);
- ao2_unlock(q);
- ao2_ref(q, -1);
- return -1;
- }
- ao2_ref(m, -1);
- } else {
- ao2_unlock(q);
- ao2_ref(q, -1);
- ast_log(LOG_ERROR, "Invalid interface for queue\n");
+ } else if (!strcasecmp(args.option, "paused")) {
+ memvalue = (memvalue <= 0) ? 0 : 1;
+ if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
+ ast_log(LOG_ERROR, "Invalid interface or queue\n");
return -1;
}
- ao2_unlock(q);
- ao2_ref(q, -1);
- } else {
- ast_log(LOG_ERROR, "Invalid queue\n");
+ } else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
+ || !strcasecmp(args.option, "ringinuse")) {
+ memvalue = (memvalue <= 0) ? 0 : 1;
+ if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
+ ast_log(LOG_ERROR, "Invalid interface or queue\n");
+ return -1;
+ }
+ } else {
+ ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
return -1;
}
return 0;
diff --git a/apps/app_record.c b/apps/app_record.c
index ed54a7789..fcd893876 100644
--- a/apps/app_record.c
+++ b/apps/app_record.c
@@ -294,9 +294,10 @@ static int record_exec(struct ast_channel *chan, const char *data)
}
count++;
} while (ast_fileexists(tmp, ext, ast_channel_language(chan)) > 0);
- pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
} else
ast_copy_string(tmp, args.filename, sizeof(tmp));
+
+ pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
/* end of routine mentioned */
if (ast_channel_state(chan) != AST_STATE_UP) {