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