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