Sat Mar 10 01:54:32 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"

Go to the source code of this file.

Data Structures

struct  call_followme
 Data structure for followme scripts. More...
struct  call_followme::blnumbers
struct  call_followme::numbers
struct  call_followme::wlnumbers
struct  findme_user
struct  findme_user_listptr
struct  fm_args
struct  fm_args::cnumbers
struct  followmes
struct  number
 Number structure. More...

Enumerations

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

Functions

static void __reg_module (void)
static void __unreg_module (void)
static struct call_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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 }, [ '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_IGNORE_CONNECTEDLINE 

Definition at line 181 of file app_followme.c.

00181      {
00182    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00183    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00184    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00185    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00186    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4),
00187 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1342 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1342 of file app_followme.c.

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

Allocate and initialize followme profile.

Definition at line 238 of file app_followme.c.

References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, and f.

Referenced by find_realtime(), and reload_followme().

00239 {
00240    struct call_followme *f;
00241 
00242    if (!(f = ast_calloc(1, sizeof(*f))))
00243       return NULL;
00244 
00245    ast_mutex_init(&f->lock);
00246    ast_copy_string(f->name, fmname, sizeof(f->name));
00247    f->moh[0] = '\0';
00248    f->context[0] = '\0';
00249    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00250    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00251    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00252    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00253    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00254    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00255    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00256    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00257    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00258    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00259    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00260    return f;
00261 }

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

Definition at line 1130 of file app_followme.c.

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

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

static void clear_caller ( struct findme_user tmpuser  )  [static]

Definition at line 496 of file app_followme.c.

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

Referenced by clear_calling_tree(), and destroy_calling_tree().

00497 {
00498    struct ast_channel *outbound;
00499 
00500    if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00501       outbound = tmpuser->ochan;
00502       ast_channel_lock(outbound);
00503       if (!outbound->cdr) {
00504          outbound->cdr = ast_cdr_alloc();
00505          if (outbound->cdr) {
00506             ast_cdr_init(outbound->cdr, outbound);
00507          }
00508       }
00509       if (outbound->cdr) {
00510          char tmp[256];
00511 
00512          snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00513          ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00514          ast_cdr_update(outbound);
00515          ast_cdr_start(outbound->cdr);
00516          ast_cdr_end(outbound->cdr);
00517          /* If the cause wasn't handled properly */
00518          if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00519             ast_cdr_failed(outbound->cdr);
00520          }
00521       } else {
00522          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00523       }
00524       ast_channel_unlock(outbound);
00525       ast_hangup(outbound);
00526       tmpuser->ochan = NULL;
00527    }
00528 }

static void clear_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 530 of file app_followme.c.

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

Referenced by wait_for_winner().

00531 {
00532    struct findme_user *tmpuser;
00533 
00534    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00535       clear_caller(tmpuser);
00536       tmpuser->cleared = 1;
00537    }
00538 }

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

Add a new number.

Definition at line 304 of file app_followme.c.

References ast_calloc, ast_copy_string(), ast_debug, and ast_strdupa.

Referenced by app_exec(), and reload_followme().

00305 {
00306    struct number *cur;
00307    char *buf = ast_strdupa(number);
00308    char *tmp;
00309 
00310    if (!(cur = ast_calloc(1, sizeof(*cur))))
00311       return NULL;
00312 
00313    cur->timeout = timeout;
00314    if ((tmp = strchr(buf, ',')))
00315       *tmp = '\0';
00316    ast_copy_string(cur->number, buf, sizeof(cur->number));
00317    cur->order = numorder;
00318    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00319 
00320    return cur;
00321 }

static void destroy_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 540 of file app_followme.c.

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

00541 {
00542    struct findme_user *fmuser;
00543 
00544    while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00545       if (!fmuser->cleared) {
00546          clear_caller(fmuser);
00547       }
00548       ast_party_connected_line_free(&fmuser->connected);
00549       ast_free(fmuser);
00550    }
00551    ast_free(findme_user_list);
00552 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 1104 of file app_followme.c.

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

01105 {
01106    char buf[80];
01107    time_t end;
01108    struct ast_channel *chan = data;
01109 
01110    time(&end);
01111 
01112    ast_channel_lock(chan);
01113    if (chan->cdr->answer.tv_sec) {
01114       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
01115       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01116    }
01117 
01118    if (chan->cdr->start.tv_sec) {
01119       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
01120       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01121    }
01122    ast_channel_unlock(chan);
01123 }

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

Definition at line 1125 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

01126 {
01127    bconfig->end_bridge_callback_data = originator;
01128 }

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

Definition at line 1023 of file app_followme.c.

References alloc_profile(), ast_false(), ast_free, ast_load_realtime(), ast_mutex_destroy, ast_str_create(), ast_variables_destroy(), call_followme::lock, ast_variable::name, ast_variable::next, SENTINEL, str, ast_variable::value, and var.

Referenced by app_exec().

01024 {
01025    struct ast_variable *var;
01026    struct ast_variable *v;
01027    struct ast_config *cfg;
01028    const char *catg;
01029    struct call_followme *new_follower;
01030    struct ast_str *str;
01031 
01032    str = ast_str_create(16);
01033    if (!str) {
01034       return NULL;
01035    }
01036 
01037    var = ast_load_realtime("followme", "name", name, SENTINEL);
01038    if (!var) {
01039       ast_free(str);
01040       return NULL;
01041    }
01042 
01043    if (!(new_follower = alloc_profile(name))) {
01044       ast_variables_destroy(var);
01045       ast_free(str);
01046       return NULL;
01047    }
01048 
01049    for (v = var; v; v = v->next) {
01050       if (!strcasecmp(v->name, "active")) {
01051          if (ast_false(v->value)) {
01052             ast_mutex_destroy(&new_follower->lock);
01053             ast_free(new_follower);
01054             ast_variables_destroy(var);
01055             ast_free(str);
01056             return NULL;
01057          }
01058       } else {
01059          profile_set_param(new_follower, v->name, v->value, 0, 0);
01060       }
01061    }
01062 
01063    ast_variables_destroy(var);
01064    new_follower->realtime = 1;
01065 
01066    /* Load numbers */
01067    cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01068       name, SENTINEL);
01069    if (!cfg) {
01070       ast_mutex_destroy(&new_follower->lock);
01071       ast_free(new_follower);
01072       ast_free(str);
01073       return NULL;
01074    }
01075 
01076    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01077       const char *numstr;
01078       const char *timeoutstr;
01079       const char *ordstr;
01080       int timeout;
01081       struct number *cur;
01082 
01083       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01084          continue;
01085       }
01086       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01087          || sscanf(timeoutstr, "%30d", &timeout) != 1
01088          || timeout < 1) {
01089          timeout = 25;
01090       }
01091       /* This one has to exist; it was part of the query */
01092       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01093       ast_str_set(&str, 0, "%s", numstr);
01094       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01095          AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01096       }
01097    }
01098    ast_config_destroy(cfg);
01099 
01100    ast_free(str);
01101    return new_follower;
01102 }

static void findmeexec ( struct fm_args tpargs  )  [static]

Definition at line 872 of file app_followme.c.

References accountcode, ast_best_codec(), ast_call(), ast_calloc, ast_cause2str(), ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, ast_hangup(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_request(), ast_string_field_set, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, fm_args::cnumbers, ast_channel::connected, fm_args::context, number::entry, ast_channel::hangupcause, ast_party_caller::id, language, LOG_ERROR, LOG_WARNING, musicclass, ast_channel::nativeformats, fm_args::nextindp, ast_party_id::number, number::number, number::order, S_COR, ast_party_number::str, fm_args::takecall, number::timeout, and ast_party_number::valid.

Referenced by app_exec().

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

static void free_numbers ( struct call_followme f  )  [static]

Definition at line 215 of file app_followme.c.

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, number::entry, and f.

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

00216 {
00217    /* Free numbers attached to the profile */
00218    struct number *prev;
00219 
00220    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00221       /* Free the number */
00222       ast_free(prev);
00223    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00224 
00225    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00226       /* Free the blacklisted number */
00227       ast_free(prev);
00228    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00229 
00230    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00231       /* Free the whitelisted number */
00232       ast_free(prev);
00233    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00234 }

static void init_profile ( struct call_followme f  )  [static]

Definition at line 263 of file app_followme.c.

References ast_copy_string(), and f.

Referenced by reload_followme().

00264 {
00265    f->active = 1;
00266    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00267 }

static int load_module ( void   )  [static]

Definition at line 1323 of file app_followme.c.

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

01324 {
01325    if(!reload_followme(0))
01326       return AST_MODULE_LOAD_DECLINE;
01327 
01328    return ast_register_application_xml(app, app_exec);
01329 }

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

Set parameter in profile from configuration file.

Definition at line 272 of file app_followme.c.

References ast_copy_string(), ast_log(), f, LOG_WARNING, and ast_format::name.

Referenced by reload_followme().

00273 {
00274 
00275    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00276       ast_copy_string(f->moh, val, sizeof(f->moh));
00277    else if (!strcasecmp(param, "context")) 
00278       ast_copy_string(f->context, val, sizeof(f->context));
00279    else if (!strcasecmp(param, "takecall"))
00280       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00281    else if (!strcasecmp(param, "declinecall"))
00282       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00283    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00284       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00285    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00286       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00287    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00288       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00289    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00290       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00291    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00292       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00293    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00294       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00295    else if (failunknown) {
00296       if (linenum >= 0)
00297          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00298       else
00299          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00300    }
00301 }

static int reload ( void   )  [static]

Definition at line 1331 of file app_followme.c.

References reload_followme().

01332 {
01333    reload_followme(1);
01334 
01335    return 0;
01336 }

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

Definition at line 324 of file app_followme.c.

References alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), number::entry, f, free_numbers(), init_profile(), LOG_ERROR, LOG_WARNING, ast_format::name, profile_set_param(), number::timeout, and var.

Referenced by load_module(), and reload().

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

static int unload_module ( void   )  [static]

Definition at line 1305 of file app_followme.c.

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

01306 {
01307    struct call_followme *f;
01308 
01309    ast_unregister_application(app);
01310 
01311    /* Free Memory. Yeah! I'm free! */
01312    AST_RWLIST_WRLOCK(&followmes);
01313    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01314       free_numbers(f);
01315       ast_free(f);
01316    }
01317 
01318    AST_RWLIST_UNLOCK(&followmes);
01319 
01320    return 0;
01321 }

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

Definition at line 554 of file app_followme.c.

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

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


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

Definition at line 1342 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 103 of file app_followme.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1342 of file app_followme.c.

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

Definition at line 204 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 201 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 200 of file app_followme.c.

Referenced by ast_bridge_call(), and process_config().

const char* featuredigittostr [static]

Definition at line 199 of file app_followme.c.

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

Definition at line 195 of file app_followme.c.

Referenced by app_exec().

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

Definition at line 203 of file app_followme.c.

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

Definition at line 205 of file app_followme.c.

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

Definition at line 206 of file app_followme.c.

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

Definition at line 207 of file app_followme.c.

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

Definition at line 209 of file app_followme.c.

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

Definition at line 208 of file app_followme.c.

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

Definition at line 203 of file app_followme.c.

int ynlongest = 0 [static]

Definition at line 197 of file app_followme.c.


Generated on Sat Mar 10 01:54:32 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7