Find-Me Follow-Me application. More...
#include "asterisk.h"#include <signal.h>#include "asterisk/paths.h"#include "asterisk/lock.h"#include "asterisk/file.h"#include "asterisk/channel.h"#include "asterisk/pbx.h"#include "asterisk/module.h"#include "asterisk/translate.h"#include "asterisk/say.h"#include "asterisk/features.h"#include "asterisk/musiconhold.h"#include "asterisk/cli.h"#include "asterisk/manager.h"#include "asterisk/config.h"#include "asterisk/utils.h"#include "asterisk/causes.h"#include "asterisk/astdb.h"#include "asterisk/dsp.h"#include "asterisk/app.h"
Go to the source code of this file.
Data Structures | |
| struct | call_followme::blnumbers |
| struct | call_followme |
| Data structure for followme scripts. More... | |
| struct | fm_args::cnumbers |
| struct | findme_user |
| struct | findme_user_listptr |
| struct | fm_args |
| struct | followmes |
| struct | number |
| Number structure. More... | |
| struct | call_followme::numbers |
| struct | call_followme::wlnumbers |
Defines | |
| #define | MAX_YN_STRING 20 |
Enumerations | |
| enum | { FOLLOWMEFLAG_STATUSMSG = (1 << 0), FOLLOWMEFLAG_RECORDNAME = (1 << 1), FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2), FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3), FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4) } |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static struct call_followme * | alloc_profile (const char *fmname) |
| Allocate and initialize followme profile. | |
| static int | app_exec (struct ast_channel *chan, const char *data) |
| static void | clear_caller (struct findme_user *tmpuser) |
| static void | clear_calling_tree (struct findme_user_listptr *findme_user_list) |
| static struct number * | create_followme_number (const char *number, int timeout, int numorder) |
| Add a new number. | |
| static void | destroy_calling_tree (struct findme_user_listptr *findme_user_list) |
| static void | end_bridge_callback (void *data) |
| static void | end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) |
| static struct call_followme * | find_realtime (const char *name) |
| static void | findmeexec (struct fm_args *tpargs) |
| static void | free_numbers (struct call_followme *f) |
| static void | init_profile (struct call_followme *f) |
| static int | load_module (void) |
| static void | profile_set_param (struct call_followme *f, const char *param, const char *val, int linenum, int failunknown) |
| Set parameter in profile from configuration file. | |
| static int | reload (void) |
| static int | reload_followme (int reload) |
| Reload followme application module. | |
| static int | unload_module (void) |
| 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) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } |
| static char * | app = "FollowMe" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static char | callfromprompt [PATH_MAX] = "followme/call-from" |
| static const char * | defaultmoh = "default" |
| static int | featuredigittimeout = 5000 |
| static const char * | featuredigittostr |
| static struct ast_app_option | followme_opts [128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG },} |
| static struct followmes | followmes |
| static char | nextindp [MAX_YN_STRING] = "2" |
| static char | norecordingprompt [PATH_MAX] = "followme/no-recording" |
| static char | optionsprompt [PATH_MAX] = "followme/options" |
| static char | plsholdprompt [PATH_MAX] = "followme/pls-hold-while-try" |
| static char | sorryprompt [PATH_MAX] = "followme/sorry" |
| static char | statusprompt [PATH_MAX] = "followme/status" |
| static char | takecall [MAX_YN_STRING] = "1" |
Find-Me Follow-Me application.
Definition in file app_followme.c.
| #define MAX_YN_STRING 20 |
Maximum accept/decline DTMF string plus terminator.
Definition at line 106 of file app_followme.c.
| anonymous enum |
| FOLLOWMEFLAG_STATUSMSG | |
| FOLLOWMEFLAG_RECORDNAME | |
| FOLLOWMEFLAG_UNREACHABLEMSG | |
| FOLLOWMEFLAG_DISABLEHOLDPROMPT | |
| FOLLOWMEFLAG_IGNORE_CONNECTEDLINE |
Definition at line 185 of file app_followme.c.
{
FOLLOWMEFLAG_STATUSMSG = (1 << 0),
FOLLOWMEFLAG_RECORDNAME = (1 << 1),
FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4),
};
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1342 of file app_followme.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1342 of file app_followme.c.
| static struct call_followme* alloc_profile | ( | const char * | fmname | ) | [static, read] |
Allocate and initialize followme profile.
Definition at line 241 of file app_followme.c.
References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, call_followme::blnumbers, call_followme::callfromprompt, call_followme::context, f, call_followme::lock, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::numbers, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, call_followme::takecall, and call_followme::wlnumbers.
Referenced by find_realtime(), and reload_followme().
{
struct call_followme *f;
if (!(f = ast_calloc(1, sizeof(*f))))
return NULL;
ast_mutex_init(&f->lock);
ast_copy_string(f->name, fmname, sizeof(f->name));
f->moh[0] = '\0';
f->context[0] = '\0';
ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
return f;
}
| static int app_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 1126 of file app_followme.c.
References ast_channel::_state, call_followme::active, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_connected_line_macro(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_unlock, ast_channel_update_connected_line(), ast_config_AST_SPOOL_DIR, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_get_threshold_from_settings(), AST_FEATURE_AUTOMON, AST_FEATURE_REDIRECT, ast_fileexists(), ast_free, ast_hangup(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_moh_start(), ast_moh_stop(), ast_mutex_lock, ast_mutex_unlock, ast_party_connected_line_free(), ast_play_and_record(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_channel::caller, call_followme::callfromprompt, fm_args::callfromprompt, fm_args::chan, fm_args::cnumbers, fm_args::connected_in, fm_args::connected_out, call_followme::context, fm_args::context, create_followme_number(), ast_bridge_config::end_bridge_callback, end_bridge_callback(), ast_bridge_config::end_bridge_callback_data, ast_bridge_config::end_bridge_callback_data_fixup, end_bridge_callback_data_fixup(), f, ast_bridge_config::features_callee, ast_bridge_config::features_caller, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), ast_channel::language, call_followme::lock, LOG_WARNING, call_followme::moh, fm_args::mohclass, call_followme::name, ast_channel::name, fm_args::namerecloc, call_followme::nextindp, fm_args::nextindp, call_followme::norecordingprompt, fm_args::norecordingprompt, number::number, call_followme::numbers, call_followme::optionsprompt, fm_args::optionsprompt, number::order, fm_args::outbound, fm_args::pending_in_connected_update, fm_args::pending_out_connected_update, call_followme::plsholdprompt, fm_args::plsholdprompt, call_followme::realtime, S_OR, call_followme::sorryprompt, fm_args::sorryprompt, fm_args::status, call_followme::statusprompt, fm_args::statusprompt, call_followme::takecall, fm_args::takecall, THRESHOLD_SILENCE, number::timeout, and ast_channel::uniqueid.
Referenced by load_module().
{
struct fm_args targs = { 0, };
struct ast_bridge_config config;
struct call_followme *f;
struct number *nm, *newnm;
int res = 0;
char *argstr;
struct ast_channel *caller;
struct ast_channel *outbound;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(followmeid);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
return -1;
}
argstr = ast_strdupa((char *) data);
AST_STANDARD_APP_ARGS(args, argstr);
if (ast_strlen_zero(args.followmeid)) {
ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
return -1;
}
AST_RWLIST_RDLOCK(&followmes);
AST_RWLIST_TRAVERSE(&followmes, f, entry) {
if (!strcasecmp(f->name, args.followmeid) && (f->active))
break;
}
AST_RWLIST_UNLOCK(&followmes);
ast_debug(1, "New profile %s.\n", args.followmeid);
if (!f) {
f = find_realtime(args.followmeid);
}
if (!f) {
ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
return 0;
}
/* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
if (args.options)
ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
/* Lock the profile lock and copy out everything we need to run with before unlocking it again */
ast_mutex_lock(&f->lock);
targs.mohclass = ast_strdupa(f->moh);
ast_copy_string(targs.context, f->context, sizeof(targs.context));
ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
/* Copy the numbers we're going to use into another list in case the master list should get modified
(and locked) while we're trying to do a follow-me */
AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
newnm = create_followme_number(nm->number, nm->timeout, nm->order);
if (newnm) {
AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
}
}
ast_mutex_unlock(&f->lock);
/* Answer the call */
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)) {
int duration = 5;
snprintf(targs.namerecloc, sizeof(targs.namerecloc), "%s/followme.%s",
ast_config_AST_SPOOL_DIR, chan->uniqueid);
if (ast_play_and_record(chan, "vm-rec-name", targs.namerecloc, 5, "sln", &duration,
NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
goto outrun;
}
if (!ast_fileexists(targs.namerecloc, NULL, chan->language)) {
targs.namerecloc[0] = '\0';
}
}
if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
goto outrun;
if (ast_waitstream(chan, "") < 0)
goto outrun;
}
ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
targs.status = 0;
targs.chan = chan;
ast_channel_lock(chan);
ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller);
ast_channel_unlock(chan);
findmeexec(&targs);
if (targs.status != 100) {
ast_moh_stop(chan);
if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
ast_stream_and_wait(chan, targs.sorryprompt, "");
res = 0;
} else {
caller = chan;
outbound = targs.outbound;
/* Bridge the two channels. */
memset(&config, 0, sizeof(config));
ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
config.end_bridge_callback = end_bridge_callback;
config.end_bridge_callback_data = chan;
config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
ast_moh_stop(caller);
/* Be sure no generators are left on it */
ast_deactivate_generator(caller);
/* Make sure channels are compatible */
res = ast_channel_make_compatible(caller, outbound);
if (res < 0) {
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
ast_hangup(outbound);
goto outrun;
}
/* 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);
}
}
/* 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:
while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry))) {
ast_free(nm);
}
if (!ast_strlen_zero(targs.namerecloc)) {
unlink(targs.namerecloc);
}
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);
ast_free(f);
}
return res;
}
| static void clear_caller | ( | struct findme_user * | tmpuser | ) | [static] |
Definition at line 499 of file app_followme.c.
References ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_lock, ast_channel_unlock, ast_hangup(), ast_log(), ast_channel::cdr, findme_user::dialarg, ast_channel::hangupcause, LOG_WARNING, findme_user::ochan, and findme_user::state.
Referenced by clear_calling_tree(), destroy_calling_tree(), and findmeexec().
{
struct ast_channel *outbound;
if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
outbound = tmpuser->ochan;
ast_channel_lock(outbound);
if (!outbound->cdr) {
outbound->cdr = ast_cdr_alloc();
if (outbound->cdr) {
ast_cdr_init(outbound->cdr, outbound);
}
}
if (outbound->cdr) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
ast_cdr_update(outbound);
ast_cdr_start(outbound->cdr);
ast_cdr_end(outbound->cdr);
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
ast_cdr_failed(outbound->cdr);
}
} else {
ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
}
ast_channel_unlock(outbound);
ast_hangup(outbound);
tmpuser->ochan = NULL;
}
}
| static void clear_calling_tree | ( | struct findme_user_listptr * | findme_user_list | ) | [static] |
Definition at line 533 of file app_followme.c.
References AST_LIST_TRAVERSE, clear_caller(), and findme_user::cleared.
Referenced by wait_for_winner().
{
struct findme_user *tmpuser;
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
clear_caller(tmpuser);
tmpuser->cleared = 1;
}
}
| static struct number* create_followme_number | ( | const char * | number, |
| int | timeout, | ||
| int | numorder | ||
| ) | [static, read] |
Add a new number.
Definition at line 307 of file app_followme.c.
References ast_calloc, ast_copy_string(), ast_debug, number::number, number::order, and number::timeout.
Referenced by app_exec(), find_realtime(), and reload_followme().
{
struct number *cur;
char *buf = ast_strdupa(number);
char *tmp;
if (!(cur = ast_calloc(1, sizeof(*cur))))
return NULL;
cur->timeout = timeout;
if ((tmp = strchr(buf, ',')))
*tmp = '\0';
ast_copy_string(cur->number, buf, sizeof(cur->number));
cur->order = numorder;
ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
return cur;
}
| static void destroy_calling_tree | ( | struct findme_user_listptr * | findme_user_list | ) | [static] |
Definition at line 543 of file app_followme.c.
References ast_free, AST_LIST_REMOVE_HEAD, ast_party_connected_line_free(), clear_caller(), findme_user::cleared, and findme_user::connected.
Referenced by findmeexec().
{
struct findme_user *fmuser;
while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
if (!fmuser->cleared) {
clear_caller(fmuser);
}
ast_party_connected_line_free(&fmuser->connected);
ast_free(fmuser);
}
ast_free(findme_user_list);
}
| static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 1100 of file app_followme.c.
References ast_cdr::answer, ast_channel_lock, ast_channel_unlock, ast_channel::cdr, ast_channel::data, pbx_builtin_setvar_helper(), and ast_cdr::start.
Referenced by app_exec().
{
char buf[80];
time_t end;
struct ast_channel *chan = data;
time(&end);
ast_channel_lock(chan);
if (chan->cdr->answer.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
}
if (chan->cdr->start.tv_sec) {
snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
}
ast_channel_unlock(chan);
}
| static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, |
| struct ast_channel * | originator, | ||
| struct ast_channel * | terminator | ||
| ) | [static] |
Definition at line 1121 of file app_followme.c.
References ast_bridge_config::end_bridge_callback_data.
Referenced by app_exec().
{
bconfig->end_bridge_callback_data = originator;
}
| static struct call_followme* find_realtime | ( | const char * | name | ) | [static, read] |
Definition at line 1019 of file app_followme.c.
References alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_false(), ast_free, AST_LIST_INSERT_TAIL, ast_load_realtime(), ast_load_realtime_multientry(), ast_mutex_destroy, ast_str_buffer(), ast_str_create(), ast_str_set(), ast_variable_retrieve(), ast_variables_destroy(), create_followme_number(), call_followme::lock, ast_variable::name, ast_variable::next, call_followme::numbers, profile_set_param(), call_followme::realtime, SENTINEL, str, number::timeout, ast_variable::value, and var.
Referenced by app_exec().
{
struct ast_variable *var;
struct ast_variable *v;
struct ast_config *cfg;
const char *catg;
struct call_followme *new_follower;
struct ast_str *str;
str = ast_str_create(16);
if (!str) {
return NULL;
}
var = ast_load_realtime("followme", "name", name, SENTINEL);
if (!var) {
ast_free(str);
return NULL;
}
if (!(new_follower = alloc_profile(name))) {
ast_variables_destroy(var);
ast_free(str);
return NULL;
}
for (v = var; v; v = v->next) {
if (!strcasecmp(v->name, "active")) {
if (ast_false(v->value)) {
ast_mutex_destroy(&new_follower->lock);
ast_free(new_follower);
ast_variables_destroy(var);
ast_free(str);
return NULL;
}
} else {
profile_set_param(new_follower, v->name, v->value, 0, 0);
}
}
ast_variables_destroy(var);
new_follower->realtime = 1;
/* Load numbers */
cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
name, SENTINEL);
if (!cfg) {
ast_mutex_destroy(&new_follower->lock);
ast_free(new_follower);
ast_free(str);
return NULL;
}
for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
const char *numstr;
const char *timeoutstr;
const char *ordstr;
int timeout;
struct number *cur;
if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
continue;
}
if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
|| sscanf(timeoutstr, "%30d", &timeout) != 1
|| timeout < 1) {
timeout = 25;
}
/* This one has to exist; it was part of the query */
ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
ast_str_set(&str, 0, "%s", numstr);
if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
}
}
ast_config_destroy(cfg);
ast_free(str);
return new_follower;
}
| static void findmeexec | ( | struct fm_args * | tpargs | ) | [static] |
Definition at line 873 of file app_followme.c.
References accountcode, ast_channel::accountcode, ast_best_codec(), ast_call(), ast_calloc, ast_cause2str(), ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, ast_hangup(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_party_connected_line_free(), ast_request(), ast_string_field_set, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, clear_caller(), findme_user::cleared, fm_args::cnumbers, findme_user::connected, ast_channel::connected, fm_args::connected_out, fm_args::context, destroy_calling_tree(), findme_user::dialarg, ast_channel::hangupcause, ast_party_caller::id, language, ast_channel::language, LOG_ERROR, LOG_WARNING, musicclass, ast_channel::musicclass, fm_args::namerecloc, ast_channel::nativeformats, number::number, ast_party_id::number, findme_user::ochan, number::order, fm_args::outbound, findme_user::pending_connected_update, fm_args::pending_out_connected_update, S_COR, findme_user::state, fm_args::status, ast_party_number::str, number::timeout, ast_party_number::valid, and wait_for_winner().
Referenced by app_exec().
{
struct number *nm;
struct ast_channel *outbound;
struct ast_channel *caller;
struct ast_channel *winner = NULL;
char dialarg[512];
char num[512];
int dg, idx;
char *rest, *number;
struct findme_user *tmpuser;
struct findme_user *fmuser;
struct findme_user_listptr *findme_user_list;
findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
if (!findme_user_list) {
ast_log(LOG_WARNING, "Failed to allocate memory for findme_user_list\n");
return;
}
AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
caller = tpargs->chan;
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) {
break;
}
}
if (!nm) {
break;
}
ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
ast_copy_string(num, nm->number, sizeof(num));
for (number = num; number; number = rest) {
rest = strchr(number, '&');
if (rest) {
*rest++ = 0;
}
/* We check if the extension exists, before creating the ast_channel struct */
if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
continue;
}
if (!strcmp(tpargs->context, "")) {
snprintf(dialarg, sizeof(dialarg), "%s", number);
} else {
snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
}
tmpuser = ast_calloc(1, sizeof(*tmpuser));
if (!tmpuser) {
continue;
}
outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
if (outbound) {
ast_channel_lock_both(caller, outbound);
ast_connected_line_copy_from_caller(&outbound->connected, &caller->caller);
ast_channel_inherit_variables(caller, outbound);
ast_channel_datastore_inherit(caller, outbound);
ast_string_field_set(outbound, language, caller->language);
ast_string_field_set(outbound, accountcode, caller->accountcode);
ast_string_field_set(outbound, musicclass, caller->musicclass);
ast_channel_unlock(outbound);
ast_channel_unlock(caller);
ast_verb(3, "calling Local/%s\n", dialarg);
if (!ast_call(outbound, dialarg, 0)) {
tmpuser->ochan = outbound;
tmpuser->state = 0;
tmpuser->cleared = 0;
ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
} else {
ast_verb(3, "couldn't reach at this number.\n");
ast_channel_lock(outbound);
if (!outbound->cdr) {
outbound->cdr = ast_cdr_alloc();
}
if (outbound->cdr) {
char tmp[256];
ast_cdr_init(outbound->cdr, outbound);
snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
ast_cdr_update(outbound);
ast_cdr_start(outbound->cdr);
ast_cdr_end(outbound->cdr);
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
ast_cdr_failed(outbound->cdr);
}
} else {
ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
}
ast_channel_unlock(outbound);
ast_hangup(outbound);
ast_free(tmpuser);
}
} else {
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
ast_free(tmpuser);
}
}
if (AST_LIST_EMPTY(findme_user_list)) {
continue;
}
winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs);
if (!winner) {
continue;
}
/* 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->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);
}
}
break;
}
destroy_calling_tree(findme_user_list);
if (!winner) {
tpargs->status = 1;
} else {
tpargs->status = 100;
tpargs->outbound = winner;
}
}
| static void free_numbers | ( | struct call_followme * | f | ) | [static] |
Definition at line 218 of file app_followme.c.
References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, call_followme::blnumbers, call_followme::numbers, and call_followme::wlnumbers.
Referenced by app_exec(), reload_followme(), and unload_module().
{
/* Free numbers attached to the profile */
struct number *prev;
while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
/* Free the number */
ast_free(prev);
AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
/* Free the blacklisted number */
ast_free(prev);
AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
/* Free the whitelisted number */
ast_free(prev);
AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
}
| static void init_profile | ( | struct call_followme * | f | ) | [static] |
Definition at line 266 of file app_followme.c.
References call_followme::active, ast_copy_string(), and call_followme::moh.
Referenced by reload_followme().
{
f->active = 1;
ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
}
| static int load_module | ( | void | ) | [static] |
Definition at line 1323 of file app_followme.c.
References app_exec(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, and reload_followme().
{
if(!reload_followme(0))
return AST_MODULE_LOAD_DECLINE;
return ast_register_application_xml(app, app_exec);
}
| static void profile_set_param | ( | struct call_followme * | f, |
| const char * | param, | ||
| const char * | val, | ||
| int | linenum, | ||
| int | failunknown | ||
| ) | [static] |
Set parameter in profile from configuration file.
Definition at line 275 of file app_followme.c.
References ast_copy_string(), ast_log(), call_followme::callfromprompt, call_followme::context, LOG_WARNING, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, and call_followme::takecall.
Referenced by find_realtime(), and reload_followme().
{
if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
ast_copy_string(f->moh, val, sizeof(f->moh));
else if (!strcasecmp(param, "context"))
ast_copy_string(f->context, val, sizeof(f->context));
else if (!strcasecmp(param, "takecall"))
ast_copy_string(f->takecall, val, sizeof(f->takecall));
else if (!strcasecmp(param, "declinecall"))
ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
else if (failunknown) {
if (linenum >= 0)
ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
else
ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
}
}
| static int reload | ( | void | ) | [static] |
Definition at line 1331 of file app_followme.c.
References reload_followme().
{
reload_followme(1);
return 0;
}
| static int reload_followme | ( | int | reload | ) | [static] |
Reload followme application module.
Definition at line 327 of file app_followme.c.
References call_followme::active, alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), f, free_numbers(), init_profile(), ast_variable::lineno, call_followme::lock, LOG_ERROR, LOG_WARNING, ast_variable::name, call_followme::name, ast_variable::next, call_followme::numbers, profile_set_param(), number::timeout, ast_variable::value, and var.
Referenced by load_module(), and reload().
{
struct call_followme *f;
struct ast_config *cfg;
char *cat = NULL, *tmp;
struct ast_variable *var;
struct number *cur, *nm;
char *numberstr;
int timeout;
int numorder;
const char *takecallstr;
const char *declinecallstr;
const char *tmpstr;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
if (!(cfg = ast_config_load("followme.conf", config_flags))) {
ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
return 0;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
return 0;
}
AST_RWLIST_WRLOCK(&followmes);
/* Reset Global Var Values */
featuredigittimeout = 5000;
/* Mark all profiles as inactive for the moment */
AST_RWLIST_TRAVERSE(&followmes, f, entry) {
f->active = 0;
}
featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
if (!ast_strlen_zero(featuredigittostr)) {
if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
featuredigittimeout = 5000;
}
if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
ast_copy_string(takecall, takecallstr, sizeof(takecall));
}
if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
}
if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
}
/* Chug through config file */
while ((cat = ast_category_browse(cfg, cat))) {
int new = 0;
if (!strcasecmp(cat, "general"))
continue;
/* Look for an existing one */
AST_LIST_TRAVERSE(&followmes, f, entry) {
if (!strcasecmp(f->name, cat))
break;
}
ast_debug(1, "New profile %s.\n", cat);
if (!f) {
/* Make one then */
f = alloc_profile(cat);
new = 1;
}
/* Totally fail if we fail to find/create an entry */
if (!f)
continue;
if (!new)
ast_mutex_lock(&f->lock);
/* Re-initialize the profile */
init_profile(f);
free_numbers(f);
var = ast_variable_browse(cfg, cat);
while (var) {
if (!strcasecmp(var->name, "number")) {
int idx = 0;
/* Add a new number */
numberstr = ast_strdupa(var->value);
if ((tmp = strchr(numberstr, ','))) {
*tmp++ = '\0';
timeout = atoi(tmp);
if (timeout < 0) {
timeout = 25;
}
if ((tmp = strchr(tmp, ','))) {
*tmp++ = '\0';
numorder = atoi(tmp);
if (numorder < 0)
numorder = 0;
} else
numorder = 0;
} else {
timeout = 25;
numorder = 0;
}
if (!numorder) {
idx = 1;
AST_LIST_TRAVERSE(&f->numbers, nm, entry)
idx++;
numorder = idx;
}
cur = create_followme_number(numberstr, timeout, numorder);
if (cur) {
AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
}
} else {
profile_set_param(f, var->name, var->value, var->lineno, 1);
ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
}
var = var->next;
} /* End while(var) loop */
if (!new)
ast_mutex_unlock(&f->lock);
else
AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
}
ast_config_destroy(cfg);
AST_RWLIST_UNLOCK(&followmes);
return 1;
}
| static int unload_module | ( | void | ) | [static] |
Definition at line 1305 of file app_followme.c.
References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_unregister_application(), f, and free_numbers().
{
struct call_followme *f;
ast_unregister_application(app);
/* Free Memory. Yeah! I'm free! */
AST_RWLIST_WRLOCK(&followmes);
while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
free_numbers(f);
ast_free(f);
}
AST_RWLIST_UNLOCK(&followmes);
return 0;
}
| 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 | ||
| ) | [static, read] |
Definition at line 557 of file app_followme.c.
References ARRAY_LEN, AST_CAUSE_NORMAL_CLEARING, ast_connected_line_parse_data(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_party_connected_line_free(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_read(), ast_sched_runq(), ast_sched_wait(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), findme_user::connected, fm_args::connected_in, ast_frame::data, ast_frame::datalen, findme_user::digts, f, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE, fm_args::followmeflags, ast_frame::frametype, ast_channel::hangupcause, ast_frame_subclass::integer, ast_channel::language, LOG_NOTICE, LOG_WARNING, ast_channel::name, fm_args::nextindp, fm_args::norecordingprompt, findme_user::ochan, fm_args::optionsprompt, findme_user::pending_connected_update, fm_args::pending_in_connected_update, ast_frame::ptr, ast_channel::sched, findme_user::state, ast_channel::stream, ast_frame::subclass, fm_args::takecall, number::timeout, ast_channel::timingfunc, ast_frame::uint32, findme_user::yn, and findme_user::ynidx.
Referenced by findmeexec().
{
struct ast_party_connected_line connected;
struct ast_channel *watchers[256];
int pos;
struct ast_channel *winner;
struct ast_frame *f;
int ctstatus = 0;
int dg;
struct findme_user *tmpuser;
int to = 0;
int livechannels = 0;
int tmpto;
long totalwait = 0, wtd = 0, towas = 0;
char *callfromname;
char *pressbuttonname;
/* ------------ wait_for_winner_channel start --------------- */
callfromname = ast_strdupa(tpargs->callfromprompt);
pressbuttonname = ast_strdupa(tpargs->optionsprompt);
if (AST_LIST_EMPTY(findme_user_list)) {
ast_verb(3, "couldn't reach at this number.\n");
return NULL;
}
if (!caller) {
ast_verb(3, "Original caller hungup. Cleanup.\n");
clear_calling_tree(findme_user_list);
return NULL;
}
totalwait = nm->timeout * 1000;
while (!ctstatus) {
to = 1000;
pos = 1;
livechannels = 0;
watchers[0] = caller;
dg = 0;
winner = NULL;
AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
if (tmpuser->state >= 0 && tmpuser->ochan) {
if (tmpuser->state == 3)
tmpuser->digts += (towas - wtd);
if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
ast_verb(3, "We've been waiting for digits longer than we should have.\n");
if (!ast_strlen_zero(namerecloc)) {
tmpuser->state = 1;
tmpuser->digts = 0;
if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
ast_sched_runq(tmpuser->ochan->sched);
} else {
ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
return NULL;
}
} else {
tmpuser->state = 2;
tmpuser->digts = 0;
if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
ast_sched_runq(tmpuser->ochan->sched);
else {
ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
return NULL;
}
}
}
if (tmpuser->ochan->stream) {
ast_sched_runq(tmpuser->ochan->sched);
tmpto = ast_sched_wait(tmpuser->ochan->sched);
if (tmpto > 0 && tmpto < to)
to = tmpto;
else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
ast_stopstream(tmpuser->ochan);
if (tmpuser->state == 1) {
ast_verb(3, "Playback of the call-from file appears to be done.\n");
if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
tmpuser->state = 2;
} else {
ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
tmpuser->ynidx = 0;
if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
tmpuser->state = 3;
else {
ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
return NULL;
}
}
} else if (tmpuser->state == 2) {
ast_verb(3, "Playback of name file appears to be done.\n");
memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
tmpuser->ynidx = 0;
if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
tmpuser->state = 3;
} else {
return NULL;
}
} else if (tmpuser->state == 3) {
ast_verb(3, "Playback of the next step file appears to be done.\n");
tmpuser->digts = 0;
}
}
}
watchers[pos++] = tmpuser->ochan;
livechannels++;
}
}
tmpto = to;
if (to < 0) {
to = 1000;
tmpto = 1000;
}
towas = to;
winner = ast_waitfor_n(watchers, pos, &to);
tmpto -= to;
totalwait -= tmpto;
wtd = to;
if (totalwait <= 0) {
ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
clear_calling_tree(findme_user_list);
return NULL;
}
if (winner) {
/* Need to find out which channel this is */
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) {
switch (f->subclass.integer) {
case AST_CONTROL_HANGUP:
ast_verb(3, "%s received a hangup frame.\n", winner->name);
if (f->data.uint32) {
winner->hangupcause = f->data.uint32;
}
if (dg == 0) {
ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
clear_calling_tree(findme_user_list);
ctstatus = -1;
}
break;
case AST_CONTROL_ANSWER:
ast_verb(3, "%s answered %s\n", winner->name, caller->name);
/* If call has been answered, then the eventual hangup is likely to be normal hangup */
winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
ast_verb(3, "Starting playback of %s\n", callfromname);
if (dg > 0) {
if (!ast_strlen_zero(namerecloc)) {
if (!ast_streamfile(winner, callfromname, winner->language)) {
ast_sched_runq(winner->sched);
tmpuser->state = 1;
} else {
ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
ast_frfree(f);
return NULL;
}
} else {
tmpuser->state = 2;
if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
ast_sched_runq(tmpuser->ochan->sched);
else {
ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
ast_frfree(f);
return NULL;
}
}
}
break;
case AST_CONTROL_BUSY:
ast_verb(3, "%s is busy\n", winner->name);
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", winner->name);
break;
case AST_CONTROL_RINGING:
ast_verb(3, "%s is ringing\n", winner->name);
break;
case AST_CONTROL_PROGRESS:
ast_verb(3, "%s is making progress\n", winner->name);
break;
case AST_CONTROL_VIDUPDATE:
ast_verb(3, "%s requested a video update\n", winner->name);
break;
case AST_CONTROL_SRCUPDATE:
ast_verb(3, "%s requested a source update\n", winner->name);
break;
case AST_CONTROL_PROCEEDING:
ast_verb(3, "%s is proceeding\n", winner->name);
break;
case AST_CONTROL_HOLD:
ast_verb(3, "%s placed call on hold\n", winner->name);
break;
case AST_CONTROL_UNHOLD:
ast_verb(3, "%s removed call from hold\n", winner->name);
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",
winner->name);
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",
winner->name);
} else {
ast_verb(3,
"%s connected line has changed. Saving it until answer.\n",
winner->name);
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:
/*
* Always ignore because the caller is already answered
* and is likely listening to MOH.
*/
break;
case -1:
ast_verb(3, "%s stopped sounds\n", winner->name);
break;
default:
ast_debug(1, "Dunno what to do with control type %d from %s\n",
f->subclass.integer, winner->name);
break;
}
}
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
if (winner->stream)
ast_stopstream(winner);
tmpuser->digts = 0;
ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
tmpuser->yn[tmpuser->ynidx++] = (char) f->subclass.integer;
}
ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
if (!strcmp(tmpuser->yn, tpargs->takecall)) {
ast_debug(1, "Match to take the call!\n");
ast_frfree(f);
return tmpuser->ochan;
}
if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
ast_debug(1, "Next in dial plan step requested.\n");
ast_frfree(f);
return NULL;
}
}
ast_frfree(f);
} else {
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 {
ast_debug(1, "timed out waiting for action\n");
}
}
/* --- WAIT FOR WINNER NUMBER END! -----------*/
return NULL;
}
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 1342 of file app_followme.c.
char* app = "FollowMe" [static] |
Definition at line 103 of file app_followme.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1342 of file app_followme.c.
char callfromprompt[PATH_MAX] = "followme/call-from" [static] |
Definition at line 207 of file app_followme.c.
const char* defaultmoh = "default" [static] |
Default Music-On-Hold Class
Definition at line 203 of file app_followme.c.
int featuredigittimeout = 5000 [static] |
Feature Digit Timeout
Definition at line 202 of file app_followme.c.
const char* featuredigittostr [static] |
Definition at line 201 of file app_followme.c.
struct ast_app_option followme_opts[128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG },} [static] |
Definition at line 199 of file app_followme.c.
Referenced by app_exec().
char nextindp[MAX_YN_STRING] = "2" [static] |
Definition at line 206 of file app_followme.c.
char norecordingprompt[PATH_MAX] = "followme/no-recording" [static] |
Definition at line 208 of file app_followme.c.
char optionsprompt[PATH_MAX] = "followme/options" [static] |
Definition at line 209 of file app_followme.c.
char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try" [static] |
Definition at line 210 of file app_followme.c.
char sorryprompt[PATH_MAX] = "followme/sorry" [static] |
Definition at line 212 of file app_followme.c.
char statusprompt[PATH_MAX] = "followme/status" [static] |
Definition at line 211 of file app_followme.c.
char takecall[MAX_YN_STRING] = "1" [static] |
Definition at line 205 of file app_followme.c.