Sat Feb 11 06:33:43 2012

Asterisk developer's documentation


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 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...

Enumerations

enum  {
  FOLLOWMEFLAG_STATUSMSG = (1 << 0), FOLLOWMEFLAG_RECORDNAME = (1 << 1), FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2), FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
  FOLLOWMEFLAG_NOANSWER = (1 << 4), FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5), FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6)
}

Functions

static void __fini_followmes (void)
static void __init_followmes (void)
static void __reg_module (void)
static void __unreg_module (void)
static struct call_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_calling_tree (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_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 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_channelwait_for_winner (struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "FollowMe"
static struct ast_module_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 }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'l' ] = { .flag = FOLLOWMEFLAG_DISABLEOPTIMIZATION }, [ 'N' ] = { .flag = FOLLOWMEFLAG_NOANSWER }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG },}
static char nextindp [20] = "2"
static char norecordingprompt [PATH_MAX] = "followme/no-recording"
static char optionsprompt [PATH_MAX] = "followme/options"
static char plsholdprompt [PATH_MAX] = "followme/pls-hold-while-try"
static char sorryprompt [PATH_MAX] = "followme/sorry"
static char statusprompt [PATH_MAX] = "followme/status"
static char takecall [20] = "1"
static int ynlongest = 0


Detailed Description

Find-Me Follow-Me application.

Author:
BJ Weschke <bweschke@btwtech.com>

Definition in 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 

Definition at line 197 of file app_followme.c.

00197      {
00198    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00199    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00200    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00201    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00202    FOLLOWMEFLAG_NOANSWER = (1 << 4),
00203    FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5),
00204    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6),
00205 };


Function Documentation

static void __fini_followmes ( void   )  [static]

Definition at line 232 of file app_followme.c.

00236 {

static void __init_followmes ( void   )  [static]

Definition at line 232 of file app_followme.c.

00236 {

static void __reg_module ( void   )  [static]

Definition at line 1390 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1390 of file app_followme.c.

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

Allocate and initialize followme profile.

Definition at line 258 of file app_followme.c.

References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, call_followme::blnumbers, call_followme::callfromprompt, call_followme::context, f, call_followme::lock, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::numbers, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, call_followme::takecall, and call_followme::wlnumbers.

Referenced by find_realtime(), and reload_followme().

00259 {
00260    struct call_followme *f;
00261 
00262    if (!(f = ast_calloc(1, sizeof(*f))))
00263       return NULL;
00264 
00265    ast_mutex_init(&f->lock);
00266    ast_copy_string(f->name, fmname, sizeof(f->name));
00267    f->moh[0] = '\0';
00268    f->context[0] = '\0';
00269    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00270    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00271    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00272    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00273    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00274    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00275    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00276    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00277    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00278    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00279    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00280    return f;
00281 }

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

Definition at line 1150 of file app_followme.c.

References ast_channel::_state, call_followme::active, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_connected_line_macro(), ast_channel_language(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_update_connected_line(), ast_clear_flag, ast_config_AST_SPOOL_DIR, ast_connected_line_copy_from_caller(), AST_CONTROL_RINGING, ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_get_threshold_from_settings(), AST_FEATURE_AUTOMON, AST_FEATURE_REDIRECT, ast_fileexists(), ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_moh_start(), ast_moh_stop(), ast_mutex_lock, ast_mutex_unlock, ast_party_connected_line_free(), ast_play_and_record(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_channel::caller, call_followme::callfromprompt, fm_args::callfromprompt, fm_args::chan, fm_args::cnumbers, fm_args::connected_in, fm_args::connected_out, call_followme::context, fm_args::context, create_followme_number(), end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, f, ast_bridge_config::features_callee, ast_bridge_config::features_caller, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_NOANSWER, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), call_followme::lock, LOG_WARNING, call_followme::moh, fm_args::mohclass, call_followme::name, fm_args::namerecloc, call_followme::nextindp, fm_args::nextindp, call_followme::norecordingprompt, fm_args::norecordingprompt, number::number, call_followme::numbers, call_followme::optionsprompt, fm_args::optionsprompt, number::order, fm_args::outbound, fm_args::pending_in_connected_update, fm_args::pending_out_connected_update, call_followme::plsholdprompt, fm_args::plsholdprompt, call_followme::realtime, S_OR, call_followme::sorryprompt, fm_args::sorryprompt, fm_args::status, call_followme::statusprompt, fm_args::statusprompt, call_followme::takecall, fm_args::takecall, THRESHOLD_SILENCE, and number::timeout.

01151 {
01152    struct fm_args targs = { 0, };
01153    struct ast_bridge_config config;
01154    struct call_followme *f;
01155    struct number *nm, *newnm;
01156    int res = 0;
01157    char *argstr;
01158    char namerecloc[255];
01159    struct ast_channel *caller;
01160    struct ast_channel *outbound;
01161    AST_DECLARE_APP_ARGS(args,
01162       AST_APP_ARG(followmeid);
01163       AST_APP_ARG(options);
01164    );
01165 
01166    if (ast_strlen_zero(data)) {
01167       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01168       return -1;
01169    }
01170 
01171    argstr = ast_strdupa((char *) data);
01172 
01173    AST_STANDARD_APP_ARGS(args, argstr);
01174 
01175    if (ast_strlen_zero(args.followmeid)) {
01176       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01177       return -1;
01178    }
01179 
01180    AST_RWLIST_RDLOCK(&followmes);
01181    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01182       if (!strcasecmp(f->name, args.followmeid) && (f->active))
01183          break;
01184    }
01185    AST_RWLIST_UNLOCK(&followmes);
01186 
01187    ast_debug(1, "New profile %s.\n", args.followmeid);
01188 
01189    if (!f) {
01190       f = find_realtime(args.followmeid);
01191    }
01192 
01193    if (!f) {
01194       ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01195       return 0;
01196    }
01197 
01198    /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
01199    if (args.options) 
01200       ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01201 
01202    /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
01203    ast_mutex_lock(&f->lock);
01204    targs.mohclass = ast_strdupa(f->moh);
01205    ast_copy_string(targs.context, f->context, sizeof(targs.context));
01206    ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01207    ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01208    ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01209    ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01210    ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01211    ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01212    ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01213    ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01214    /* Copy the numbers we're going to use into another list in case the master list should get modified 
01215       (and locked) while we're trying to do a follow-me */
01216    AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01217    AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01218       newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01219       if (newnm) {
01220          AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01221       }
01222    }
01223    ast_mutex_unlock(&f->lock);
01224 
01225    /* Forget the 'N' option if the call is already up. */
01226    if (chan->_state == AST_STATE_UP) {
01227       ast_clear_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER);
01228    }
01229 
01230    namerecloc[0] = '\0';
01231    if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01232       ast_indicate(chan, AST_CONTROL_RINGING);
01233    } else {
01234       /* Answer the call */
01235       if (chan->_state != AST_STATE_UP) {
01236          ast_answer(chan);
01237       }
01238 
01239       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 
01240          ast_stream_and_wait(chan, targs.statusprompt, "");
01241 
01242       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) {
01243          int duration = 5;
01244 
01245          snprintf(namerecloc, sizeof(namerecloc), "%s/followme.%s",
01246             ast_config_AST_SPOOL_DIR, ast_channel_uniqueid(chan));
01247          if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration,
01248             NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) {
01249             goto outrun;
01250          }
01251          if (!ast_fileexists(namerecloc, NULL, ast_channel_language(chan))) {
01252             namerecloc[0] = '\0';
01253          }
01254       }
01255 
01256       if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
01257          if (ast_streamfile(chan, targs.plsholdprompt, ast_channel_language(chan)))
01258             goto outrun;
01259          if (ast_waitstream(chan, "") < 0)
01260             goto outrun;
01261       }
01262       ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01263    }
01264 
01265    targs.status = 0;
01266    targs.chan = chan;
01267    ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01268    ast_channel_lock(chan);
01269    ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller);
01270    ast_channel_unlock(chan);
01271 
01272    findmeexec(&targs);
01273 
01274    while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
01275       ast_free(nm);
01276 
01277    if (!ast_strlen_zero(namerecloc))
01278       unlink(namerecloc);
01279 
01280    if (targs.status != 100) {
01281       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01282          if (chan->_state != AST_STATE_UP) {
01283             ast_answer(chan);
01284          }
01285       } else {
01286          ast_moh_stop(chan);
01287       }
01288 
01289       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 
01290          ast_stream_and_wait(chan, targs.sorryprompt, "");
01291       res = 0;
01292    } else {
01293       caller = chan;
01294       outbound = targs.outbound;
01295       /* Bridge the two channels. */
01296 
01297       memset(&config, 0, sizeof(config));
01298       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01299       ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01300       ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01301       config.end_bridge_callback = end_bridge_callback;
01302       config.end_bridge_callback_data = chan;
01303       config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01304 
01305       /* Update connected line to caller if available. */
01306       if (targs.pending_out_connected_update) {
01307          if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) {
01308             ast_channel_update_connected_line(caller, &targs.connected_out, NULL);
01309          }
01310       }
01311 
01312       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_NOANSWER)) {
01313          if (caller->_state != AST_STATE_UP) {
01314             ast_answer(caller);
01315          }
01316       } else {
01317          ast_moh_stop(caller);
01318       }
01319 
01320       /* Be sure no generators are left on it */
01321       ast_deactivate_generator(caller);
01322       /* Make sure channels are compatible */
01323       res = ast_channel_make_compatible(caller, outbound);
01324       if (res < 0) {
01325          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(caller), ast_channel_name(outbound));
01326          ast_hangup(outbound);
01327          goto outrun;
01328       }
01329 
01330       /* Update connected line to winner if changed. */
01331       if (targs.pending_in_connected_update) {
01332          if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) {
01333             ast_channel_update_connected_line(outbound, &targs.connected_in, NULL);
01334          }
01335       }
01336 
01337       res = ast_bridge_call(caller, outbound, &config);
01338       ast_hangup(outbound);
01339    }
01340 
01341 outrun:
01342    ast_party_connected_line_free(&targs.connected_in);
01343    ast_party_connected_line_free(&targs.connected_out);
01344    if (f->realtime) {
01345       /* Not in list */
01346       free_numbers(f);
01347       ast_free(f);
01348    }
01349 
01350    return res;
01351 }

static void clear_caller ( struct findme_user tmpuser  )  [static]

Definition at line 516 of file app_followme.c.

References ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_lock, ast_channel_unlock, ast_hangup(), ast_log(), ast_channel::cdr, findme_user::dialarg, ast_channel::hangupcause, LOG_WARNING, findme_user::ochan, and findme_user::state.

Referenced by clear_calling_tree(), destroy_calling_tree(), and findmeexec().

00517 {
00518    struct ast_channel *outbound;
00519 
00520    if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00521       outbound = tmpuser->ochan;
00522       ast_channel_lock(outbound);
00523       if (!outbound->cdr) {
00524          outbound->cdr = ast_cdr_alloc();
00525          if (outbound->cdr) {
00526             ast_cdr_init(outbound->cdr, outbound);
00527          }
00528       }
00529       if (outbound->cdr) {
00530          char tmp[256];
00531 
00532          snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00533          ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00534          ast_cdr_update(outbound);
00535          ast_cdr_start(outbound->cdr);
00536          ast_cdr_end(outbound->cdr);
00537          /* If the cause wasn't handled properly */
00538          if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00539             ast_cdr_failed(outbound->cdr);
00540          }
00541       } else {
00542          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00543       }
00544       ast_channel_unlock(outbound);
00545       ast_hangup(outbound);
00546       tmpuser->ochan = NULL;
00547    }
00548 }

static void clear_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 550 of file app_followme.c.

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

Referenced by wait_for_winner().

00551 {
00552    struct findme_user *tmpuser;
00553 
00554    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00555       clear_caller(tmpuser);
00556       tmpuser->cleared = 1;
00557    }
00558 }

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

Add a new number.

Definition at line 324 of file app_followme.c.

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

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

00325 {
00326    struct number *cur;
00327    char *buf = ast_strdupa(number);
00328    char *tmp;
00329 
00330    if (!(cur = ast_calloc(1, sizeof(*cur))))
00331       return NULL;
00332 
00333    cur->timeout = timeout;
00334    if ((tmp = strchr(buf, ',')))
00335       *tmp = '\0';
00336    ast_copy_string(cur->number, buf, sizeof(cur->number));
00337    cur->order = numorder;
00338    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00339 
00340    return cur;
00341 }

static void destroy_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 560 of file app_followme.c.

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

Referenced by findmeexec().

00561 {
00562    struct findme_user *fmuser;
00563 
00564    while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00565       if (!fmuser->cleared) {
00566          clear_caller(fmuser);
00567       }
00568       ast_party_connected_line_free(&fmuser->connected);
00569       ast_free(fmuser);
00570    }
00571    ast_free(findme_user_list);
00572 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 1124 of file app_followme.c.

References ast_cdr::answer, ast_channel_lock, ast_channel_unlock, ast_channel::cdr, pbx_builtin_setvar_helper(), and ast_cdr::start.

01125 {
01126    char buf[80];
01127    time_t end;
01128    struct ast_channel *chan = data;
01129 
01130    time(&end);
01131 
01132    ast_channel_lock(chan);
01133    if (chan->cdr->answer.tv_sec) {
01134       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
01135       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01136    }
01137 
01138    if (chan->cdr->start.tv_sec) {
01139       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
01140       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01141    }
01142    ast_channel_unlock(chan);
01143 }

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

Definition at line 1145 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

01146 {
01147    bconfig->end_bridge_callback_data = originator;
01148 }

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

Definition at line 1043 of file app_followme.c.

References alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_false(), ast_free, AST_LIST_INSERT_TAIL, ast_load_realtime(), ast_load_realtime_multientry(), ast_mutex_destroy, ast_str_buffer(), ast_str_create(), ast_str_set(), ast_variable_retrieve(), ast_variables_destroy(), create_followme_number(), call_followme::lock, ast_variable::name, ast_variable::next, call_followme::numbers, profile_set_param(), call_followme::realtime, SENTINEL, str, number::timeout, ast_variable::value, and var.

Referenced by app_exec().

01044 {
01045    struct ast_variable *var;
01046    struct ast_variable *v;
01047    struct ast_config *cfg;
01048    const char *catg;
01049    struct call_followme *new_follower;
01050    struct ast_str *str;
01051 
01052    str = ast_str_create(16);
01053    if (!str) {
01054       return NULL;
01055    }
01056 
01057    var = ast_load_realtime("followme", "name", name, SENTINEL);
01058    if (!var) {
01059       ast_free(str);
01060       return NULL;
01061    }
01062 
01063    if (!(new_follower = alloc_profile(name))) {
01064       ast_variables_destroy(var);
01065       ast_free(str);
01066       return NULL;
01067    }
01068 
01069    for (v = var; v; v = v->next) {
01070       if (!strcasecmp(v->name, "active")) {
01071          if (ast_false(v->value)) {
01072             ast_mutex_destroy(&new_follower->lock);
01073             ast_free(new_follower);
01074             ast_variables_destroy(var);
01075             ast_free(str);
01076             return NULL;
01077          }
01078       } else {
01079          profile_set_param(new_follower, v->name, v->value, 0, 0);
01080       }
01081    }
01082 
01083    ast_variables_destroy(var);
01084    new_follower->realtime = 1;
01085 
01086    /* Load numbers */
01087    cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01088       name, SENTINEL);
01089    if (!cfg) {
01090       ast_mutex_destroy(&new_follower->lock);
01091       ast_free(new_follower);
01092       ast_free(str);
01093       return NULL;
01094    }
01095 
01096    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01097       const char *numstr;
01098       const char *timeoutstr;
01099       const char *ordstr;
01100       int timeout;
01101       struct number *cur;
01102 
01103       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01104          continue;
01105       }
01106       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01107          || sscanf(timeoutstr, "%30d", &timeout) != 1
01108          || timeout < 1) {
01109          timeout = 25;
01110       }
01111       /* This one has to exist; it was part of the query */
01112       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01113       ast_str_set(&str, 0, "%s", numstr);
01114       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01115          AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01116       }
01117    }
01118    ast_config_destroy(cfg);
01119 
01120    ast_free(str);
01121    return new_follower;
01122 }

static void findmeexec ( struct fm_args tpargs  )  [static]

Definition at line 892 of file app_followme.c.

References ast_call(), ast_calloc, ast_cause2str(), ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_accountcode(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_language(), ast_channel_lock, ast_channel_lock_both, ast_channel_musicclass(), ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, ast_hangup(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_party_connected_line_free(), ast_request(), ast_test_flag, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, clear_caller(), findme_user::cleared, fm_args::cnumbers, findme_user::connected, ast_channel::connected, fm_args::connected_out, fm_args::context, destroy_calling_tree(), findme_user::dialarg, FOLLOWMEFLAG_DISABLEOPTIMIZATION, fm_args::followmeflags, ast_channel::hangupcause, ast_party_caller::id, LOG_ERROR, LOG_WARNING, fm_args::namerecloc, ast_channel::nativeformats, fm_args::nextindp, ast_party_id::number, number::number, findme_user::ochan, number::order, fm_args::outbound, findme_user::pending_connected_update, fm_args::pending_out_connected_update, S_COR, findme_user::state, fm_args::status, ast_party_number::str, fm_args::takecall, number::timeout, ast_party_number::valid, and wait_for_winner().

Referenced by app_exec().

00893 {
00894    struct number *nm;
00895    struct ast_channel *outbound;
00896    struct ast_channel *caller;
00897    struct ast_channel *winner = NULL;
00898    char dialarg[512];
00899    char num[512];
00900    int dg, idx;
00901    char *rest, *number;
00902    struct findme_user *tmpuser;
00903    struct findme_user *fmuser;
00904    struct findme_user_listptr *findme_user_list;
00905 
00906    findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00907    AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00908 
00909    /* We're going to figure out what the longest possible string of digits to collect is */
00910    ynlongest = 0;
00911    if (strlen(tpargs->takecall) > ynlongest) {
00912       ynlongest = strlen(tpargs->takecall);
00913    }
00914    if (strlen(tpargs->nextindp) > ynlongest) {
00915       ynlongest = strlen(tpargs->nextindp);
00916    }
00917 
00918    caller = tpargs->chan;
00919    for (idx = 1; !ast_check_hangup(caller); ++idx) {
00920       /* Find next followme numbers to dial. */
00921       AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
00922          if (nm->order == idx) {
00923             break;
00924          }
00925       }
00926       if (!nm) {
00927          break;
00928       }
00929 
00930       ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
00931 
00932       ast_copy_string(num, nm->number, sizeof(num));
00933       for (number = num; number; number = rest) {
00934          rest = strchr(number, '&');
00935          if (rest) {
00936             *rest++ = 0;
00937          }
00938 
00939          /* We check if the extension exists, before creating the ast_channel struct */
00940          if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
00941             ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
00942             continue;
00943          }
00944 
00945          if (!strcmp(tpargs->context, "")) {
00946             snprintf(dialarg, sizeof(dialarg), "%s%s", number, ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION) ? "/n" : "");
00947          } else {
00948             snprintf(dialarg, sizeof(dialarg), "%s@%s%s", number, tpargs->context, ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_DISABLEOPTIMIZATION) ? "/n" : "");
00949          }
00950 
00951          tmpuser = ast_calloc(1, sizeof(*tmpuser));
00952          if (!tmpuser) {
00953             continue;
00954          }
00955 
00956          outbound = ast_request("Local", caller->nativeformats, caller, dialarg, &dg);
00957          if (outbound) {
00958             ast_channel_lock_both(caller, outbound);
00959             ast_connected_line_copy_from_caller(&outbound->connected, &caller->caller);
00960             ast_channel_inherit_variables(caller, outbound);
00961             ast_channel_datastore_inherit(caller, outbound);
00962             ast_channel_language_set(outbound, ast_channel_language(caller));
00963             ast_channel_accountcode_set(outbound, ast_channel_accountcode(caller));
00964             ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
00965             ast_channel_unlock(outbound);
00966             ast_channel_unlock(caller);
00967             ast_verb(3, "calling Local/%s\n", dialarg);
00968             if (!ast_call(outbound, dialarg, 0)) {
00969                tmpuser->ochan = outbound;
00970                tmpuser->state = 0;
00971                tmpuser->cleared = 0;
00972                ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00973                AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00974             } else {
00975                ast_verb(3, "couldn't reach at this number.\n");
00976                ast_channel_lock(outbound);
00977                if (!outbound->cdr) {
00978                   outbound->cdr = ast_cdr_alloc();
00979                }
00980                if (outbound->cdr) {
00981                   char tmp[256];
00982 
00983                   ast_cdr_init(outbound->cdr, outbound);
00984                   snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00985                   ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00986                   ast_cdr_update(outbound);
00987                   ast_cdr_start(outbound->cdr);
00988                   ast_cdr_end(outbound->cdr);
00989                   /* If the cause wasn't handled properly */
00990                   if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00991                      ast_cdr_failed(outbound->cdr);
00992                   }
00993                } else {
00994                   ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00995                }
00996                ast_channel_unlock(outbound);
00997                ast_hangup(outbound);
00998                ast_free(tmpuser);
00999             }
01000          } else {
01001             ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
01002             ast_free(tmpuser);
01003          }
01004       }
01005 
01006       if (AST_LIST_EMPTY(findme_user_list)) {
01007          continue;
01008       }
01009 
01010       winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs);
01011       if (!winner) {
01012          continue;
01013       }
01014 
01015       /* Destroy losing calls up to the winner.  The rest will be destroyed later. */
01016       while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
01017          if (fmuser->ochan == winner) {
01018             /* Pass any connected line info up. */
01019             tpargs->connected_out = fmuser->connected;
01020             tpargs->pending_out_connected_update = fmuser->pending_connected_update;
01021             ast_free(fmuser);
01022             break;
01023          } else {
01024             /* Destroy losing call. */
01025             if (!fmuser->cleared) {
01026                clear_caller(fmuser);
01027             }
01028             ast_party_connected_line_free(&fmuser->connected);
01029             ast_free(fmuser);
01030          }
01031       }
01032       break;
01033    }
01034    destroy_calling_tree(findme_user_list);
01035    if (!winner) {
01036       tpargs->status = 1;
01037    } else {
01038       tpargs->status = 100;
01039       tpargs->outbound = winner;
01040    }
01041 }

static void free_numbers ( struct call_followme f  )  [static]

Definition at line 235 of file app_followme.c.

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, call_followme::blnumbers, call_followme::numbers, and call_followme::wlnumbers.

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

00236 {
00237    /* Free numbers attached to the profile */
00238    struct number *prev;
00239 
00240    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00241       /* Free the number */
00242       ast_free(prev);
00243    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00244 
00245    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00246       /* Free the blacklisted number */
00247       ast_free(prev);
00248    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00249 
00250    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00251       /* Free the whitelisted number */
00252       ast_free(prev);
00253    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00254 }

static void init_profile ( struct call_followme f  )  [static]

Definition at line 283 of file app_followme.c.

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

Referenced by reload_followme().

00284 {
00285    f->active = 1;
00286    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00287 }

static int load_module ( void   )  [static]

Definition at line 1371 of file app_followme.c.

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

01372 {
01373    if(!reload_followme(0))
01374       return AST_MODULE_LOAD_DECLINE;
01375 
01376    return ast_register_application_xml(app, app_exec);
01377 }

static void profile_set_param ( struct call_followme f,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Set parameter in profile from configuration file.

Definition at line 292 of file app_followme.c.

References ast_copy_string(), ast_log(), call_followme::callfromprompt, call_followme::context, LOG_WARNING, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, and call_followme::takecall.

Referenced by find_realtime(), and reload_followme().

00293 {
00294 
00295    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00296       ast_copy_string(f->moh, val, sizeof(f->moh));
00297    else if (!strcasecmp(param, "context")) 
00298       ast_copy_string(f->context, val, sizeof(f->context));
00299    else if (!strcasecmp(param, "takecall"))
00300       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00301    else if (!strcasecmp(param, "declinecall"))
00302       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00303    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00304       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00305    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00306       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00307    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00308       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00309    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00310       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00311    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00312       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00313    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00314       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00315    else if (failunknown) {
00316       if (linenum >= 0)
00317          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00318       else
00319          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00320    }
00321 }

static int reload ( void   )  [static]

Definition at line 1379 of file app_followme.c.

References reload_followme().

01380 {
01381    reload_followme(1);
01382 
01383    return 0;
01384 }

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

Definition at line 344 of file app_followme.c.

References call_followme::active, alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), f, free_numbers(), init_profile(), ast_variable::lineno, call_followme::lock, LOG_ERROR, LOG_WARNING, ast_variable::name, call_followme::name, ast_variable::next, call_followme::numbers, profile_set_param(), number::timeout, ast_variable::value, and var.

Referenced by load_module(), and reload().

00345 {
00346    struct call_followme *f;
00347    struct ast_config *cfg;
00348    char *cat = NULL, *tmp;
00349    struct ast_variable *var;
00350    struct number *cur, *nm;
00351    char numberstr[90];
00352    int timeout;
00353    int numorder;
00354    const char *takecallstr;
00355    const char *declinecallstr;
00356    const char *tmpstr;
00357    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00358 
00359    if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00360       ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00361       return 0;
00362    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00363       return 0;
00364    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00365       ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
00366       return 0;
00367    }
00368 
00369    AST_RWLIST_WRLOCK(&followmes);
00370 
00371    /* Reset Global Var Values */
00372    featuredigittimeout = 5000;
00373 
00374    /* Mark all profiles as inactive for the moment */
00375    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00376       f->active = 0;
00377    }
00378 
00379    featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00380 
00381    if (!ast_strlen_zero(featuredigittostr)) {
00382       if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00383          featuredigittimeout = 5000;
00384    }
00385 
00386    if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
00387       ast_copy_string(takecall, takecallstr, sizeof(takecall));
00388    }
00389 
00390    if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
00391       ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00392    }
00393 
00394    if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
00395       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00396    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
00397       ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00398    }
00399 
00400    if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
00401       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00402    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
00403       ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00404    }
00405 
00406 
00407    if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
00408       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00409    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
00410       ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00411    }
00412 
00413    if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
00414       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00415    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
00416       ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00417    }
00418 
00419    if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
00420       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00421    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
00422       ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00423    }
00424 
00425    if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
00426       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00427    } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
00428       ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00429    }
00430 
00431    /* Chug through config file */
00432    while ((cat = ast_category_browse(cfg, cat))) {
00433       int new = 0;
00434 
00435       if (!strcasecmp(cat, "general"))
00436          continue;
00437 
00438       /* Look for an existing one */
00439       AST_LIST_TRAVERSE(&followmes, f, entry) {
00440          if (!strcasecmp(f->name, cat))
00441             break;
00442       }
00443 
00444       ast_debug(1, "New profile %s.\n", cat);
00445 
00446       if (!f) {
00447          /* Make one then */
00448          f = alloc_profile(cat);
00449          new = 1;
00450       }
00451 
00452       /* Totally fail if we fail to find/create an entry */
00453       if (!f)
00454          continue;
00455 
00456       if (!new)
00457          ast_mutex_lock(&f->lock);
00458       /* Re-initialize the profile */
00459       init_profile(f);
00460       free_numbers(f);
00461       var = ast_variable_browse(cfg, cat);
00462       while (var) {
00463          if (!strcasecmp(var->name, "number")) {
00464             int idx = 0;
00465 
00466             /* Add a new number */
00467             ast_copy_string(numberstr, var->value, sizeof(numberstr));
00468             if ((tmp = strchr(numberstr, ','))) {
00469                *tmp++ = '\0';
00470                timeout = atoi(tmp);
00471                if (timeout < 0) {
00472                   timeout = 25;
00473                }
00474                if ((tmp = strchr(tmp, ','))) {
00475                   *tmp++ = '\0';
00476                   numorder = atoi(tmp);
00477                   if (numorder < 0)
00478                      numorder = 0;
00479                } else 
00480                   numorder = 0;
00481             } else {
00482                timeout = 25;
00483                numorder = 0;
00484             }
00485 
00486             if (!numorder) {
00487                idx = 1;
00488                AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
00489                   idx++;
00490                numorder = idx;
00491             }
00492             cur = create_followme_number(numberstr, timeout, numorder);
00493             if (cur) {
00494                AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00495             }
00496          } else {
00497             profile_set_param(f, var->name, var->value, var->lineno, 1);
00498             ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00499          }
00500          var = var->next;
00501       } /* End while(var) loop */
00502 
00503       if (!new) 
00504          ast_mutex_unlock(&f->lock);
00505       else
00506          AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00507    }
00508 
00509    ast_config_destroy(cfg);
00510 
00511    AST_RWLIST_UNLOCK(&followmes);
00512 
00513    return 1;
00514 }

static int unload_module ( void   )  [static]

Definition at line 1353 of file app_followme.c.

References ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_unregister_application(), f, and free_numbers().

01354 {
01355    struct call_followme *f;
01356 
01357    ast_unregister_application(app);
01358 
01359    /* Free Memory. Yeah! I'm free! */
01360    AST_RWLIST_WRLOCK(&followmes);
01361    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01362       free_numbers(f);
01363       ast_free(f);
01364    }
01365 
01366    AST_RWLIST_UNLOCK(&followmes);
01367 
01368    return 0;
01369 }

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

Definition at line 574 of file app_followme.c.

References ARRAY_LEN, AST_CAUSE_NORMAL_CLEARING, ast_channel_language(), ast_channel_name(), ast_connected_line_parse_data(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_party_connected_line_free(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_read(), ast_sched_runq(), ast_sched_wait(), ast_stopstream(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), findme_user::connected, fm_args::connected_in, ast_frame::data, ast_frame::datalen, findme_user::digts, f, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE, fm_args::followmeflags, ast_frame::frametype, ast_channel::hangupcause, ast_frame_subclass::integer, LOG_NOTICE, LOG_WARNING, fm_args::nextindp, fm_args::norecordingprompt, findme_user::ochan, fm_args::optionsprompt, findme_user::pending_connected_update, fm_args::pending_in_connected_update, ast_frame::ptr, ast_channel::sched, findme_user::state, ast_channel::stream, ast_frame::subclass, fm_args::takecall, number::timeout, ast_channel::timingfunc, ast_frame::uint32, findme_user::yn, and findme_user::ynidx.

Referenced by findmeexec().

00575 {
00576    struct ast_party_connected_line connected;
00577    struct ast_channel *watchers[256];
00578    int pos;
00579    struct ast_channel *winner;
00580    struct ast_frame *f;
00581    int ctstatus = 0;
00582    int dg;
00583    struct findme_user *tmpuser;
00584    int to = 0;
00585    int livechannels = 0;
00586    int tmpto;
00587    long totalwait = 0, wtd = 0, towas = 0;
00588    char *callfromname;
00589    char *pressbuttonname;
00590 
00591    /* ------------ wait_for_winner_channel start --------------- */ 
00592 
00593    callfromname = ast_strdupa(tpargs->callfromprompt);
00594    pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00595 
00596    if (AST_LIST_EMPTY(findme_user_list)) {
00597       ast_verb(3, "couldn't reach at this number.\n");
00598       return NULL;
00599    }
00600 
00601    if (!caller) {
00602       ast_verb(3, "Original caller hungup. Cleanup.\n");
00603       clear_calling_tree(findme_user_list);
00604       return NULL;
00605    }
00606 
00607    totalwait = nm->timeout * 1000;
00608 
00609    while (!ctstatus) {
00610       to = 1000;
00611       pos = 1; 
00612       livechannels = 0;
00613       watchers[0] = caller;
00614 
00615       dg = 0;
00616       winner = NULL;
00617       AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00618          if (tmpuser->state >= 0 && tmpuser->ochan) {
00619             if (tmpuser->state == 3) 
00620                tmpuser->digts += (towas - wtd);
00621             if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00622                ast_verb(3, "We've been waiting for digits longer than we should have.\n");
00623                if (!ast_strlen_zero(namerecloc)) {
00624                   tmpuser->state = 1;
00625                   tmpuser->digts = 0;
00626                   if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
00627                      ast_sched_runq(tmpuser->ochan->sched);
00628                   } else {
00629                      ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00630                      return NULL;
00631                   }
00632                } else {
00633                   tmpuser->state = 2;
00634                   tmpuser->digts = 0;
00635                   if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00636                      ast_sched_runq(tmpuser->ochan->sched);
00637                   else {
00638                      ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00639                      return NULL;
00640                   }
00641                }
00642             }
00643             if (tmpuser->ochan->stream) {
00644                ast_sched_runq(tmpuser->ochan->sched);
00645                tmpto = ast_sched_wait(tmpuser->ochan->sched);
00646                if (tmpto > 0 && tmpto < to)
00647                   to = tmpto;
00648                else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00649                   ast_stopstream(tmpuser->ochan);
00650                   if (tmpuser->state == 1) {
00651                      ast_verb(3, "Playback of the call-from file appears to be done.\n");
00652                      if (!ast_streamfile(tmpuser->ochan, namerecloc, ast_channel_language(tmpuser->ochan))) {
00653                         tmpuser->state = 2;
00654                      } else {
00655                         ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00656                         memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00657                         tmpuser->ynidx = 0;
00658                         if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan)))
00659                            tmpuser->state = 3;
00660                         else {
00661                            ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00662                            return NULL;
00663                         } 
00664                      }
00665                   } else if (tmpuser->state == 2) {
00666                      ast_verb(3, "Playback of name file appears to be done.\n");
00667                      memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00668                      tmpuser->ynidx = 0;
00669                      if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan))) {
00670                         tmpuser->state = 3;
00671                      } else {
00672                         return NULL;
00673                      } 
00674                   } else if (tmpuser->state == 3) {
00675                      ast_verb(3, "Playback of the next step file appears to be done.\n");
00676                      tmpuser->digts = 0;
00677                   }
00678                }
00679             }
00680             watchers[pos++] = tmpuser->ochan;
00681             livechannels++;
00682          }
00683       }
00684 
00685       tmpto = to;
00686       if (to < 0) {
00687          to = 1000;
00688          tmpto = 1000;
00689       }
00690       towas = to;
00691       winner = ast_waitfor_n(watchers, pos, &to);
00692       tmpto -= to;
00693       totalwait -= tmpto;
00694       wtd = to;
00695       if (totalwait <= 0) {
00696          ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00697          clear_calling_tree(findme_user_list);
00698          return NULL;
00699       }
00700       if (winner) {
00701          /* Need to find out which channel this is */
00702          for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) {
00703             if (winner == watchers[dg]) {
00704                break;
00705             }
00706          }
00707          if (dg) {
00708             /* The winner is an outgoing channel. */
00709             AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00710                if (tmpuser->ochan == winner) {
00711                   break;
00712                }
00713             }
00714          } else {
00715             tmpuser = NULL;
00716          }
00717          f = ast_read(winner);
00718          if (f) {
00719             if (f->frametype == AST_FRAME_CONTROL) {
00720                switch (f->subclass.integer) {
00721                case AST_CONTROL_HANGUP:
00722                   ast_verb(3, "%s received a hangup frame.\n", ast_channel_name(winner));
00723                   if (f->data.uint32) {
00724                      winner->hangupcause = f->data.uint32;
00725                   }
00726                   if (dg == 0) {
00727                      ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
00728                      clear_calling_tree(findme_user_list);
00729                      ctstatus = -1;
00730                   }
00731                   break;
00732                case AST_CONTROL_ANSWER:
00733                   ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
00734                   /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
00735                   winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00736                   caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00737                   ast_verb(3, "Starting playback of %s\n", callfromname);
00738                   if (dg > 0) {
00739                      if (!ast_strlen_zero(namerecloc)) {
00740                         if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
00741                            ast_sched_runq(winner->sched);
00742                            tmpuser->state = 1;
00743                         } else {
00744                            ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00745                            ast_frfree(f);
00746                            return NULL;
00747                         }
00748                      } else {
00749                         tmpuser->state = 2;
00750                         if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
00751                            ast_sched_runq(tmpuser->ochan->sched);
00752                         else {
00753                            ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00754                            ast_frfree(f);
00755                            return NULL;
00756                         }
00757                      }
00758                   }
00759                   break;
00760                case AST_CONTROL_BUSY:
00761                   ast_verb(3, "%s is busy\n", ast_channel_name(winner));
00762                   break;
00763                case AST_CONTROL_CONGESTION:
00764                   ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner));
00765                   break;
00766                case AST_CONTROL_RINGING:
00767                   ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
00768                   break;
00769                case AST_CONTROL_PROGRESS:
00770                   ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
00771                   break;
00772                case AST_CONTROL_VIDUPDATE:
00773                   ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
00774                   break;
00775                case AST_CONTROL_SRCUPDATE:
00776                   ast_verb(3, "%s requested a source update\n", ast_channel_name(winner));
00777                   break;
00778                case AST_CONTROL_PROCEEDING:
00779                   ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
00780                   break;
00781                case AST_CONTROL_HOLD:
00782                   ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
00783                   break;
00784                case AST_CONTROL_UNHOLD:
00785                   ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner));
00786                   break;
00787                case AST_CONTROL_OFFHOOK:
00788                case AST_CONTROL_FLASH:
00789                   /* Ignore going off hook and flash */
00790                   break;
00791                case AST_CONTROL_CONNECTED_LINE:
00792                   if (!tmpuser) {
00793                      /*
00794                       * Hold connected line update from caller until we have a
00795                       * winner.
00796                       */
00797                      ast_verb(3,
00798                         "%s connected line has changed. Saving it until we have a winner.\n",
00799                         ast_channel_name(winner));
00800                      ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
00801                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00802                         ast_party_connected_line_set(&tpargs->connected_in,
00803                            &connected, NULL);
00804                         tpargs->pending_in_connected_update = 1;
00805                      }
00806                      ast_party_connected_line_free(&connected);
00807                      break;
00808                   }
00809                   if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
00810                      ast_verb(3, "Connected line update from %s prevented.\n",
00811                         ast_channel_name(winner));
00812                   } else {
00813                      ast_verb(3,
00814                         "%s connected line has changed. Saving it until answer.\n",
00815                         ast_channel_name(winner));
00816                      ast_party_connected_line_set_init(&connected, &tmpuser->connected);
00817                      if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00818                         ast_party_connected_line_set(&tmpuser->connected,
00819                            &connected, NULL);
00820                         tmpuser->pending_connected_update = 1;
00821                      }
00822                      ast_party_connected_line_free(&connected);
00823                   }
00824                   break;
00825                case AST_CONTROL_REDIRECTING:
00826                   /*
00827                    * Ignore because we are masking the FollowMe search progress to
00828                    * the caller.
00829                    */
00830                   break;
00831                case -1:
00832                   ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner));
00833                   break;
00834                default:
00835                   ast_debug(1, "Dunno what to do with control type %d from %s\n",
00836                      f->subclass.integer, ast_channel_name(winner));
00837                   break;
00838                }
00839             } 
00840             if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00841                if (winner->stream)
00842                   ast_stopstream(winner);
00843                tmpuser->digts = 0;
00844                ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
00845                tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer;
00846                tmpuser->ynidx++;
00847                ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00848                if (tmpuser->ynidx >= ynlongest) {
00849                   ast_debug(1, "reached longest possible match - doing evals\n");
00850                   if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00851                      ast_debug(1, "Match to take the call!\n");
00852                      ast_frfree(f);
00853                      return tmpuser->ochan;
00854                   }
00855                   if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00856                      ast_debug(1, "Next in dial plan step requested.\n");
00857                      ast_frfree(f);
00858                      return NULL;
00859                   }
00860                }
00861             }
00862 
00863             ast_frfree(f);
00864          } else {
00865             ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg);
00866             if (!dg) {
00867                /* Caller hung up. */
00868                clear_calling_tree(findme_user_list);
00869                return NULL;
00870             } else {
00871                /* Outgoing channel hung up. */
00872                tmpuser->state = -1;
00873                tmpuser->ochan = NULL;
00874                ast_hangup(winner);
00875                --livechannels;
00876                ast_debug(1, "live channels left %d\n", livechannels);
00877                if (!livechannels) {
00878                   ast_verb(3, "no live channels left. exiting.\n");
00879                   return NULL;
00880                }
00881             }
00882          }
00883       } else {
00884          ast_debug(1, "timed out waiting for action\n");
00885       }
00886    }
00887 
00888    /* --- WAIT FOR WINNER NUMBER END! -----------*/
00889    return NULL;
00890 }


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

Definition at line 1390 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 119 of file app_followme.c.

Definition at line 1390 of file app_followme.c.

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

Definition at line 224 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 221 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 220 of file app_followme.c.

Referenced by ast_bridge_call(), and process_config().

const char* featuredigittostr [static]

Definition at line 219 of file app_followme.c.

struct ast_app_option followme_opts[128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'l' ] = { .flag = FOLLOWMEFLAG_DISABLEOPTIMIZATION }, [ 'N' ] = { .flag = FOLLOWMEFLAG_NOANSWER }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG },} [static]

Definition at line 215 of file app_followme.c.

Referenced by app_exec().

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

Definition at line 223 of file app_followme.c.

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

Definition at line 225 of file app_followme.c.

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

Definition at line 226 of file app_followme.c.

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

Definition at line 227 of file app_followme.c.

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

Definition at line 229 of file app_followme.c.

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

Definition at line 228 of file app_followme.c.

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

Definition at line 223 of file app_followme.c.

int ynlongest = 0 [static]

Definition at line 217 of file app_followme.c.


Generated on Sat Feb 11 06:33:43 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6