#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 |
Data structure for followme scripts. More... | |
struct | call_followme::blnumbers |
struct | call_followme::numbers |
struct | call_followme::wlnumbers |
struct | findme_user |
struct | findme_user_listptr |
struct | fm_args |
struct | fm_args::cnumbers |
struct | followmes |
struct | number |
Number structure. More... | |
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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 char | nextindp [20] = "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 [20] = "1" |
static int | ynlongest = 0 |
Definition in file app_followme.c.
anonymous enum |
FOLLOWMEFLAG_STATUSMSG | |
FOLLOWMEFLAG_RECORDNAME | |
FOLLOWMEFLAG_UNREACHABLEMSG | |
FOLLOWMEFLAG_DISABLEHOLDPROMPT | |
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE |
Definition at line 181 of file app_followme.c.
00181 { 00182 FOLLOWMEFLAG_STATUSMSG = (1 << 0), 00183 FOLLOWMEFLAG_RECORDNAME = (1 << 1), 00184 FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2), 00185 FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3), 00186 FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4), 00187 };
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] |
Allocate and initialize followme profile.
Definition at line 238 of file app_followme.c.
References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, and f.
Referenced by find_realtime(), and reload_followme().
00239 { 00240 struct call_followme *f; 00241 00242 if (!(f = ast_calloc(1, sizeof(*f)))) 00243 return NULL; 00244 00245 ast_mutex_init(&f->lock); 00246 ast_copy_string(f->name, fmname, sizeof(f->name)); 00247 f->moh[0] = '\0'; 00248 f->context[0] = '\0'; 00249 ast_copy_string(f->takecall, takecall, sizeof(f->takecall)); 00250 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp)); 00251 ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt)); 00252 ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt)); 00253 ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt)); 00254 ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt)); 00255 ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt)); 00256 ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt)); 00257 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers); 00258 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers); 00259 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers); 00260 return f; 00261 }
static int app_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 1130 of file app_followme.c.
References ast_channel::_state, 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_strdupa, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_channel::caller, fm_args::callfromprompt, fm_args::chan, fm_args::cnumbers, config, fm_args::connected_in, fm_args::connected_out, fm_args::context, create_followme_number(), end_bridge_callback(), end_bridge_callback_data_fixup(), number::entry, f, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), LOG_WARNING, fm_args::mohclass, ast_channel::name, ast_format::name, fm_args::namerecloc, fm_args::nextindp, fm_args::norecordingprompt, number::number, fm_args::optionsprompt, number::order, fm_args::outbound, fm_args::pending_in_connected_update, fm_args::pending_out_connected_update, fm_args::plsholdprompt, S_OR, fm_args::sorryprompt, fm_args::status, fm_args::statusprompt, fm_args::takecall, THRESHOLD_SILENCE, and number::timeout.
01131 { 01132 struct fm_args targs = { 0, }; 01133 struct ast_bridge_config config; 01134 struct call_followme *f; 01135 struct number *nm, *newnm; 01136 int res = 0; 01137 char *argstr; 01138 char namerecloc[255]; 01139 int duration = 0; 01140 struct ast_channel *caller; 01141 struct ast_channel *outbound; 01142 AST_DECLARE_APP_ARGS(args, 01143 AST_APP_ARG(followmeid); 01144 AST_APP_ARG(options); 01145 ); 01146 01147 if (ast_strlen_zero(data)) { 01148 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app); 01149 return -1; 01150 } 01151 01152 argstr = ast_strdupa((char *) data); 01153 01154 AST_STANDARD_APP_ARGS(args, argstr); 01155 01156 if (ast_strlen_zero(args.followmeid)) { 01157 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app); 01158 return -1; 01159 } 01160 01161 AST_RWLIST_RDLOCK(&followmes); 01162 AST_RWLIST_TRAVERSE(&followmes, f, entry) { 01163 if (!strcasecmp(f->name, args.followmeid) && (f->active)) 01164 break; 01165 } 01166 AST_RWLIST_UNLOCK(&followmes); 01167 01168 ast_debug(1, "New profile %s.\n", args.followmeid); 01169 01170 if (!f) { 01171 f = find_realtime(args.followmeid); 01172 } 01173 01174 if (!f) { 01175 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid); 01176 return 0; 01177 } 01178 01179 /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */ 01180 if (args.options) 01181 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options); 01182 01183 /* Lock the profile lock and copy out everything we need to run with before unlocking it again */ 01184 ast_mutex_lock(&f->lock); 01185 targs.mohclass = ast_strdupa(f->moh); 01186 ast_copy_string(targs.context, f->context, sizeof(targs.context)); 01187 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall)); 01188 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp)); 01189 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt)); 01190 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt)); 01191 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt)); 01192 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt)); 01193 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt)); 01194 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt)); 01195 /* Copy the numbers we're going to use into another list in case the master list should get modified 01196 (and locked) while we're trying to do a follow-me */ 01197 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers); 01198 AST_LIST_TRAVERSE(&f->numbers, nm, entry) { 01199 newnm = create_followme_number(nm->number, nm->timeout, nm->order); 01200 if (newnm) { 01201 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry); 01202 } 01203 } 01204 ast_mutex_unlock(&f->lock); 01205 01206 /* Answer the call */ 01207 if (chan->_state != AST_STATE_UP) { 01208 ast_answer(chan); 01209 } 01210 01211 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 01212 ast_stream_and_wait(chan, targs.statusprompt, ""); 01213 01214 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid); 01215 duration = 5; 01216 01217 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) 01218 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) 01219 goto outrun; 01220 01221 if (!ast_fileexists(namerecloc, NULL, chan->language)) 01222 ast_copy_string(namerecloc, "", sizeof(namerecloc)); 01223 if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) { 01224 if (ast_streamfile(chan, targs.plsholdprompt, chan->language)) 01225 goto outrun; 01226 if (ast_waitstream(chan, "") < 0) 01227 goto outrun; 01228 } 01229 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL); 01230 01231 targs.status = 0; 01232 targs.chan = chan; 01233 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc)); 01234 ast_channel_lock(chan); 01235 ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller); 01236 ast_channel_unlock(chan); 01237 01238 findmeexec(&targs); 01239 01240 while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry))) 01241 ast_free(nm); 01242 01243 if (!ast_strlen_zero(namerecloc)) 01244 unlink(namerecloc); 01245 01246 if (targs.status != 100) { 01247 ast_moh_stop(chan); 01248 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 01249 ast_stream_and_wait(chan, targs.sorryprompt, ""); 01250 res = 0; 01251 } else { 01252 caller = chan; 01253 outbound = targs.outbound; 01254 /* Bridge the two channels. */ 01255 01256 memset(&config, 0, sizeof(config)); 01257 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); 01258 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); 01259 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); 01260 config.end_bridge_callback = end_bridge_callback; 01261 config.end_bridge_callback_data = chan; 01262 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 01263 01264 ast_moh_stop(caller); 01265 /* Be sure no generators are left on it */ 01266 ast_deactivate_generator(caller); 01267 /* Make sure channels are compatible */ 01268 res = ast_channel_make_compatible(caller, outbound); 01269 if (res < 0) { 01270 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name); 01271 ast_hangup(outbound); 01272 goto outrun; 01273 } 01274 01275 /* Update connected line to caller if available. */ 01276 if (targs.pending_out_connected_update) { 01277 if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) { 01278 ast_channel_update_connected_line(caller, &targs.connected_out, NULL); 01279 } 01280 } 01281 01282 /* Update connected line to winner if changed. */ 01283 if (targs.pending_in_connected_update) { 01284 if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) { 01285 ast_channel_update_connected_line(outbound, &targs.connected_in, NULL); 01286 } 01287 } 01288 01289 res = ast_bridge_call(caller, outbound, &config); 01290 ast_hangup(outbound); 01291 } 01292 01293 outrun: 01294 ast_party_connected_line_free(&targs.connected_in); 01295 ast_party_connected_line_free(&targs.connected_out); 01296 if (f->realtime) { 01297 /* Not in list */ 01298 free_numbers(f); 01299 ast_free(f); 01300 } 01301 01302 return res; 01303 }
static void clear_caller | ( | struct findme_user * | tmpuser | ) | [static] |
Definition at line 496 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(), and destroy_calling_tree().
00497 { 00498 struct ast_channel *outbound; 00499 00500 if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) { 00501 outbound = tmpuser->ochan; 00502 ast_channel_lock(outbound); 00503 if (!outbound->cdr) { 00504 outbound->cdr = ast_cdr_alloc(); 00505 if (outbound->cdr) { 00506 ast_cdr_init(outbound->cdr, outbound); 00507 } 00508 } 00509 if (outbound->cdr) { 00510 char tmp[256]; 00511 00512 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg); 00513 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp); 00514 ast_cdr_update(outbound); 00515 ast_cdr_start(outbound->cdr); 00516 ast_cdr_end(outbound->cdr); 00517 /* If the cause wasn't handled properly */ 00518 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) { 00519 ast_cdr_failed(outbound->cdr); 00520 } 00521 } else { 00522 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); 00523 } 00524 ast_channel_unlock(outbound); 00525 ast_hangup(outbound); 00526 tmpuser->ochan = NULL; 00527 } 00528 }
static void clear_calling_tree | ( | struct findme_user_listptr * | findme_user_list | ) | [static] |
Definition at line 530 of file app_followme.c.
References AST_LIST_TRAVERSE, clear_caller(), findme_user::cleared, and number::entry.
Referenced by wait_for_winner().
00531 { 00532 struct findme_user *tmpuser; 00533 00534 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { 00535 clear_caller(tmpuser); 00536 tmpuser->cleared = 1; 00537 } 00538 }
static struct number* create_followme_number | ( | const char * | number, | |
int | timeout, | |||
int | numorder | |||
) | [static] |
Add a new number.
Definition at line 304 of file app_followme.c.
References ast_calloc, ast_copy_string(), ast_debug, and ast_strdupa.
Referenced by app_exec(), and reload_followme().
00305 { 00306 struct number *cur; 00307 char *buf = ast_strdupa(number); 00308 char *tmp; 00309 00310 if (!(cur = ast_calloc(1, sizeof(*cur)))) 00311 return NULL; 00312 00313 cur->timeout = timeout; 00314 if ((tmp = strchr(buf, ','))) 00315 *tmp = '\0'; 00316 ast_copy_string(cur->number, buf, sizeof(cur->number)); 00317 cur->order = numorder; 00318 ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout); 00319 00320 return cur; 00321 }
static void destroy_calling_tree | ( | struct findme_user_listptr * | findme_user_list | ) | [static] |
Definition at line 540 of file app_followme.c.
References ast_free, AST_LIST_REMOVE_HEAD, ast_party_connected_line_free(), clear_caller(), findme_user::cleared, findme_user::connected, and number::entry.
00541 { 00542 struct findme_user *fmuser; 00543 00544 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) { 00545 if (!fmuser->cleared) { 00546 clear_caller(fmuser); 00547 } 00548 ast_party_connected_line_free(&fmuser->connected); 00549 ast_free(fmuser); 00550 } 00551 ast_free(findme_user_list); 00552 }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 1104 of file app_followme.c.
References ast_cdr::answer, ast_channel_lock, ast_channel_unlock, ast_channel::cdr, pbx_builtin_setvar_helper(), and ast_cdr::start.
01105 { 01106 char buf[80]; 01107 time_t end; 01108 struct ast_channel *chan = data; 01109 01110 time(&end); 01111 01112 ast_channel_lock(chan); 01113 if (chan->cdr->answer.tv_sec) { 01114 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec); 01115 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); 01116 } 01117 01118 if (chan->cdr->start.tv_sec) { 01119 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec); 01120 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); 01121 } 01122 ast_channel_unlock(chan); 01123 }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
struct ast_channel * | originator, | |||
struct ast_channel * | terminator | |||
) | [static] |
Definition at line 1125 of file app_followme.c.
References ast_bridge_config::end_bridge_callback_data.
01126 { 01127 bconfig->end_bridge_callback_data = originator; 01128 }
static struct call_followme* find_realtime | ( | const char * | name | ) | [static] |
Definition at line 1023 of file app_followme.c.
References alloc_profile(), ast_false(), ast_free, ast_load_realtime(), ast_mutex_destroy, ast_str_create(), ast_variables_destroy(), call_followme::lock, ast_variable::name, ast_variable::next, SENTINEL, str, ast_variable::value, and var.
Referenced by app_exec().
01024 { 01025 struct ast_variable *var; 01026 struct ast_variable *v; 01027 struct ast_config *cfg; 01028 const char *catg; 01029 struct call_followme *new_follower; 01030 struct ast_str *str; 01031 01032 str = ast_str_create(16); 01033 if (!str) { 01034 return NULL; 01035 } 01036 01037 var = ast_load_realtime("followme", "name", name, SENTINEL); 01038 if (!var) { 01039 ast_free(str); 01040 return NULL; 01041 } 01042 01043 if (!(new_follower = alloc_profile(name))) { 01044 ast_variables_destroy(var); 01045 ast_free(str); 01046 return NULL; 01047 } 01048 01049 for (v = var; v; v = v->next) { 01050 if (!strcasecmp(v->name, "active")) { 01051 if (ast_false(v->value)) { 01052 ast_mutex_destroy(&new_follower->lock); 01053 ast_free(new_follower); 01054 ast_variables_destroy(var); 01055 ast_free(str); 01056 return NULL; 01057 } 01058 } else { 01059 profile_set_param(new_follower, v->name, v->value, 0, 0); 01060 } 01061 } 01062 01063 ast_variables_destroy(var); 01064 new_follower->realtime = 1; 01065 01066 /* Load numbers */ 01067 cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", 01068 name, SENTINEL); 01069 if (!cfg) { 01070 ast_mutex_destroy(&new_follower->lock); 01071 ast_free(new_follower); 01072 ast_free(str); 01073 return NULL; 01074 } 01075 01076 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { 01077 const char *numstr; 01078 const char *timeoutstr; 01079 const char *ordstr; 01080 int timeout; 01081 struct number *cur; 01082 01083 if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) { 01084 continue; 01085 } 01086 if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) 01087 || sscanf(timeoutstr, "%30d", &timeout) != 1 01088 || timeout < 1) { 01089 timeout = 25; 01090 } 01091 /* This one has to exist; it was part of the query */ 01092 ordstr = ast_variable_retrieve(cfg, catg, "ordinal"); 01093 ast_str_set(&str, 0, "%s", numstr); 01094 if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) { 01095 AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry); 01096 } 01097 } 01098 ast_config_destroy(cfg); 01099 01100 ast_free(str); 01101 return new_follower; 01102 }
static void findmeexec | ( | struct fm_args * | tpargs | ) | [static] |
Definition at line 872 of file app_followme.c.
References 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_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_request(), ast_string_field_set, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, fm_args::cnumbers, ast_channel::connected, fm_args::context, number::entry, ast_channel::hangupcause, ast_party_caller::id, language, LOG_ERROR, LOG_WARNING, musicclass, ast_channel::nativeformats, fm_args::nextindp, ast_party_id::number, number::number, number::order, S_COR, ast_party_number::str, fm_args::takecall, number::timeout, and ast_party_number::valid.
Referenced by app_exec().
00873 { 00874 struct number *nm; 00875 struct ast_channel *outbound; 00876 struct ast_channel *caller; 00877 struct ast_channel *winner = NULL; 00878 char dialarg[512]; 00879 char num[512]; 00880 int dg, idx; 00881 char *rest, *number; 00882 struct findme_user *tmpuser; 00883 struct findme_user *fmuser; 00884 struct findme_user_listptr *findme_user_list; 00885 00886 findme_user_list = ast_calloc(1, sizeof(*findme_user_list)); 00887 AST_LIST_HEAD_INIT_NOLOCK(findme_user_list); 00888 00889 /* We're going to figure out what the longest possible string of digits to collect is */ 00890 ynlongest = 0; 00891 if (strlen(tpargs->takecall) > ynlongest) { 00892 ynlongest = strlen(tpargs->takecall); 00893 } 00894 if (strlen(tpargs->nextindp) > ynlongest) { 00895 ynlongest = strlen(tpargs->nextindp); 00896 } 00897 00898 caller = tpargs->chan; 00899 for (idx = 1; !ast_check_hangup(caller); ++idx) { 00900 /* Find next followme numbers to dial. */ 00901 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) { 00902 if (nm->order == idx) { 00903 break; 00904 } 00905 } 00906 if (!nm) { 00907 break; 00908 } 00909 00910 ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout); 00911 00912 ast_copy_string(num, nm->number, sizeof(num)); 00913 for (number = num; number; number = rest) { 00914 rest = strchr(number, '&'); 00915 if (rest) { 00916 *rest++ = 0; 00917 } 00918 00919 /* We check if the extension exists, before creating the ast_channel struct */ 00920 if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) { 00921 ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context); 00922 continue; 00923 } 00924 00925 if (!strcmp(tpargs->context, "")) { 00926 snprintf(dialarg, sizeof(dialarg), "%s", number); 00927 } else { 00928 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context); 00929 } 00930 00931 tmpuser = ast_calloc(1, sizeof(*tmpuser)); 00932 if (!tmpuser) { 00933 continue; 00934 } 00935 00936 outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg); 00937 if (outbound) { 00938 ast_channel_lock_both(caller, outbound); 00939 ast_connected_line_copy_from_caller(&outbound->connected, &caller->caller); 00940 ast_channel_inherit_variables(caller, outbound); 00941 ast_channel_datastore_inherit(caller, outbound); 00942 ast_string_field_set(outbound, language, caller->language); 00943 ast_string_field_set(outbound, accountcode, caller->accountcode); 00944 ast_string_field_set(outbound, musicclass, caller->musicclass); 00945 ast_channel_unlock(outbound); 00946 ast_channel_unlock(caller); 00947 ast_verb(3, "calling Local/%s\n", dialarg); 00948 if (!ast_call(outbound, dialarg, 0)) { 00949 tmpuser->ochan = outbound; 00950 tmpuser->state = 0; 00951 tmpuser->cleared = 0; 00952 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg)); 00953 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry); 00954 } else { 00955 ast_verb(3, "couldn't reach at this number.\n"); 00956 ast_channel_lock(outbound); 00957 if (!outbound->cdr) { 00958 outbound->cdr = ast_cdr_alloc(); 00959 } 00960 if (outbound->cdr) { 00961 char tmp[256]; 00962 00963 ast_cdr_init(outbound->cdr, outbound); 00964 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg); 00965 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp); 00966 ast_cdr_update(outbound); 00967 ast_cdr_start(outbound->cdr); 00968 ast_cdr_end(outbound->cdr); 00969 /* If the cause wasn't handled properly */ 00970 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) { 00971 ast_cdr_failed(outbound->cdr); 00972 } 00973 } else { 00974 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n"); 00975 } 00976 ast_channel_unlock(outbound); 00977 ast_hangup(outbound); 00978 ast_free(tmpuser); 00979 } 00980 } else { 00981 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg)); 00982 ast_free(tmpuser); 00983 } 00984 } 00985 00986 if (AST_LIST_EMPTY(findme_user_list)) { 00987 continue; 00988 } 00989 00990 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs); 00991 if (!winner) { 00992 continue; 00993 } 00994 00995 /* Destroy losing calls up to the winner. The rest will be destroyed later. */ 00996 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) { 00997 if (fmuser->ochan == winner) { 00998 /* Pass any connected line info up. */ 00999 tpargs->connected_out = fmuser->connected; 01000 tpargs->pending_out_connected_update = fmuser->pending_connected_update; 01001 ast_free(fmuser); 01002 break; 01003 } else { 01004 /* Destroy losing call. */ 01005 if (!fmuser->cleared) { 01006 clear_caller(fmuser); 01007 } 01008 ast_party_connected_line_free(&fmuser->connected); 01009 ast_free(fmuser); 01010 } 01011 } 01012 break; 01013 } 01014 destroy_calling_tree(findme_user_list); 01015 if (!winner) { 01016 tpargs->status = 1; 01017 } else { 01018 tpargs->status = 100; 01019 tpargs->outbound = winner; 01020 } 01021 }
static void free_numbers | ( | struct call_followme * | f | ) | [static] |
Definition at line 215 of file app_followme.c.
References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, number::entry, and f.
Referenced by app_exec(), reload_followme(), and unload_module().
00216 { 00217 /* Free numbers attached to the profile */ 00218 struct number *prev; 00219 00220 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry))) 00221 /* Free the number */ 00222 ast_free(prev); 00223 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers); 00224 00225 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry))) 00226 /* Free the blacklisted number */ 00227 ast_free(prev); 00228 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers); 00229 00230 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry))) 00231 /* Free the whitelisted number */ 00232 ast_free(prev); 00233 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers); 00234 }
static void init_profile | ( | struct call_followme * | f | ) | [static] |
Definition at line 263 of file app_followme.c.
References ast_copy_string(), and f.
Referenced by reload_followme().
00264 { 00265 f->active = 1; 00266 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh)); 00267 }
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().
01324 { 01325 if(!reload_followme(0)) 01326 return AST_MODULE_LOAD_DECLINE; 01327 01328 return ast_register_application_xml(app, app_exec); 01329 }
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 272 of file app_followme.c.
References ast_copy_string(), ast_log(), f, LOG_WARNING, and ast_format::name.
Referenced by reload_followme().
00273 { 00274 00275 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 00276 ast_copy_string(f->moh, val, sizeof(f->moh)); 00277 else if (!strcasecmp(param, "context")) 00278 ast_copy_string(f->context, val, sizeof(f->context)); 00279 else if (!strcasecmp(param, "takecall")) 00280 ast_copy_string(f->takecall, val, sizeof(f->takecall)); 00281 else if (!strcasecmp(param, "declinecall")) 00282 ast_copy_string(f->nextindp, val, sizeof(f->nextindp)); 00283 else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt")) 00284 ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt)); 00285 else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 00286 ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt)); 00287 else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 00288 ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt)); 00289 else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt")) 00290 ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt)); 00291 else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 00292 ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt)); 00293 else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 00294 ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt)); 00295 else if (failunknown) { 00296 if (linenum >= 0) 00297 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum); 00298 else 00299 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param); 00300 } 00301 }
static int reload | ( | void | ) | [static] |
Definition at line 1331 of file app_followme.c.
References reload_followme().
01332 { 01333 reload_followme(1); 01334 01335 return 0; 01336 }
static int reload_followme | ( | int | reload | ) | [static] |
Reload followme application module.
Definition at line 324 of file app_followme.c.
References 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_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), number::entry, f, free_numbers(), init_profile(), LOG_ERROR, LOG_WARNING, ast_format::name, profile_set_param(), number::timeout, and var.
Referenced by load_module(), and reload().
00325 { 00326 struct call_followme *f; 00327 struct ast_config *cfg; 00328 char *cat = NULL, *tmp; 00329 struct ast_variable *var; 00330 struct number *cur, *nm; 00331 char numberstr[90]; 00332 int timeout; 00333 int numorder; 00334 const char *takecallstr; 00335 const char *declinecallstr; 00336 const char *tmpstr; 00337 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00338 00339 if (!(cfg = ast_config_load("followme.conf", config_flags))) { 00340 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n"); 00341 return 0; 00342 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 00343 return 0; 00344 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 00345 ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n"); 00346 return 0; 00347 } 00348 00349 AST_RWLIST_WRLOCK(&followmes); 00350 00351 /* Reset Global Var Values */ 00352 featuredigittimeout = 5000; 00353 00354 /* Mark all profiles as inactive for the moment */ 00355 AST_RWLIST_TRAVERSE(&followmes, f, entry) { 00356 f->active = 0; 00357 } 00358 00359 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout"); 00360 00361 if (!ast_strlen_zero(featuredigittostr)) { 00362 if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout)) 00363 featuredigittimeout = 5000; 00364 } 00365 00366 if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) { 00367 ast_copy_string(takecall, takecallstr, sizeof(takecall)); 00368 } 00369 00370 if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) { 00371 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp)); 00372 } 00373 00374 if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) { 00375 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt)); 00376 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) { 00377 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt)); 00378 } 00379 00380 if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) { 00381 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt)); 00382 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) { 00383 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt)); 00384 } 00385 00386 00387 if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) { 00388 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt)); 00389 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) { 00390 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt)); 00391 } 00392 00393 if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) { 00394 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt)); 00395 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) { 00396 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt)); 00397 } 00398 00399 if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) { 00400 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt)); 00401 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) { 00402 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt)); 00403 } 00404 00405 if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) { 00406 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt)); 00407 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) { 00408 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt)); 00409 } 00410 00411 /* Chug through config file */ 00412 while ((cat = ast_category_browse(cfg, cat))) { 00413 int new = 0; 00414 00415 if (!strcasecmp(cat, "general")) 00416 continue; 00417 00418 /* Look for an existing one */ 00419 AST_LIST_TRAVERSE(&followmes, f, entry) { 00420 if (!strcasecmp(f->name, cat)) 00421 break; 00422 } 00423 00424 ast_debug(1, "New profile %s.\n", cat); 00425 00426 if (!f) { 00427 /* Make one then */ 00428 f = alloc_profile(cat); 00429 new = 1; 00430 } 00431 00432 /* Totally fail if we fail to find/create an entry */ 00433 if (!f) 00434 continue; 00435 00436 if (!new) 00437 ast_mutex_lock(&f->lock); 00438 /* Re-initialize the profile */ 00439 init_profile(f); 00440 free_numbers(f); 00441 var = ast_variable_browse(cfg, cat); 00442 while (var) { 00443 if (!strcasecmp(var->name, "number")) { 00444 int idx = 0; 00445 00446 /* Add a new number */ 00447 ast_copy_string(numberstr, var->value, sizeof(numberstr)); 00448 if ((tmp = strchr(numberstr, ','))) { 00449 *tmp++ = '\0'; 00450 timeout = atoi(tmp); 00451 if (timeout < 0) { 00452 timeout = 25; 00453 } 00454 if ((tmp = strchr(tmp, ','))) { 00455 *tmp++ = '\0'; 00456 numorder = atoi(tmp); 00457 if (numorder < 0) 00458 numorder = 0; 00459 } else 00460 numorder = 0; 00461 } else { 00462 timeout = 25; 00463 numorder = 0; 00464 } 00465 00466 if (!numorder) { 00467 idx = 1; 00468 AST_LIST_TRAVERSE(&f->numbers, nm, entry) 00469 idx++; 00470 numorder = idx; 00471 } 00472 cur = create_followme_number(numberstr, timeout, numorder); 00473 if (cur) { 00474 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry); 00475 } 00476 } else { 00477 profile_set_param(f, var->name, var->value, var->lineno, 1); 00478 ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno); 00479 } 00480 var = var->next; 00481 } /* End while(var) loop */ 00482 00483 if (!new) 00484 ast_mutex_unlock(&f->lock); 00485 else 00486 AST_RWLIST_INSERT_HEAD(&followmes, f, entry); 00487 } 00488 00489 ast_config_destroy(cfg); 00490 00491 AST_RWLIST_UNLOCK(&followmes); 00492 00493 return 1; 00494 }
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(), number::entry, f, and free_numbers().
01306 { 01307 struct call_followme *f; 01308 01309 ast_unregister_application(app); 01310 01311 /* Free Memory. Yeah! I'm free! */ 01312 AST_RWLIST_WRLOCK(&followmes); 01313 while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) { 01314 free_numbers(f); 01315 ast_free(f); 01316 } 01317 01318 AST_RWLIST_UNLOCK(&followmes); 01319 01320 return 0; 01321 }
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] |
Definition at line 554 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_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), findme_user::connected, connected, fm_args::connected_in, findme_user::digts, number::entry, f, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE, fm_args::followmeflags, ast_channel::hangupcause, 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_channel::sched, findme_user::state, ast_channel::stream, fm_args::takecall, number::timeout, ast_channel::timingfunc, findme_user::yn, and findme_user::ynidx.
00555 { 00556 struct ast_party_connected_line connected; 00557 struct ast_channel *watchers[256]; 00558 int pos; 00559 struct ast_channel *winner; 00560 struct ast_frame *f; 00561 int ctstatus = 0; 00562 int dg; 00563 struct findme_user *tmpuser; 00564 int to = 0; 00565 int livechannels = 0; 00566 int tmpto; 00567 long totalwait = 0, wtd = 0, towas = 0; 00568 char *callfromname; 00569 char *pressbuttonname; 00570 00571 /* ------------ wait_for_winner_channel start --------------- */ 00572 00573 callfromname = ast_strdupa(tpargs->callfromprompt); 00574 pressbuttonname = ast_strdupa(tpargs->optionsprompt); 00575 00576 if (AST_LIST_EMPTY(findme_user_list)) { 00577 ast_verb(3, "couldn't reach at this number.\n"); 00578 return NULL; 00579 } 00580 00581 if (!caller) { 00582 ast_verb(3, "Original caller hungup. Cleanup.\n"); 00583 clear_calling_tree(findme_user_list); 00584 return NULL; 00585 } 00586 00587 totalwait = nm->timeout * 1000; 00588 00589 while (!ctstatus) { 00590 to = 1000; 00591 pos = 1; 00592 livechannels = 0; 00593 watchers[0] = caller; 00594 00595 dg = 0; 00596 winner = NULL; 00597 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { 00598 if (tmpuser->state >= 0 && tmpuser->ochan) { 00599 if (tmpuser->state == 3) 00600 tmpuser->digts += (towas - wtd); 00601 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) { 00602 ast_verb(3, "We've been waiting for digits longer than we should have.\n"); 00603 if (!ast_strlen_zero(namerecloc)) { 00604 tmpuser->state = 1; 00605 tmpuser->digts = 0; 00606 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) { 00607 ast_sched_runq(tmpuser->ochan->sched); 00608 } else { 00609 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname); 00610 return NULL; 00611 } 00612 } else { 00613 tmpuser->state = 2; 00614 tmpuser->digts = 0; 00615 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language)) 00616 ast_sched_runq(tmpuser->ochan->sched); 00617 else { 00618 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt); 00619 return NULL; 00620 } 00621 } 00622 } 00623 if (tmpuser->ochan->stream) { 00624 ast_sched_runq(tmpuser->ochan->sched); 00625 tmpto = ast_sched_wait(tmpuser->ochan->sched); 00626 if (tmpto > 0 && tmpto < to) 00627 to = tmpto; 00628 else if (tmpto < 0 && !tmpuser->ochan->timingfunc) { 00629 ast_stopstream(tmpuser->ochan); 00630 if (tmpuser->state == 1) { 00631 ast_verb(3, "Playback of the call-from file appears to be done.\n"); 00632 if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) { 00633 tmpuser->state = 2; 00634 } else { 00635 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc); 00636 memset(tmpuser->yn, 0, sizeof(tmpuser->yn)); 00637 tmpuser->ynidx = 0; 00638 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) 00639 tmpuser->state = 3; 00640 else { 00641 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname); 00642 return NULL; 00643 } 00644 } 00645 } else if (tmpuser->state == 2) { 00646 ast_verb(3, "Playback of name file appears to be done.\n"); 00647 memset(tmpuser->yn, 0, sizeof(tmpuser->yn)); 00648 tmpuser->ynidx = 0; 00649 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) { 00650 tmpuser->state = 3; 00651 } else { 00652 return NULL; 00653 } 00654 } else if (tmpuser->state == 3) { 00655 ast_verb(3, "Playback of the next step file appears to be done.\n"); 00656 tmpuser->digts = 0; 00657 } 00658 } 00659 } 00660 watchers[pos++] = tmpuser->ochan; 00661 livechannels++; 00662 } 00663 } 00664 00665 tmpto = to; 00666 if (to < 0) { 00667 to = 1000; 00668 tmpto = 1000; 00669 } 00670 towas = to; 00671 winner = ast_waitfor_n(watchers, pos, &to); 00672 tmpto -= to; 00673 totalwait -= tmpto; 00674 wtd = to; 00675 if (totalwait <= 0) { 00676 ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait); 00677 clear_calling_tree(findme_user_list); 00678 return NULL; 00679 } 00680 if (winner) { 00681 /* Need to find out which channel this is */ 00682 for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) { 00683 if (winner == watchers[dg]) { 00684 break; 00685 } 00686 } 00687 if (dg) { 00688 /* The winner is an outgoing channel. */ 00689 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { 00690 if (tmpuser->ochan == winner) { 00691 break; 00692 } 00693 } 00694 } else { 00695 tmpuser = NULL; 00696 } 00697 f = ast_read(winner); 00698 if (f) { 00699 if (f->frametype == AST_FRAME_CONTROL) { 00700 switch (f->subclass.integer) { 00701 case AST_CONTROL_HANGUP: 00702 ast_verb(3, "%s received a hangup frame.\n", winner->name); 00703 if (f->data.uint32) { 00704 winner->hangupcause = f->data.uint32; 00705 } 00706 if (dg == 0) { 00707 ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n"); 00708 clear_calling_tree(findme_user_list); 00709 ctstatus = -1; 00710 } 00711 break; 00712 case AST_CONTROL_ANSWER: 00713 ast_verb(3, "%s answered %s\n", winner->name, caller->name); 00714 /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 00715 winner->hangupcause = AST_CAUSE_NORMAL_CLEARING; 00716 caller->hangupcause = AST_CAUSE_NORMAL_CLEARING; 00717 ast_verb(3, "Starting playback of %s\n", callfromname); 00718 if (dg > 0) { 00719 if (!ast_strlen_zero(namerecloc)) { 00720 if (!ast_streamfile(winner, callfromname, winner->language)) { 00721 ast_sched_runq(winner->sched); 00722 tmpuser->state = 1; 00723 } else { 00724 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname); 00725 ast_frfree(f); 00726 return NULL; 00727 } 00728 } else { 00729 tmpuser->state = 2; 00730 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language)) 00731 ast_sched_runq(tmpuser->ochan->sched); 00732 else { 00733 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt); 00734 ast_frfree(f); 00735 return NULL; 00736 } 00737 } 00738 } 00739 break; 00740 case AST_CONTROL_BUSY: 00741 ast_verb(3, "%s is busy\n", winner->name); 00742 break; 00743 case AST_CONTROL_CONGESTION: 00744 ast_verb(3, "%s is circuit-busy\n", winner->name); 00745 break; 00746 case AST_CONTROL_RINGING: 00747 ast_verb(3, "%s is ringing\n", winner->name); 00748 break; 00749 case AST_CONTROL_PROGRESS: 00750 ast_verb(3, "%s is making progress\n", winner->name); 00751 break; 00752 case AST_CONTROL_VIDUPDATE: 00753 ast_verb(3, "%s requested a video update\n", winner->name); 00754 break; 00755 case AST_CONTROL_SRCUPDATE: 00756 ast_verb(3, "%s requested a source update\n", winner->name); 00757 break; 00758 case AST_CONTROL_PROCEEDING: 00759 ast_verb(3, "%s is proceeding\n", winner->name); 00760 break; 00761 case AST_CONTROL_HOLD: 00762 ast_verb(3, "%s placed call on hold\n", winner->name); 00763 break; 00764 case AST_CONTROL_UNHOLD: 00765 ast_verb(3, "%s removed call from hold\n", winner->name); 00766 break; 00767 case AST_CONTROL_OFFHOOK: 00768 case AST_CONTROL_FLASH: 00769 /* Ignore going off hook and flash */ 00770 break; 00771 case AST_CONTROL_CONNECTED_LINE: 00772 if (!tmpuser) { 00773 /* 00774 * Hold connected line update from caller until we have a 00775 * winner. 00776 */ 00777 ast_verb(3, 00778 "%s connected line has changed. Saving it until we have a winner.\n", 00779 winner->name); 00780 ast_party_connected_line_set_init(&connected, &tpargs->connected_in); 00781 if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) { 00782 ast_party_connected_line_set(&tpargs->connected_in, 00783 &connected, NULL); 00784 tpargs->pending_in_connected_update = 1; 00785 } 00786 ast_party_connected_line_free(&connected); 00787 break; 00788 } 00789 if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) { 00790 ast_verb(3, "Connected line update from %s prevented.\n", 00791 winner->name); 00792 } else { 00793 ast_verb(3, 00794 "%s connected line has changed. Saving it until answer.\n", 00795 winner->name); 00796 ast_party_connected_line_set_init(&connected, &tmpuser->connected); 00797 if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) { 00798 ast_party_connected_line_set(&tmpuser->connected, 00799 &connected, NULL); 00800 tmpuser->pending_connected_update = 1; 00801 } 00802 ast_party_connected_line_free(&connected); 00803 } 00804 break; 00805 case AST_CONTROL_REDIRECTING: 00806 /* 00807 * Always ignore because the caller is already answered 00808 * and is likely listening to MOH. 00809 */ 00810 break; 00811 case -1: 00812 ast_verb(3, "%s stopped sounds\n", winner->name); 00813 break; 00814 default: 00815 ast_debug(1, "Dunno what to do with control type %d from %s\n", 00816 f->subclass.integer, winner->name); 00817 break; 00818 } 00819 } 00820 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) { 00821 if (winner->stream) 00822 ast_stopstream(winner); 00823 tmpuser->digts = 0; 00824 ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer); 00825 tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer; 00826 tmpuser->ynidx++; 00827 ast_debug(1, "DTMF string: %s\n", tmpuser->yn); 00828 if (tmpuser->ynidx >= ynlongest) { 00829 ast_debug(1, "reached longest possible match - doing evals\n"); 00830 if (!strcmp(tmpuser->yn, tpargs->takecall)) { 00831 ast_debug(1, "Match to take the call!\n"); 00832 ast_frfree(f); 00833 return tmpuser->ochan; 00834 } 00835 if (!strcmp(tmpuser->yn, tpargs->nextindp)) { 00836 ast_debug(1, "Next in dial plan step requested.\n"); 00837 ast_frfree(f); 00838 return NULL; 00839 } 00840 } 00841 } 00842 00843 ast_frfree(f); 00844 } else { 00845 ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg); 00846 if (!dg) { 00847 /* Caller hung up. */ 00848 clear_calling_tree(findme_user_list); 00849 return NULL; 00850 } else { 00851 /* Outgoing channel hung up. */ 00852 tmpuser->state = -1; 00853 tmpuser->ochan = NULL; 00854 ast_hangup(winner); 00855 --livechannels; 00856 ast_debug(1, "live channels left %d\n", livechannels); 00857 if (!livechannels) { 00858 ast_verb(3, "no live channels left. exiting.\n"); 00859 return NULL; 00860 } 00861 } 00862 } 00863 } else { 00864 ast_debug(1, "timed out waiting for action\n"); 00865 } 00866 } 00867 00868 /* --- WAIT FOR WINNER NUMBER END! -----------*/ 00869 return NULL; 00870 }
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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 204 of file app_followme.c.
const char* defaultmoh = "default" [static] |
Default Music-On-Hold Class
Definition at line 201 of file app_followme.c.
int featuredigittimeout = 5000 [static] |
Feature Digit Timeout
Definition at line 200 of file app_followme.c.
Referenced by ast_bridge_call(), and process_config().
const char* featuredigittostr [static] |
Definition at line 199 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] |
char nextindp[20] = "2" [static] |
Definition at line 203 of file app_followme.c.
char norecordingprompt[PATH_MAX] = "followme/no-recording" [static] |
Definition at line 205 of file app_followme.c.
char optionsprompt[PATH_MAX] = "followme/options" [static] |
Definition at line 206 of file app_followme.c.
char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try" [static] |
Definition at line 207 of file app_followme.c.
char sorryprompt[PATH_MAX] = "followme/sorry" [static] |
Definition at line 209 of file app_followme.c.
char statusprompt[PATH_MAX] = "followme/status" [static] |
Definition at line 208 of file app_followme.c.
char takecall[20] = "1" [static] |
Definition at line 203 of file app_followme.c.
int ynlongest = 0 [static] |
Definition at line 197 of file app_followme.c.