diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/app_dial.c | 156 | ||||
-rw-r--r-- | apps/app_page.c | 28 | ||||
-rw-r--r-- | apps/app_queue.c | 412 | ||||
-rw-r--r-- | apps/app_record.c | 3 |
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) { |