app_followme.c File Reference

Find-Me Follow-Me application. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/paths.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/dsp.h"
#include "asterisk/app.h"
#include "asterisk/stasis_channels.h"

Include dependency graph for app_followme.c:

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_NOANSWER = (1 << 4), FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5), FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6), FOLLOWMEFLAG_PREDIAL_CALLER = (1 << 7),
  FOLLOWMEFLAG_PREDIAL_CALLEE = (1 << 8)
}
enum  { FOLLOWMEFLAG_ARG_PREDIAL_CALLER, FOLLOWMEFLAG_ARG_PREDIAL_CALLEE, FOLLOWMEFLAG_ARG_ARRAY_SIZE }

Functions

static void __fini_followmes (void)
static void __init_followmes (void)
static void __reg_module (void)
static void __unreg_module (void)
static struct call_followmealloc_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_unanswered_calls (struct findme_user_listptr *findme_user_list)
static struct numbercreate_followme_number (const char *number, int timeout, int numorder)
 Add a new number.
static void destroy_calling_node (struct findme_user *node)
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_followmefind_realtime (const char *name)
static struct ast_channelfindmeexec (struct fm_args *tpargs, struct ast_channel *caller)
static void free_numbers (struct call_followme *f)
static void init_profile (struct call_followme *f)
static int load_module (void)
 Load the module.
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 void publish_dial_end_event (struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status)
static int reload (void)
static int reload_followme (int reload)
 Reload followme application module.
static int unload_module (void)
static struct ast_channelwait_for_winner (struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, 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, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "FollowMe"
static struct ast_module_infoast_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 }, [ 'B' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLER , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLER + 1 }, [ 'b' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLEE , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLEE + 1 }, [ '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 [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"


Detailed Description

Find-Me Follow-Me application.

Author:
BJ Weschke <bweschke@btwtech.com>

Definition in file app_followme.c.


Define Documentation

#define MAX_YN_STRING   20

Maximum accept/decline DTMF string plus terminator.

Definition at line 150 of file app_followme.c.


Enumeration Type Documentation

anonymous enum

Enumerator:
FOLLOWMEFLAG_STATUSMSG 
FOLLOWMEFLAG_RECORDNAME 
FOLLOWMEFLAG_UNREACHABLEMSG 
FOLLOWMEFLAG_DISABLEHOLDPROMPT 
FOLLOWMEFLAG_NOANSWER 
FOLLOWMEFLAG_DISABLEOPTIMIZATION 
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE 
FOLLOWMEFLAG_PREDIAL_CALLER 
FOLLOWMEFLAG_PREDIAL_CALLEE 

Definition at line 230 of file app_followme.c.

00230      {
00231    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00232    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00233    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00234    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00235    FOLLOWMEFLAG_NOANSWER = (1 << 4),
00236    FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5),
00237    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6),
00238    FOLLOWMEFLAG_PREDIAL_CALLER = (1 << 7),
00239    FOLLOWMEFLAG_PREDIAL_CALLEE = (1 << 8),
00240 };

anonymous enum

Enumerator:
FOLLOWMEFLAG_ARG_PREDIAL_CALLER 
FOLLOWMEFLAG_ARG_PREDIAL_CALLEE 
FOLLOWMEFLAG_ARG_ARRAY_SIZE 

Definition at line 242 of file app_followme.c.

00242      {
00243    FOLLOWMEFLAG_ARG_PREDIAL_CALLER,
00244    FOLLOWMEFLAG_ARG_PREDIAL_CALLEE,
00245 
00246    /* note: this entry _MUST_ be the last one in the enum */
00247    FOLLOWMEFLAG_ARG_ARRAY_SIZE
00248 };


Function Documentation

static void __fini_followmes ( void   )  [static]

Definition at line 276 of file app_followme.c.

00280 {

static void __init_followmes ( void   )  [static]

Definition at line 276 of file app_followme.c.

00280 {

static void __reg_module ( void   )  [static]

Definition at line 1576 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1576 of file app_followme.c.

static struct call_followme* alloc_profile ( const char *  fmname  )  [static, read]

Allocate and initialize followme profile.

Definition at line 302 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, NULL, 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().

00303 {
00304    struct call_followme *f;
00305 
00306    if (!(f = ast_calloc(1, sizeof(*f))))
00307       return NULL;
00308 
00309    ast_mutex_init(&f->lock);
00310    ast_copy_string(f->name, fmname, sizeof(f->name));
00311    f->moh[0] = '\0';
00312    f->context[0] = '\0';
00313    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00314    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00315    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00316    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00317    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00318    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00319    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00320    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00321    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00322    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00323    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00324    return f;
00325 }

static int app_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1292 of file app_followme.c.

References call_followme::active, args, ast_answer(), AST_APP_ARG, ast_app_exec_sub(), ast_app_expand_sub_args(), ast_app_parse_options(), ast_autoservice_chan_hangup_peer(), ast_bridge_call(), ast_calloc, ast_channel_caller(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), 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_HOLD, 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_indicate(), ast_indicate_data(), 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_replace_subargument_delimiter(), 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::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_ARG_ARRAY_SIZE, FOLLOWMEFLAG_ARG_PREDIAL_CALLEE, FOLLOWMEFLAG_ARG_PREDIAL_CALLER, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_NOANSWER, FOLLOWMEFLAG_PREDIAL_CALLEE, FOLLOWMEFLAG_PREDIAL_CALLER, 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, NULL, number::number, call_followme::numbers, call_followme::optionsprompt, fm_args::optionsprompt, number::order, fm_args::pending_hold, fm_args::pending_in_connected_update, fm_args::pending_out_connected_update, call_followme::plsholdprompt, fm_args::plsholdprompt, fm_args::predial_callee, call_followme::realtime, call_followme::sorryprompt, fm_args::sorryprompt, call_followme::statusprompt, fm_args::statusprompt, fm_args::suggested_moh, call_followme::takecall, fm_args::takecall, THRESHOLD_SILENCE, and number::timeout.

01293 {
01294    struct fm_args *targs;
01295    struct ast_bridge_config config;
01296    struct call_followme *f;
01297    struct number *nm, *newnm;
01298    int res = 0;
01299    char *argstr;
01300    struct ast_channel *caller;
01301    struct ast_channel *outbound;
01302    AST_DECLARE_APP_ARGS(args,
01303       AST_APP_ARG(followmeid);
01304       AST_APP_ARG(options);
01305    );
01306    char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
01307 
01308    if (ast_strlen_zero(data)) {
01309       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01310       return -1;
01311    }
01312 
01313    argstr = ast_strdupa((char *) data);
01314 
01315    AST_STANDARD_APP_ARGS(args, argstr);
01316 
01317    if (ast_strlen_zero(args.followmeid)) {
01318       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01319       return -1;
01320    }
01321 
01322    targs = ast_calloc(1, sizeof(*targs));
01323    if (!targs) {
01324       return -1;
01325    }
01326 
01327    AST_RWLIST_RDLOCK(&followmes);
01328    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01329       if (!strcasecmp(f->name, args.followmeid) && (f->active))
01330          break;
01331    }
01332    AST_RWLIST_UNLOCK(&followmes);
01333 
01334    ast_debug(1, "New profile %s.\n", args.followmeid);
01335 
01336    if (!f) {
01337       f = find_realtime(args.followmeid);
01338    }
01339 
01340    if (!f) {
01341       ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01342       ast_free(targs);
01343       return 0;
01344    }
01345 
01346    /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
01347    if (args.options) {
01348       ast_app_parse_options(followme_opts, &targs->followmeflags, opt_args, args.options);
01349    }
01350 
01351    /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
01352    ast_mutex_lock(&f->lock);
01353    targs->mohclass = ast_strdupa(f->moh);
01354    ast_copy_string(targs->context, f->context, sizeof(targs->context));
01355    ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
01356    ast_copy_string(targs->nextindp, f->nextindp, sizeof(targs->nextindp));
01357    ast_copy_string(targs->callfromprompt, f->callfromprompt, sizeof(targs->callfromprompt));
01358    ast_copy_string(targs->norecordingprompt, f->norecordingprompt, sizeof(targs->norecordingprompt));
01359    ast_copy_string(targs->optionsprompt, f->optionsprompt, sizeof(targs->optionsprompt));
01360    ast_copy_string(targs->plsholdprompt, f->plsholdprompt, sizeof(targs->plsholdprompt));
01361    ast_copy_string(targs->statusprompt, f->statusprompt, sizeof(targs->statusprompt));
01362    ast_copy_string(targs->sorryprompt, f->sorryprompt, sizeof(targs->sorryprompt));
01363    /* Copy the numbers we're going to use into another list in case the master list should get modified 
01364       (and locked) while we're trying to do a follow-me */
01365    AST_LIST_HEAD_INIT_NOLOCK(&targs->cnumbers);
01366    AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01367       newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01368       if (newnm) {
01369          AST_LIST_INSERT_TAIL(&targs->cnumbers, newnm, entry);
01370       }
01371    }
01372    ast_mutex_unlock(&f->lock);
01373 
01374    /* PREDIAL: Preprocess any callee gosub arguments. */
01375    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLEE)
01376       && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE])) {
01377       ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
01378       targs->predial_callee =
01379          ast_app_expand_sub_args(chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]);
01380    }
01381 
01382    /* PREDIAL: Run gosub on the caller's channel */
01383    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLER)
01384       && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER])) {
01385       ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER]);
01386       ast_app_exec_sub(NULL, chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER], 0);
01387    }
01388 
01389    /* Forget the 'N' option if the call is already up. */
01390    if (ast_channel_state(chan) == AST_STATE_UP) {
01391       ast_clear_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER);
01392    }
01393 
01394    if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01395       ast_indicate(chan, AST_CONTROL_RINGING);
01396    } else {
01397       /* Answer the call */
01398       if (ast_channel_state(chan) != AST_STATE_UP) {
01399          ast_answer(chan);
01400       }
01401 
01402       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_STATUSMSG)) {
01403          ast_stream_and_wait(chan, targs->statusprompt, "");
01404       }
01405 
01406       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_RECORDNAME)) {
01407          int duration = 5;
01408 
01409          snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
01410             ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan));
01411          if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, "sln", &duration,
01412             NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
01413             goto outrun;
01414          }
01415          if (!ast_fileexists(targs->namerecloc, NULL, ast_channel_language(chan))) {
01416             targs->namerecloc[0] = '\0';
01417          }
01418       }
01419 
01420       if (!ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
01421          if (ast_streamfile(chan, targs->plsholdprompt, ast_channel_language(chan))) {
01422             goto outrun;
01423          }
01424          if (ast_waitstream(chan, "") < 0)
01425             goto outrun;
01426       }
01427       ast_moh_start(chan, targs->mohclass, NULL);
01428    }
01429 
01430    ast_channel_lock(chan);
01431    ast_connected_line_copy_from_caller(&targs->connected_in, ast_channel_caller(chan));
01432    ast_channel_unlock(chan);
01433 
01434    outbound = findmeexec(targs, chan);
01435    if (!outbound) {
01436       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01437          if (ast_channel_state(chan) != AST_STATE_UP) {
01438             ast_answer(chan);
01439          }
01440       } else {
01441          ast_moh_stop(chan);
01442       }
01443 
01444       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) {
01445          ast_stream_and_wait(chan, targs->sorryprompt, "");
01446       }
01447       res = 0;
01448    } else {
01449       caller = chan;
01450       /* Bridge the two channels. */
01451 
01452       memset(&config, 0, sizeof(config));
01453       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01454       ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01455       ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01456       config.end_bridge_callback = end_bridge_callback;
01457       config.end_bridge_callback_data = chan;
01458       config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01459 
01460       /* Update connected line to caller if available. */
01461       if (targs->pending_out_connected_update) {
01462          if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0) &&
01463             ast_channel_connected_line_macro(outbound, caller, &targs->connected_out, 1, 0)) {
01464             ast_channel_update_connected_line(caller, &targs->connected_out, NULL);
01465          }
01466       }
01467 
01468       if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01469          if (ast_channel_state(caller) != AST_STATE_UP) {
01470             ast_answer(caller);
01471          }
01472       } else {
01473          ast_moh_stop(caller);
01474       }
01475 
01476       /* Be sure no generators are left on it */
01477       ast_deactivate_generator(caller);
01478       /* Make sure channels are compatible */
01479       res = ast_channel_make_compatible(caller, outbound);
01480       if (res < 0) {
01481          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));
01482          ast_autoservice_chan_hangup_peer(caller, outbound);
01483          goto outrun;
01484       }
01485 
01486       /* Update connected line to winner if changed. */
01487       if (targs->pending_in_connected_update) {
01488          if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0) &&
01489             ast_channel_connected_line_macro(caller, outbound, &targs->connected_in, 0, 0)) {
01490             ast_channel_update_connected_line(outbound, &targs->connected_in, NULL);
01491          }
01492       }
01493 
01494       /* Put winner on hold if caller requested. */
01495       if (targs->pending_hold) {
01496          if (ast_strlen_zero(targs->suggested_moh)) {
01497             ast_indicate_data(outbound, AST_CONTROL_HOLD, NULL, 0);
01498          } else {
01499             ast_indicate_data(outbound, AST_CONTROL_HOLD,
01500                targs->suggested_moh, strlen(targs->suggested_moh) + 1);
01501          }
01502       }
01503 
01504       res = ast_bridge_call(caller, outbound, &config);
01505    }
01506 
01507 outrun:
01508    while ((nm = AST_LIST_REMOVE_HEAD(&targs->cnumbers, entry))) {
01509       ast_free(nm);
01510    }
01511    if (!ast_strlen_zero(targs->namerecloc)) {
01512       unlink(targs->namerecloc);
01513    }
01514    ast_free((char *) targs->predial_callee);
01515    ast_party_connected_line_free(&targs->connected_in);
01516    ast_party_connected_line_free(&targs->connected_out);
01517    ast_free(targs);
01518 
01519    if (f->realtime) {
01520       /* Not in list */
01521       free_numbers(f);
01522       ast_free(f);
01523    }
01524 
01525    return res;
01526 }

static void clear_caller ( struct findme_user tmpuser  )  [static]

Definition at line 571 of file app_followme.c.

References ast_hangup(), NULL, and findme_user::ochan.

Referenced by clear_unanswered_calls(), destroy_calling_node(), and wait_for_winner().

00572 {
00573    struct ast_channel *outbound;
00574 
00575    if (!tmpuser->ochan) {
00576       /* Call already cleared. */
00577       return;
00578    }
00579 
00580    outbound = tmpuser->ochan;
00581    ast_hangup(outbound);
00582    tmpuser->ochan = NULL;
00583 }

static void clear_unanswered_calls ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 585 of file app_followme.c.

References findme_user::answered, AST_LIST_TRAVERSE, and clear_caller().

Referenced by wait_for_winner().

00586 {
00587    struct findme_user *tmpuser;
00588 
00589    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00590       if (!tmpuser->answered) {
00591          clear_caller(tmpuser);
00592       }
00593    }
00594 }

static struct number* create_followme_number ( const char *  number,
int  timeout,
int  numorder 
) [static, read]

Add a new number.

Definition at line 368 of file app_followme.c.

References ast_calloc, ast_copy_string(), ast_debug, ast_strdupa, buf, NULL, number::number, number::order, number::timeout, and tmp().

Referenced by app_exec(), find_realtime(), and reload_followme().

00369 {
00370    struct number *cur;
00371    char *buf = ast_strdupa(number);
00372    char *tmp;
00373 
00374    if (!(cur = ast_calloc(1, sizeof(*cur))))
00375       return NULL;
00376 
00377    cur->timeout = timeout;
00378    if ((tmp = strchr(buf, ',')))
00379       *tmp = '\0';
00380    ast_copy_string(cur->number, buf, sizeof(cur->number));
00381    cur->order = numorder;
00382    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00383 
00384    return cur;
00385 }

static void destroy_calling_node ( struct findme_user node  )  [static]

Definition at line 596 of file app_followme.c.

References ast_free, ast_party_connected_line_free(), clear_caller(), and findme_user::connected.

Referenced by destroy_calling_tree(), and findmeexec().

00597 {
00598    clear_caller(node);
00599    ast_party_connected_line_free(&node->connected);
00600    ast_free(node);
00601 }

static void destroy_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 603 of file app_followme.c.

References AST_LIST_REMOVE_HEAD, and destroy_calling_node().

Referenced by findmeexec().

00604 {
00605    struct findme_user *fmuser;
00606 
00607    while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00608       destroy_calling_node(fmuser);
00609    }
00610 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 1271 of file app_followme.c.

References ast_channel_get_duration(), ast_channel_get_up_time(), ast_channel_lock, ast_channel_unlock, buf, end, and pbx_builtin_setvar_helper().

01272 {
01273    char buf[80];
01274    time_t end;
01275    struct ast_channel *chan = data;
01276 
01277    time(&end);
01278 
01279    ast_channel_lock(chan);
01280    snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
01281    pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01282    snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
01283    pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01284    ast_channel_unlock(chan);
01285 }

static void end_bridge_callback_data_fixup ( struct ast_bridge_config bconfig,
struct ast_channel originator,
struct ast_channel terminator 
) [static]

Definition at line 1287 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

01288 {
01289    bconfig->end_bridge_callback_data = originator;
01290 }

static struct call_followme* find_realtime ( const char *  name  )  [static, read]

Definition at line 1190 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, NULL, call_followme::numbers, profile_set_param(), call_followme::realtime, SENTINEL, str, number::timeout, ast_variable::value, and var.

Referenced by app_exec().

01191 {
01192    struct ast_variable *var;
01193    struct ast_variable *v;
01194    struct ast_config *cfg;
01195    const char *catg;
01196    struct call_followme *new_follower;
01197    struct ast_str *str;
01198 
01199    str = ast_str_create(16);
01200    if (!str) {
01201       return NULL;
01202    }
01203 
01204    var = ast_load_realtime("followme", "name", name, SENTINEL);
01205    if (!var) {
01206       ast_free(str);
01207       return NULL;
01208    }
01209 
01210    if (!(new_follower = alloc_profile(name))) {
01211       ast_variables_destroy(var);
01212       ast_free(str);
01213       return NULL;
01214    }
01215 
01216    for (v = var; v; v = v->next) {
01217       if (!strcasecmp(v->name, "active")) {
01218          if (ast_false(v->value)) {
01219             ast_mutex_destroy(&new_follower->lock);
01220             ast_free(new_follower);
01221             ast_variables_destroy(var);
01222             ast_free(str);
01223             return NULL;
01224          }
01225       } else {
01226          profile_set_param(new_follower, v->name, v->value, 0, 0);
01227       }
01228    }
01229 
01230    ast_variables_destroy(var);
01231    new_follower->realtime = 1;
01232 
01233    /* Load numbers */
01234    cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01235       name, SENTINEL);
01236    if (!cfg) {
01237       ast_mutex_destroy(&new_follower->lock);
01238       ast_free(new_follower);
01239       ast_free(str);
01240       return NULL;
01241    }
01242 
01243    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01244       const char *numstr;
01245       const char *timeoutstr;
01246       const char *ordstr;
01247       int timeout;
01248       struct number *cur;
01249 
01250       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01251          continue;
01252       }
01253       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01254          || sscanf(timeoutstr, "%30d", &timeout) != 1
01255          || timeout < 1) {
01256          timeout = 25;
01257       }
01258       /* This one has to exist; it was part of the query */
01259       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01260       ast_str_set(&str, 0, "%s", numstr);
01261       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01262          AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01263       }
01264    }
01265    ast_config_destroy(cfg);
01266 
01267    ast_free(str);
01268    return new_follower;
01269 }

static struct ast_channel* findmeexec ( struct fm_args tpargs,
struct ast_channel caller 
) [static, read]

Definition at line 988 of file app_followme.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_call(), ast_calloc, ast_cause2str(), ast_channel_caller(), ast_channel_connected(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_language(), ast_channel_lock_both, ast_channel_musicclass(), ast_channel_nativeformats(), ast_channel_publish_dial(), ast_channel_req_accountcodes(), AST_CHANNEL_REQUESTOR_BRIDGE_PEER, ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, AST_LIST_APPEND_LIST, AST_LIST_EMPTY, AST_LIST_HEAD_NOLOCK_INIT_VALUE, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_CURRENT, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log, ast_pre_call(), ast_request(), ast_strlen_zero, ast_test_flag, ast_verb, fm_args::cnumbers, findme_user::connected, fm_args::connected_out, fm_args::context, destroy_calling_node(), destroy_calling_tree(), findme_user::dialarg, FOLLOWMEFLAG_DISABLEOPTIMIZATION, fm_args::followmeflags, LOG_ERROR, LOG_WARNING, NULL, number::number, findme_user::ochan, number::order, findme_user::pending_connected_update, fm_args::pending_out_connected_update, fm_args::predial_callee, S_COR, findme_user::state, number::timeout, and wait_for_winner().

Referenced by app_exec().

00989 {
00990    struct number *nm;
00991    struct ast_channel *winner = NULL;
00992    char num[512];
00993    int dg, idx;
00994    char *rest, *number;
00995    struct findme_user *tmpuser;
00996    struct findme_user *fmuser;
00997    struct findme_user_listptr findme_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00998    struct findme_user_listptr new_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00999 
01000    for (idx = 1; !ast_check_hangup(caller); ++idx) {
01001       /* Find next followme numbers to dial. */
01002       AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
01003          if (nm->order == idx) {
01004             break;
01005          }
01006       }
01007       if (!nm) {
01008          ast_verb(3, "No more steps left.\n");
01009          break;
01010       }
01011 
01012       ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
01013 
01014       /*
01015        * Put all active outgoing channels into autoservice.
01016        *
01017        * This needs to be done because ast_exists_extension() may put
01018        * the caller into autoservice.
01019        */
01020       AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
01021          if (tmpuser->ochan) {
01022             ast_autoservice_start(tmpuser->ochan);
01023          }
01024       }
01025 
01026       /* Create all new outgoing calls */
01027       ast_copy_string(num, nm->number, sizeof(num));
01028       for (number = num; number; number = rest) {
01029          struct ast_channel *outbound;
01030 
01031          rest = strchr(number, '&');
01032          if (rest) {
01033             *rest++ = 0;
01034          }
01035 
01036          /* We check if the extension exists, before creating the ast_channel struct */
01037          if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(ast_channel_caller(caller)->id.number.valid, ast_channel_caller(caller)->id.number.str, NULL))) {
01038             ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
01039             continue;
01040          }
01041 
01042          tmpuser = ast_calloc(1, sizeof(*tmpuser));
01043          if (!tmpuser) {
01044             continue;
01045          }
01046 
01047          if (ast_strlen_zero(tpargs->context)) {
01048             snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s%s",
01049                number,
01050                ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
01051                   ? "/n" : "/m");
01052          } else {
01053             snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s@%s%s",
01054                number, tpargs->context,
01055                ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION)
01056                   ? "/n" : "/m");
01057          }
01058 
01059          outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
01060             tmpuser->dialarg, &dg);
01061          if (!outbound) {
01062             ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
01063                tmpuser->dialarg, ast_cause2str(dg));
01064             ast_free(tmpuser);
01065             continue;
01066          }
01067 
01068          ast_channel_lock_both(caller, outbound);
01069          ast_connected_line_copy_from_caller(ast_channel_connected(outbound), ast_channel_caller(caller));
01070          ast_channel_inherit_variables(caller, outbound);
01071          ast_channel_datastore_inherit(caller, outbound);
01072          ast_channel_language_set(outbound, ast_channel_language(caller));
01073          ast_channel_req_accountcodes(outbound, caller, AST_CHANNEL_REQUESTOR_BRIDGE_PEER);
01074          ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
01075          ast_channel_unlock(outbound);
01076          ast_channel_unlock(caller);
01077 
01078          tmpuser->ochan = outbound;
01079          tmpuser->state = 0;
01080          AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
01081       }
01082 
01083       /*
01084        * PREDIAL: Run gosub on all of the new callee channels
01085        *
01086        * We run the callee predial before ast_call() in case the user
01087        * wishes to do something on the newly created channels before
01088        * the channel does anything important.
01089        */
01090       if (tpargs->predial_callee && !AST_LIST_EMPTY(&new_user_list)) {
01091          /* Put caller into autoservice. */
01092          ast_autoservice_start(caller);
01093 
01094          /* Run predial on all new outgoing calls. */
01095          AST_LIST_TRAVERSE(&new_user_list, tmpuser, entry) {
01096             ast_pre_call(tmpuser->ochan, tpargs->predial_callee);
01097          }
01098 
01099          /* Take caller out of autoservice. */
01100          if (ast_autoservice_stop(caller)) {
01101             /*
01102              * Caller hungup.
01103              *
01104              * Destoy all new outgoing calls.
01105              */
01106             while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
01107                destroy_calling_node(tmpuser);
01108             }
01109 
01110             /* Take all active outgoing channels out of autoservice. */
01111             AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
01112                if (tmpuser->ochan) {
01113                   ast_autoservice_stop(tmpuser->ochan);
01114                }
01115             }
01116             break;
01117          }
01118       }
01119 
01120       /* Start all new outgoing calls */
01121       AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
01122          ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
01123          if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
01124             ast_verb(3, "couldn't reach at this number.\n");
01125             AST_LIST_REMOVE_CURRENT(entry);
01126 
01127             /* Destroy this failed new outgoing call. */
01128             destroy_calling_node(tmpuser);
01129             continue;
01130          }
01131 
01132          ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL);
01133       }
01134       AST_LIST_TRAVERSE_SAFE_END;
01135 
01136       /* Take all active outgoing channels out of autoservice. */
01137       AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
01138          if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
01139             /* Existing outgoing call hungup. */
01140             AST_LIST_REMOVE_CURRENT(entry);
01141             destroy_calling_node(tmpuser);
01142          }
01143       }
01144       AST_LIST_TRAVERSE_SAFE_END;
01145 
01146       if (AST_LIST_EMPTY(&new_user_list)) {
01147          /* No new channels remain at this order level.  If there were any at all. */
01148          continue;
01149       }
01150 
01151       /* Add new outgoing channels to the findme list. */
01152       AST_LIST_APPEND_LIST(&findme_user_list, &new_user_list, entry);
01153 
01154       winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
01155       if (!winner) {
01156          /* Remove all dead outgoing nodes. */
01157          AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
01158             if (!tmpuser->ochan) {
01159                AST_LIST_REMOVE_CURRENT(entry);
01160                destroy_calling_node(tmpuser);
01161             }
01162          }
01163          AST_LIST_TRAVERSE_SAFE_END;
01164          continue;
01165       }
01166 
01167       /* Destroy losing calls up to the winner.  The rest will be destroyed later. */
01168       while ((fmuser = AST_LIST_REMOVE_HEAD(&findme_user_list, entry))) {
01169          if (fmuser->ochan == winner) {
01170             /*
01171              * Pass any connected line info up.
01172              *
01173              * NOTE: This code must be in line with destroy_calling_node().
01174              */
01175             tpargs->connected_out = fmuser->connected;
01176             tpargs->pending_out_connected_update = fmuser->pending_connected_update;
01177             ast_free(fmuser);
01178             break;
01179          } else {
01180             /* Destroy losing call. */
01181             destroy_calling_node(fmuser);
01182          }
01183       }
01184       break;
01185    }
01186    destroy_calling_tree(&findme_user_list);
01187    return winner;
01188 }

static void free_numbers ( struct call_followme f  )  [static]

Definition at line 279 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().

00280 {
00281    /* Free numbers attached to the profile */
00282    struct number *prev;
00283 
00284    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00285       /* Free the number */
00286       ast_free(prev);
00287    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00288 
00289    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00290       /* Free the blacklisted number */
00291       ast_free(prev);
00292    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00293 
00294    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00295       /* Free the whitelisted number */
00296       ast_free(prev);
00297    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00298 }

static void init_profile ( struct call_followme f  )  [static]

Definition at line 327 of file app_followme.c.

References call_followme::active, ast_copy_string(), and call_followme::moh.

Referenced by reload_followme().

00328 {
00329    f->active = 1;
00330    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00331 }

static int load_module ( void   )  [static]

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 1556 of file app_followme.c.

References app_exec, AST_MODULE_LOAD_DECLINE, ast_register_application_xml, and reload_followme().

01557 {
01558    if(!reload_followme(0))
01559       return AST_MODULE_LOAD_DECLINE;
01560 
01561    return ast_register_application_xml(app, app_exec);
01562 }

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 336 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().

00337 {
00338 
00339    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00340       ast_copy_string(f->moh, val, sizeof(f->moh));
00341    else if (!strcasecmp(param, "context")) 
00342       ast_copy_string(f->context, val, sizeof(f->context));
00343    else if (!strcasecmp(param, "takecall"))
00344       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00345    else if (!strcasecmp(param, "declinecall"))
00346       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00347    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00348       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00349    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00350       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00351    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00352       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00353    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00354       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00355    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00356       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00357    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00358       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00359    else if (failunknown) {
00360       if (linenum >= 0)
00361          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00362       else
00363          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00364    }
00365 }

static void publish_dial_end_event ( struct ast_channel in,
struct findme_user_listptr findme_user_list,
struct ast_channel exception,
const char *  status 
) [static]

Definition at line 560 of file app_followme.c.

References ast_channel_publish_dial(), AST_LIST_TRAVERSE, NULL, and findme_user::ochan.

00561 {
00562    struct findme_user *tmpuser;
00563 
00564    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00565       if (tmpuser->ochan && tmpuser->ochan != exception) {
00566          ast_channel_publish_dial(in, tmpuser->ochan, NULL, status);
00567       }
00568    }
00569 }

static int reload ( void   )  [static]

Definition at line 1564 of file app_followme.c.

References reload_followme().

01565 {
01566    reload_followme(1);
01567 
01568    return 0;
01569 }

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

Definition at line 388 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_strdupa, 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, NULL, call_followme::numbers, profile_set_param(), number::timeout, tmp(), ast_variable::value, and var.

Referenced by load_module(), and reload().

00389 {
00390    struct call_followme *f;
00391    struct ast_config *cfg;
00392    char *cat = NULL, *tmp;
00393    struct ast_variable *var;
00394    struct number *cur, *nm;
00395    char *numberstr;
00396    int timeout;
00397    int numorder;
00398    const char *takecallstr;
00399    const char *declinecallstr;
00400    const char *tmpstr;
00401    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00402 
00403    if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00404       ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00405       return 0;
00406    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00407       return 0;
00408    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00409       ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
00410       return 0;
00411    }
00412 
00413    AST_RWLIST_WRLOCK(&followmes);
00414 
00415    /* Reset Global Var Values */
00416    featuredigittimeout = 5000;
00417 
00418    /* Mark all profiles as inactive for the moment */
00419    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00420       f->active = 0;
00421    }
00422 
00423    featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00424 
00425    if (!ast_strlen_zero(featuredigittostr)) {
00426       if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00427          featuredigittimeout = 5000;
00428    }
00429 
00430    if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
00431       ast_copy_string(takecall, takecallstr, sizeof(takecall));
00432    }
00433 
00434    if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
00435       ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00436    }
00437 
00438    if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
00439       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00440    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
00441       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00442    }
00443 
00444    if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
00445       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00446    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
00447       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00448    }
00449 
00450 
00451    if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
00452       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00453    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
00454       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00455    }
00456 
00457    if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
00458       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00459    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
00460       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00461    }
00462 
00463    if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
00464       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00465    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
00466       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00467    }
00468 
00469    if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
00470       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00471    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
00472       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00473    }
00474 
00475    /* Chug through config file */
00476    while ((cat = ast_category_browse(cfg, cat))) {
00477       int new = 0;
00478 
00479       if (!strcasecmp(cat, "general"))
00480          continue;
00481 
00482       /* Look for an existing one */
00483       AST_LIST_TRAVERSE(&followmes, f, entry) {
00484          if (!strcasecmp(f->name, cat))
00485             break;
00486       }
00487 
00488       ast_debug(1, "New profile %s.\n", cat);
00489 
00490       if (!f) {
00491          /* Make one then */
00492          f = alloc_profile(cat);
00493          new = 1;
00494       }
00495 
00496       /* Totally fail if we fail to find/create an entry */
00497       if (!f)
00498          continue;
00499 
00500       if (!new)
00501          ast_mutex_lock(&f->lock);
00502       /* Re-initialize the profile */
00503       init_profile(f);
00504       free_numbers(f);
00505       var = ast_variable_browse(cfg, cat);
00506       while (var) {
00507          if (!strcasecmp(var->name, "number")) {
00508             int idx = 0;
00509 
00510             /* Add a new number */
00511             numberstr = ast_strdupa(var->value);
00512             if ((tmp = strchr(numberstr, ','))) {
00513                *tmp++ = '\0';
00514                timeout = atoi(tmp);
00515                if (timeout < 0) {
00516                   timeout = 25;
00517                }
00518                if ((tmp = strchr(tmp, ','))) {
00519                   *tmp++ = '\0';
00520                   numorder = atoi(tmp);
00521                   if (numorder < 0)
00522                      numorder = 0;
00523                } else 
00524                   numorder = 0;
00525             } else {
00526                timeout = 25;
00527                numorder = 0;
00528             }
00529 
00530             if (!numorder) {
00531                idx = 1;
00532                AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
00533                   idx++;
00534                numorder = idx;
00535             }
00536             cur = create_followme_number(numberstr, timeout, numorder);
00537             if (cur) {
00538                AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00539             }
00540          } else {
00541             profile_set_param(f, var->name, var->value, var->lineno, 1);
00542             ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00543          }
00544          var = var->next;
00545       } /* End while(var) loop */
00546 
00547       if (!new) 
00548          ast_mutex_unlock(&f->lock);
00549       else
00550          AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00551    }
00552 
00553    ast_config_destroy(cfg);
00554 
00555    AST_RWLIST_UNLOCK(&followmes);
00556 
00557    return 1;
00558 }

static int unload_module ( void   )  [static]

Definition at line 1528 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().

01529 {
01530    struct call_followme *f;
01531 
01532    ast_unregister_application(app);
01533 
01534    /* Free Memory. Yeah! I'm free! */
01535    AST_RWLIST_WRLOCK(&followmes);
01536    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01537       free_numbers(f);
01538       ast_free(f);
01539    }
01540 
01541    AST_RWLIST_UNLOCK(&followmes);
01542 
01543    return 0;
01544 }

static struct ast_channel* wait_for_winner ( struct findme_user_listptr findme_user_list,
struct number nm,
struct ast_channel caller,
struct fm_args tpargs 
) [static, read]

Definition at line 612 of file app_followme.c.

References findme_user::answered, ARRAY_LEN, AST_CAUSE_NORMAL_CLEARING, ast_channel_hangupcause_set(), ast_channel_language(), ast_channel_name(), ast_channel_publish_dial(), ast_channel_sched(), ast_channel_stream(), ast_channel_timingfunc(), 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_PVT_CAUSE_CODE, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_indicate_data(), 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_caller(), clear_unanswered_calls(), 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_frame_subclass::integer, LOG_NOTICE, LOG_WARNING, fm_args::namerecloc, fm_args::nextindp, fm_args::norecordingprompt, NULL, findme_user::ochan, fm_args::optionsprompt, findme_user::pending_connected_update, fm_args::pending_hold, fm_args::pending_in_connected_update, ast_frame::ptr, publish_dial_end_event(), findme_user::state, ast_frame::subclass, fm_args::suggested_moh, fm_args::takecall, number::timeout, ast_frame::uint32, findme_user::yn, and findme_user::ynidx.

Referenced by findmeexec().

00613 {
00614    struct ast_party_connected_line connected;
00615    struct ast_channel *watchers[256];
00616    int pos;
00617    struct ast_channel *winner;
00618    struct ast_frame *f;
00619    struct findme_user *tmpuser;
00620    int to = 0;
00621    int livechannels;
00622    int tmpto;
00623    long totalwait = 0, wtd = 0, towas = 0;
00624    char *callfromname;
00625    char *pressbuttonname;
00626 
00627    /* ------------ wait_for_winner_channel start --------------- */ 
00628 
00629    callfromname = ast_strdupa(tpargs->callfromprompt);
00630    pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00631 
00632    totalwait = nm->timeout * 1000;
00633 
00634    for (;;) {
00635       to = 1000;
00636       pos = 1; 
00637       livechannels = 0;
00638       watchers[0] = caller;
00639 
00640       winner = NULL;
00641       AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00642          if (!tmpuser->ochan) {
00643             continue;
00644          }
00645          if (tmpuser->state == 3) {
00646             tmpuser->digts += (towas - wtd);
00647          }
00648          if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00649             ast_verb(3, "<%s> We've been waiting for digits longer than we should have.\n",
00650                ast_channel_name(tmpuser->ochan));
00651             if (!ast_strlen_zero(tpargs->namerecloc)) {
00652                tmpuser->state = 1;
00653                tmpuser->digts = 0;
00654                if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
00655                   ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00656                } else {
00657                   ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00658                   clear_caller(tmpuser);
00659                   continue;
00660                }
00661             } else {
00662                tmpuser->state = 2;
00663                tmpuser->digts = 0;
00664                if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00665                   ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00666                else {
00667                   ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00668                   clear_caller(tmpuser);
00669                   continue;
00670                }
00671             }
00672          }
00673          if (ast_channel_stream(tmpuser->ochan)) {
00674             ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00675             tmpto = ast_sched_wait(ast_channel_sched(tmpuser->ochan));
00676             if (tmpto > 0 && tmpto < to)
00677                to = tmpto;
00678             else if (tmpto < 0 && !ast_channel_timingfunc(tmpuser->ochan)) {
00679                ast_stopstream(tmpuser->ochan);
00680                switch (tmpuser->state) {
00681                case 1:
00682                   ast_verb(3, "<%s> Playback of the call-from file appears to be done.\n",
00683                      ast_channel_name(tmpuser->ochan));
00684                   if (!ast_streamfile(tmpuser->ochan, tpargs->namerecloc, ast_channel_language(tmpuser->ochan))) {
00685                      tmpuser->state = 2;
00686                   } else {
00687                      ast_log(LOG_NOTICE, "<%s> Unable to playback %s. Maybe the caller didn't record their name?\n",
00688                         ast_channel_name(tmpuser->ochan), tpargs->namerecloc);
00689                      memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00690                      tmpuser->ynidx = 0;
00691                      if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan)))
00692                         tmpuser->state = 3;
00693                      else {
00694                         ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00695                         clear_caller(tmpuser);
00696                         continue;
00697                      }
00698                   }
00699                   break;
00700                case 2:
00701                   ast_verb(3, "<%s> Playback of name file appears to be done.\n",
00702                      ast_channel_name(tmpuser->ochan));
00703                   memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00704                   tmpuser->ynidx = 0;
00705                   if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan))) {
00706                      tmpuser->state = 3;
00707                   } else {
00708                      clear_caller(tmpuser);
00709                      continue;
00710                   }
00711                   break;
00712                case 3:
00713                   ast_verb(3, "<%s> Playback of the next step file appears to be done.\n",
00714                      ast_channel_name(tmpuser->ochan));
00715                   tmpuser->digts = 0;
00716                   break;
00717                default:
00718                   break;
00719                }
00720             }
00721          }
00722          watchers[pos++] = tmpuser->ochan;
00723          livechannels++;
00724       }
00725       if (!livechannels) {
00726          ast_verb(3, "No live channels left for this step.\n");
00727          return NULL;
00728       }
00729 
00730       tmpto = to;
00731       if (to < 0) {
00732          to = 1000;
00733          tmpto = 1000;
00734       }
00735       towas = to;
00736       winner = ast_waitfor_n(watchers, pos, &to);
00737       tmpto -= to;
00738       totalwait -= tmpto;
00739       wtd = to;
00740       if (totalwait <= 0) {
00741          ast_verb(3, "We've hit our timeout for this step. Dropping unanswered calls and starting the next step.\n");
00742          clear_unanswered_calls(findme_user_list);
00743          return NULL;
00744       }
00745       if (winner) {
00746          /* Need to find out which channel this is */
00747          if (winner != caller) {
00748             /* The winner is an outgoing channel. */
00749             AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00750                if (tmpuser->ochan == winner) {
00751                   break;
00752                }
00753             }
00754          } else {
00755             tmpuser = NULL;
00756          }
00757 
00758          f = ast_read(winner);
00759          if (f) {
00760             if (f->frametype == AST_FRAME_CONTROL) {
00761                switch (f->subclass.integer) {
00762                case AST_CONTROL_HANGUP:
00763                   ast_verb(3, "%s received a hangup frame.\n", ast_channel_name(winner));
00764                   if (f->data.uint32) {
00765                      ast_channel_hangupcause_set(winner, f->data.uint32);
00766                   }
00767                   if (!tmpuser) {
00768                      ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
00769                      publish_dial_end_event(caller, findme_user_list, NULL, "CANCEL");
00770                      ast_frfree(f);
00771                      return NULL;
00772                   }
00773                   clear_caller(tmpuser);
00774                   break;
00775                case AST_CONTROL_ANSWER:
00776                   if (!tmpuser) {
00777                      /* The caller answered?  We want an outgoing channel to answer. */
00778                      break;
00779                   }
00780                   ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
00781                   ast_channel_publish_dial(caller, winner, NULL, "ANSWER");
00782                   publish_dial_end_event(caller, findme_user_list, winner, "CANCEL");
00783                   tmpuser->answered = 1;
00784                   /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
00785                   ast_channel_hangupcause_set(winner, AST_CAUSE_NORMAL_CLEARING);
00786                   ast_channel_hangupcause_set(caller, AST_CAUSE_NORMAL_CLEARING);
00787                   ast_verb(3, "Starting playback of %s\n", callfromname);
00788                   if (!ast_strlen_zero(tpargs->namerecloc)) {
00789                      if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
00790                         ast_sched_runq(ast_channel_sched(winner));
00791                         tmpuser->state = 1;
00792                      } else {
00793                         ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00794                         clear_caller(tmpuser);
00795                      }
00796                   } else {
00797                      tmpuser->state = 2;
00798                      if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00799                         ast_sched_runq(ast_channel_sched(tmpuser->ochan));
00800                      else {
00801                         ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00802                         clear_caller(tmpuser);
00803                      }
00804                   }
00805                   break;
00806                case AST_CONTROL_BUSY:
00807                   ast_verb(3, "%s is busy\n", ast_channel_name(winner));
00808                   if (tmpuser) {
00809                      /* Outbound call was busy.  Drop it. */
00810                      ast_channel_publish_dial(caller, winner, NULL, "BUSY");
00811                      clear_caller(tmpuser);
00812                   }
00813                   break;
00814                case AST_CONTROL_CONGESTION:
00815                   ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner));
00816                   if (tmpuser) {
00817                      /* Outbound call was congested.  Drop it. */
00818                      ast_channel_publish_dial(caller, winner, NULL, "CONGESTION");
00819                      clear_caller(tmpuser);
00820                   }
00821                   break;
00822                case AST_CONTROL_RINGING:
00823                   ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
00824                   break;
00825                case AST_CONTROL_PROGRESS:
00826                   ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
00827                   break;
00828                case AST_CONTROL_VIDUPDATE:
00829                   ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
00830                   break;
00831                case AST_CONTROL_SRCUPDATE:
00832                   ast_verb(3, "%s requested a source update\n", ast_channel_name(winner));
00833                   break;
00834                case AST_CONTROL_PROCEEDING:
00835                   ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
00836                   break;
00837                case AST_CONTROL_HOLD:
00838                   ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
00839                   if (!tmpuser) {
00840                      /* Caller placed outgoing calls on hold. */
00841                      tpargs->pending_hold = 1;
00842                      if (f->data.ptr) {
00843                         ast_copy_string(tpargs->suggested_moh, f->data.ptr,
00844                            sizeof(tpargs->suggested_moh));
00845                      } else {
00846                         tpargs->suggested_moh[0] = '\0';
00847                      }
00848                   } else {
00849                      /*
00850                       * Outgoing call placed caller on hold.
00851                       *
00852                       * Ignore because the outgoing call should not be able to place
00853                       * the caller on hold until after they are bridged.
00854                       */
00855                   }
00856                   break;
00857                case AST_CONTROL_UNHOLD:
00858                   ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner));
00859                   if (!tmpuser) {
00860                      /* Caller removed outgoing calls from hold. */
00861                      tpargs->pending_hold = 0;
00862                   } else {
00863                      /*
00864                       * Outgoing call removed caller from hold.
00865                       *
00866                       * Ignore because the outgoing call should not be able to place
00867                       * the caller on hold until after they are bridged.
00868                       */
00869                   }
00870                   break;
00871                case AST_CONTROL_OFFHOOK:
00872                case AST_CONTROL_FLASH:
00873                   /* Ignore going off hook and flash */
00874                   break;
00875                case AST_CONTROL_CONNECTED_LINE:
00876                   if (!tmpuser) {
00877                      /*
00878                       * Hold connected line update from caller until we have a
00879                       * winner.
00880                       */
00881                      ast_verb(3,
00882                         "%s connected line has changed. Saving it until we have a winner.\n",
00883                         ast_channel_name(winner));
00884                      ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
00885                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00886                         ast_party_connected_line_set(&tpargs->connected_in,
00887                            &connected, NULL);
00888                         tpargs->pending_in_connected_update = 1;
00889                      }
00890                      ast_party_connected_line_free(&connected);
00891                      break;
00892                   }
00893                   if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
00894                      ast_verb(3, "Connected line update from %s prevented.\n",
00895                         ast_channel_name(winner));
00896                   } else {
00897                      ast_verb(3,
00898                         "%s connected line has changed. Saving it until answer.\n",
00899                         ast_channel_name(winner));
00900                      ast_party_connected_line_set_init(&connected, &tmpuser->connected);
00901                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00902                         ast_party_connected_line_set(&tmpuser->connected,
00903                            &connected, NULL);
00904                         tmpuser->pending_connected_update = 1;
00905                      }
00906                      ast_party_connected_line_free(&connected);
00907                   }
00908                   break;
00909                case AST_CONTROL_REDIRECTING:
00910                   /*
00911                    * Ignore because we are masking the FollowMe search progress to
00912                    * the caller.
00913                    */
00914                   break;
00915                case AST_CONTROL_PVT_CAUSE_CODE:
00916                   ast_indicate_data(caller, f->subclass.integer, f->data.ptr, f->datalen);
00917                   break;
00918                case -1:
00919                   ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner));
00920                   break;
00921                default:
00922                   ast_debug(1, "Dunno what to do with control type %d from %s\n",
00923                      f->subclass.integer, ast_channel_name(winner));
00924                   break;
00925                }
00926             } 
00927             if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00928                int cmp_len;
00929 
00930                if (ast_channel_stream(winner))
00931                   ast_stopstream(winner);
00932                tmpuser->digts = 0;
00933                ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
00934                if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
00935                   tmpuser->yn[tmpuser->ynidx++] = f->subclass.integer;
00936                } else {
00937                   /* Discard oldest digit. */
00938                   memmove(tmpuser->yn, tmpuser->yn + 1,
00939                      sizeof(tmpuser->yn) - 2 * sizeof(tmpuser->yn[0]));
00940                   tmpuser->yn[ARRAY_LEN(tmpuser->yn) - 2] = f->subclass.integer;
00941                }
00942                ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00943                cmp_len = strlen(tpargs->takecall);
00944                if (cmp_len <= tmpuser->ynidx
00945                   && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->takecall)) {
00946                   ast_debug(1, "Match to take the call!\n");
00947                   ast_frfree(f);
00948                   return tmpuser->ochan;
00949                }
00950                cmp_len = strlen(tpargs->nextindp);
00951                if (cmp_len <= tmpuser->ynidx
00952                   && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->nextindp)) {
00953                   ast_debug(1, "Declined to take the call.\n");
00954                   clear_caller(tmpuser);
00955                }
00956             }
00957 
00958             ast_frfree(f);
00959          } else {
00960             ast_debug(1, "we didn't get a frame. hanging up.\n");
00961             if (!tmpuser) {
00962                /* Caller hung up. */
00963                ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
00964                return NULL;
00965             }
00966             /* Outgoing channel hung up. */
00967             ast_channel_publish_dial(caller, winner, NULL, "NOANSWER");
00968             clear_caller(tmpuser);
00969          }
00970       } else {
00971          ast_debug(1, "timed out waiting for action\n");
00972       }
00973    }
00974 
00975    /* Unreachable. */
00976 }


Variable Documentation

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, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1576 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 147 of file app_followme.c.

Definition at line 1576 of file app_followme.c.

char callfromprompt[PATH_MAX] = "followme/call-from" [static]

Definition at line 268 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 264 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 263 of file app_followme.c.

const char* featuredigittostr [static]

Definition at line 262 of file app_followme.c.

struct ast_app_option followme_opts[128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'B' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLER , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLER + 1 }, [ 'b' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLEE , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLEE + 1 }, [ '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]

Definition at line 260 of file app_followme.c.

Referenced by app_exec().

char nextindp[MAX_YN_STRING] = "2" [static]

Definition at line 267 of file app_followme.c.

char norecordingprompt[PATH_MAX] = "followme/no-recording" [static]

Definition at line 269 of file app_followme.c.

char optionsprompt[PATH_MAX] = "followme/options" [static]

Definition at line 270 of file app_followme.c.

char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try" [static]

Definition at line 271 of file app_followme.c.

char sorryprompt[PATH_MAX] = "followme/sorry" [static]

Definition at line 273 of file app_followme.c.

char statusprompt[PATH_MAX] = "followme/status" [static]

Definition at line 272 of file app_followme.c.

char takecall[MAX_YN_STRING] = "1" [static]

Definition at line 266 of file app_followme.c.


Generated on Thu Apr 16 06:28:29 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6