diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/app_dial.c | 4 | ||||
-rw-r--r-- | apps/app_followme.c | 287 |
2 files changed, 208 insertions, 83 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c index 2c7f50a9e..1e06b28f1 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -203,8 +203,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>Asterisk will ignore any forwarding requests it may receive on this dial attempt.</para> </option> <option name="I"> - <para>Asterisk will ignore any connected line update requests or redirecting party update - requests it may receiveon this dial attempt.</para> + <para>Asterisk will ignore any connected line update requests or any redirecting party + update requests it may receive on this dial attempt.</para> </option> <option name="k"> <para>Allow the called party to enable parking of the call by sending diff --git a/apps/app_followme.c b/apps/app_followme.c index d3e8f4778..65787f92d 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -67,31 +67,41 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <parameter name="followmeid" required="true" /> <parameter name="options"> <optionlist> - <option name="s"> - <para>Playback the incoming status message prior to starting - the follow-me step(s)</para> - </option> <option name="a"> <para>Record the caller's name so it can be announced to the callee on each step.</para> </option> - <option name="n"> - <para>Playback the unreachable status message if we've run out - of steps to reach the or the callee has elected not to be reachable.</para> - </option> - <option name="N"> - <para>Don't answer the incoming call until we're ready to - connect the caller or give up. This will disable all the other - options while implicitly turning on the 'd' option.</para> - </option> <option name="d"> <para>Disable the 'Please hold while we try to connect your call' announcement.</para> </option> + <option name="I"> + <para>Asterisk will ignore any connected line update requests + it may receive on this dial attempt.</para> + </option> <option name="l"> <para>Disable local call optimization so that applications with audio hooks between the local bridge don't get dropped when the calls get joined directly.</para> </option> + <option name="N"> + <para>Don't answer the incoming call until we're ready to + connect the caller or give up.</para> + <note> + <para>This option is ignored if the call is already answered.</para> + </note> + <note> + <para>If the call is not already answered, the 'a' and 's' + options are ignored while the 'd' option is implicitly enabled.</para> + </note> + </option> + <option name="n"> + <para>Playback the unreachable status message if we've run out + of steps or the callee has elected not to be reachable.</para> + </option> + <option name="s"> + <para>Playback the incoming status message prior to starting + the follow-me step(s)</para> + </option> </optionlist> </parameter> </syntax> @@ -140,13 +150,23 @@ struct call_followme { }; struct fm_args { + /*! Inbound (caller) channel */ struct ast_channel *chan; char *mohclass; AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers; + /*! Winning outbound (callee) channel */ + struct ast_channel *outbound; + /*! Accumulated connected line information from inbound call. */ + struct ast_party_connected_line connected_in; + /*! Accumulated connected line information from outbound call. */ + struct ast_party_connected_line connected_out; + /*! TRUE if connected line information from inbound call changed. */ + int pending_in_connected_update:1; + /*! TRUE if connected line information from outbound call is available. */ + int pending_out_connected_update:1; int status; char context[AST_MAX_CONTEXT]; char namerecloc[AST_MAX_CONTEXT]; - struct ast_channel *outbound; char takecall[20]; /*!< Digit mapping to take a call */ char nextindp[20]; /*!< Digit mapping to decline a call */ char callfromprompt[PATH_MAX]; /*!< Sound prompt name and path */ @@ -160,12 +180,17 @@ struct fm_args { struct findme_user { struct ast_channel *ochan; + /*! Accumulated connected line information from outgoing call. */ + struct ast_party_connected_line connected; + long digts; + int ynidx; int state; char dialarg[256]; char yn[10]; - int ynidx; - long digts; - int cleared; + /*! TRUE if call cleared. */ + int cleared:1; + /*! TRUE if connected line information is available. */ + int pending_connected_update:1; AST_LIST_ENTRY(findme_user) entry; }; @@ -176,15 +201,17 @@ enum { FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3), FOLLOWMEFLAG_NOANSWER = (1 << 4), FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5), + FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6), }; AST_APP_OPTIONS(followme_opts, { - AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ), - AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ), - AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ), - AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT ), - AST_APP_OPTION('N', FOLLOWMEFLAG_NOANSWER ), - AST_APP_OPTION('l', FOLLOWMEFLAG_DISABLEOPTIMIZATION ), + AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME), + AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT), + AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE), + AST_APP_OPTION('l', FOLLOWMEFLAG_DISABLEOPTIMIZATION), + AST_APP_OPTION('N', FOLLOWMEFLAG_NOANSWER), + AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG), + AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG), }); static int ynlongest = 0; @@ -538,13 +565,15 @@ static void destroy_calling_tree(struct findme_user_listptr *findme_user_list) if (!fmuser->cleared) { clear_caller(fmuser); } + ast_party_connected_line_free(&fmuser->connected); ast_free(fmuser); } ast_free(findme_user_list); } -static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, int *status, struct fm_args *tpargs) +static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs) { + struct ast_party_connected_line connected; struct ast_channel *watchers[256]; int pos; struct ast_channel *winner; @@ -670,12 +699,21 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us } if (winner) { /* Need to find out which channel this is */ - dg = 0; - while ((winner != watchers[dg]) && (dg < 256)) - dg++; - AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) - if (tmpuser->ochan == winner) + for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) { + if (winner == watchers[dg]) { break; + } + } + if (dg) { + /* The winner is an outgoing channel. */ + AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { + if (tmpuser->ochan == winner) { + break; + } + } + } else { + tmpuser = NULL; + } f = ast_read(winner); if (f) { if (f->frametype == AST_FRAME_CONTROL) { @@ -729,32 +767,73 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us ast_verb(3, "%s is ringing\n", ast_channel_name(winner)); break; case AST_CONTROL_PROGRESS: - ast_verb(3, "%s is making progress passing it to %s\n", ast_channel_name(winner), ast_channel_name(caller)); + ast_verb(3, "%s is making progress\n", ast_channel_name(winner)); break; case AST_CONTROL_VIDUPDATE: - ast_verb(3, "%s requested a video update, passing it to %s\n", ast_channel_name(winner), ast_channel_name(caller)); + ast_verb(3, "%s requested a video update\n", ast_channel_name(winner)); break; case AST_CONTROL_SRCUPDATE: - ast_verb(3, "%s requested a source update, passing it to %s\n", ast_channel_name(winner), ast_channel_name(caller)); + ast_verb(3, "%s requested a source update\n", ast_channel_name(winner)); break; case AST_CONTROL_PROCEEDING: - ast_verb(3, "%s is proceeding passing it to %s\n", ast_channel_name(winner),ast_channel_name(caller)); + ast_verb(3, "%s is proceeding\n", ast_channel_name(winner)); break; case AST_CONTROL_HOLD: - ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(winner)); + ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner)); break; case AST_CONTROL_UNHOLD: - ast_verb(3, "Call on %s left from hold\n", ast_channel_name(winner)); + ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner)); break; case AST_CONTROL_OFFHOOK: case AST_CONTROL_FLASH: /* Ignore going off hook and flash */ break; + case AST_CONTROL_CONNECTED_LINE: + if (!tmpuser) { + /* + * Hold connected line update from caller until we have a + * winner. + */ + ast_verb(3, + "%s connected line has changed. Saving it until we have a winner.\n", + ast_channel_name(winner)); + ast_party_connected_line_set_init(&connected, &tpargs->connected_in); + if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) { + ast_party_connected_line_set(&tpargs->connected_in, + &connected, NULL); + tpargs->pending_in_connected_update = 1; + } + ast_party_connected_line_free(&connected); + break; + } + if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) { + ast_verb(3, "Connected line update from %s prevented.\n", + ast_channel_name(winner)); + } else { + ast_verb(3, + "%s connected line has changed. Saving it until answer.\n", + ast_channel_name(winner)); + ast_party_connected_line_set_init(&connected, &tmpuser->connected); + if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) { + ast_party_connected_line_set(&tmpuser->connected, + &connected, NULL); + tmpuser->pending_connected_update = 1; + } + ast_party_connected_line_free(&connected); + } + break; + case AST_CONTROL_REDIRECTING: + /* + * Ignore because we are masking the FollowMe search progress to + * the caller. + */ + break; case -1: ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner)); break; default: - ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer); + ast_debug(1, "Dunno what to do with control type %d from %s\n", + f->subclass.integer, ast_channel_name(winner)); break; } } @@ -775,36 +854,35 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us } if (!strcmp(tmpuser->yn, tpargs->nextindp)) { ast_debug(1, "Next in dial plan step requested.\n"); - *status = 1; ast_frfree(f); return NULL; } - } } ast_frfree(f); } else { - if (winner) { - ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg); - if (!dg) { - clear_calling_tree(findme_user_list); + ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg); + if (!dg) { + /* Caller hung up. */ + clear_calling_tree(findme_user_list); + return NULL; + } else { + /* Outgoing channel hung up. */ + tmpuser->state = -1; + tmpuser->ochan = NULL; + ast_hangup(winner); + --livechannels; + ast_debug(1, "live channels left %d\n", livechannels); + if (!livechannels) { + ast_verb(3, "no live channels left. exiting.\n"); return NULL; - } else { - tmpuser->state = -1; - ast_hangup(winner); - livechannels--; - ast_debug(1, "live channels left %d\n", livechannels); - if (!livechannels) { - ast_verb(3, "no live channels left. exiting.\n"); - return NULL; - } } } } - - } else + } else { ast_debug(1, "timed out waiting for action\n"); + } } /* --- WAIT FOR WINNER NUMBER END! -----------*/ @@ -824,7 +902,6 @@ static void findmeexec(struct fm_args *tpargs) struct findme_user *tmpuser; struct findme_user *fmuser; struct findme_user_listptr *findme_user_list; - int status; findme_user_list = ast_calloc(1, sizeof(*findme_user_list)); AST_LIST_HEAD_INIT_NOLOCK(findme_user_list); @@ -839,7 +916,7 @@ static void findmeexec(struct fm_args *tpargs) } caller = tpargs->chan; - for (idx = 1; !winner && !ast_check_hangup(caller); ++idx) { + for (idx = 1; !ast_check_hangup(caller); ++idx) { /* Find next followme numbers to dial. */ AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) { if (nm->order == idx) { @@ -930,16 +1007,29 @@ static void findmeexec(struct fm_args *tpargs) continue; } - status = 0; - winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs); + winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs); + if (!winner) { + continue; + } - /* Clean up all calls but winner. */ + /* Destroy losing calls up to the winner. The rest will be destroyed later. */ while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) { - if (!fmuser->cleared && fmuser->ochan != winner) { - clear_caller(fmuser); + if (fmuser->ochan == winner) { + /* Pass any connected line info up. */ + tpargs->connected_out = fmuser->connected; + tpargs->pending_out_connected_update = fmuser->pending_connected_update; + ast_free(fmuser); + break; + } else { + /* Destroy losing call. */ + if (!fmuser->cleared) { + clear_caller(fmuser); + } + ast_party_connected_line_free(&fmuser->connected); + ast_free(fmuser); } - ast_free(fmuser); } + break; } destroy_calling_tree(findme_user_list); if (!winner) { @@ -1066,7 +1156,6 @@ static int app_exec(struct ast_channel *chan, const char *data) int res = 0; char *argstr; char namerecloc[255]; - int duration = 0; struct ast_channel *caller; struct ast_channel *outbound; AST_DECLARE_APP_ARGS(args, @@ -1133,27 +1222,36 @@ static int app_exec(struct ast_channel *chan, const char *data) } ast_mutex_unlock(&f->lock); - snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid); - duration = 5; - - if (!ast_fileexists(namerecloc, NULL, chan->language)) - ast_copy_string(namerecloc, "", sizeof(namerecloc)); + /* Forget the 'N' option if the call is already up. */ + if (chan->_state == AST_STATE_UP) { + ast_clear_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER); + } + namerecloc[0] = '\0'; if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) { - if (chan->_state != AST_STATE_UP) { - ast_indicate(chan, AST_CONTROL_RINGING); - } + ast_indicate(chan, AST_CONTROL_RINGING); } else { /* Answer the call */ - if (chan->_state != AST_STATE_UP) + if (chan->_state != AST_STATE_UP) { ast_answer(chan); + } if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) ast_stream_and_wait(chan, targs.statusprompt, ""); - if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) - if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) + if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) { + int duration = 5; + + snprintf(namerecloc, sizeof(namerecloc), "%s/followme.%s", + ast_config_AST_SPOOL_DIR, chan->uniqueid); + if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, + NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) { goto outrun; + } + if (!ast_fileexists(namerecloc, NULL, chan->language)) { + namerecloc[0] = '\0'; + } + } if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) { if (ast_streamfile(chan, targs.plsholdprompt, chan->language)) @@ -1167,6 +1265,9 @@ static int app_exec(struct ast_channel *chan, const char *data) targs.status = 0; targs.chan = chan; ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc)); + ast_channel_lock(chan); + ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller); + ast_channel_unlock(chan); findmeexec(&targs); @@ -1176,15 +1277,15 @@ static int app_exec(struct ast_channel *chan, const char *data) if (!ast_strlen_zero(namerecloc)) unlink(namerecloc); - if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) { - if (chan->_state != AST_STATE_UP) { - ast_answer(chan); + if (targs.status != 100) { + if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) { + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + } else { + ast_moh_stop(chan); } - } else { - ast_moh_stop(chan); - } - if (targs.status != 100) { if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) ast_stream_and_wait(chan, targs.sorryprompt, ""); res = 0; @@ -1201,6 +1302,21 @@ static int app_exec(struct ast_channel *chan, const char *data) config.end_bridge_callback_data = chan; config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; + /* Update connected line to caller if available. */ + if (targs.pending_out_connected_update) { + if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) { + ast_channel_update_connected_line(caller, &targs.connected_out, NULL); + } + } + + if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) { + if (caller->_state != AST_STATE_UP) { + ast_answer(caller); + } + } else { + ast_moh_stop(caller); + } + /* Be sure no generators are left on it */ ast_deactivate_generator(caller); /* Make sure channels are compatible */ @@ -1210,12 +1326,21 @@ static int app_exec(struct ast_channel *chan, const char *data) ast_hangup(outbound); goto outrun; } + + /* Update connected line to winner if changed. */ + if (targs.pending_in_connected_update) { + if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) { + ast_channel_update_connected_line(outbound, &targs.connected_in, NULL); + } + } + res = ast_bridge_call(caller, outbound, &config); ast_hangup(outbound); } outrun: - + ast_party_connected_line_free(&targs.connected_in); + ast_party_connected_line_free(&targs.connected_out); if (f->realtime) { /* Not in list */ free_numbers(f); |