Fri Nov 12 11:49:35 2010

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/monitor.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  blnumbers
struct  call_followme
 Data structure for followme scripts. More...
struct  cnumbers
struct  findme_user
struct  findme_user_listptr
struct  fm_args
struct  followmes
struct  number
 Number structure. More...
struct  numbers
struct  wlnumbers

Enumerations

enum  { FOLLOWMEFLAG_STATUSMSG = (1 << 0), FOLLOWMEFLAG_RECORDNAME = (1 << 1), FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2) }

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, void *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 (char *number, int timeout, int numorder)
 Add a new number.
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, int *status, 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 = "0901e4e500243c855563a2d78b0c03e4" , .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] = { [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG }, [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG },}
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 

Definition at line 159 of file app_followme.c.

00159      {
00160    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00161    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00162    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
00163 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1197 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1197 of file app_followme.c.

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

Allocate and initialize followme profile.

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

00213 {
00214    struct call_followme *f;
00215 
00216    if (!(f = ast_calloc(1, sizeof(*f))))
00217       return NULL;
00218 
00219    ast_mutex_init(&f->lock);
00220    ast_copy_string(f->name, fmname, sizeof(f->name));
00221    f->moh[0] = '\0';
00222    f->context[0] = '\0';
00223    ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00224    ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00225    ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00226    ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00227    ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00228    ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00229    ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00230    ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00231    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00232    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00233    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00234    return f;
00235 }

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

Definition at line 1003 of file app_followme.c.

References ast_channel::_state, call_followme::active, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_make_compatible(), ast_config_AST_SPOOL_DIR, 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_play_and_record(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), call_followme::callfromprompt, fm_args::callfromprompt, fm_args::chan, fm_args::cnumbers, 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_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), ast_channel::language, call_followme::lock, LOG_ERROR, LOG_WARNING, call_followme::moh, fm_args::mohclass, ast_channel::name, 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, 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, number::timeout, and ast_channel::uniqueid.

Referenced by load_module().

01004 {
01005    struct fm_args targs = { 0, };
01006    struct ast_bridge_config config;
01007    struct call_followme *f;
01008    struct number *nm, *newnm;
01009    int res = 0;
01010    char *argstr;
01011    char namerecloc[255];
01012    int duration = 0;
01013    struct ast_channel *caller;
01014    struct ast_channel *outbound;
01015    AST_DECLARE_APP_ARGS(args,
01016       AST_APP_ARG(followmeid);
01017       AST_APP_ARG(options);
01018    );
01019 
01020    if (ast_strlen_zero(data)) {
01021       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01022       return -1;
01023    }
01024 
01025    if (!(argstr = ast_strdupa((char *)data))) {
01026       ast_log(LOG_ERROR, "Out of memory!\n");
01027       return -1;
01028    }
01029 
01030    AST_STANDARD_APP_ARGS(args, argstr);
01031 
01032    if (ast_strlen_zero(args.followmeid)) {
01033       ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01034       return -1;
01035    }
01036 
01037    AST_RWLIST_RDLOCK(&followmes);
01038    AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01039       if (!strcasecmp(f->name, args.followmeid) && (f->active))
01040          break;
01041    }
01042    AST_RWLIST_UNLOCK(&followmes);
01043 
01044    ast_debug(1, "New profile %s.\n", args.followmeid);
01045 
01046    if (!f) {
01047       f = find_realtime(args.followmeid);
01048    }
01049 
01050    if (!f) {
01051       ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01052       return 0;
01053    }
01054 
01055    /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
01056    if (args.options) 
01057       ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01058 
01059    /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
01060    ast_mutex_lock(&f->lock);
01061    targs.mohclass = ast_strdupa(f->moh);
01062    ast_copy_string(targs.context, f->context, sizeof(targs.context));
01063    ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01064    ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01065    ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01066    ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01067    ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01068    ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01069    ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01070    ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01071    /* Copy the numbers we're going to use into another list in case the master list should get modified 
01072       (and locked) while we're trying to do a follow-me */
01073    AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01074    AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01075       newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01076       AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01077    }
01078    ast_mutex_unlock(&f->lock);
01079 
01080    /* Answer the call */
01081    if (chan->_state != AST_STATE_UP) {
01082       ast_answer(chan);
01083    }
01084 
01085    if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 
01086       ast_stream_and_wait(chan, targs.statusprompt, "");
01087 
01088    snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
01089    duration = 5;
01090 
01091    if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) 
01092       if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0)
01093          goto outrun;
01094 
01095    if (!ast_fileexists(namerecloc, NULL, chan->language))
01096       ast_copy_string(namerecloc, "", sizeof(namerecloc));
01097 
01098    if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01099       goto outrun;
01100    if (ast_waitstream(chan, "") < 0)
01101       goto outrun;
01102    ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01103 
01104    targs.status = 0;
01105    targs.chan = chan;
01106    ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01107 
01108    findmeexec(&targs);
01109 
01110    while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
01111       ast_free(nm);
01112 
01113    if (!ast_strlen_zero(namerecloc))
01114       unlink(namerecloc);
01115 
01116    if (targs.status != 100) {
01117       ast_moh_stop(chan);
01118       if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 
01119          ast_stream_and_wait(chan, targs.sorryprompt, "");
01120       res = 0;
01121    } else {
01122       caller = chan;
01123       outbound = targs.outbound;
01124       /* Bridge the two channels. */
01125 
01126       memset(&config, 0, sizeof(config));
01127       ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01128       ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01129       ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01130       config.end_bridge_callback = end_bridge_callback;
01131       config.end_bridge_callback_data = chan;
01132       config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01133 
01134       ast_moh_stop(caller);
01135       /* Be sure no generators are left on it */
01136       ast_deactivate_generator(caller);
01137       /* Make sure channels are compatible */
01138       res = ast_channel_make_compatible(caller, outbound);
01139       if (res < 0) {
01140          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01141          ast_hangup(outbound);
01142          goto outrun;
01143       }
01144       res = ast_bridge_call(caller, outbound, &config);
01145       if (outbound)
01146          ast_hangup(outbound);
01147    }
01148 
01149    outrun:
01150 
01151    if (f->realtime) {
01152       /* Not in list */
01153       free_numbers(f);
01154       ast_free(f);
01155    }
01156 
01157    return res;
01158 }

static void clear_caller ( struct findme_user tmpuser  )  [static]

Definition at line 468 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_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 findmeexec().

00469 {
00470    struct ast_channel *outbound;
00471 
00472    if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00473       outbound = tmpuser->ochan;
00474       if (!outbound->cdr) {
00475          outbound->cdr = ast_cdr_alloc();
00476          if (outbound->cdr)
00477             ast_cdr_init(outbound->cdr, outbound);
00478       }
00479       if (outbound->cdr) {
00480          char tmp[256];
00481 
00482          snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00483          ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00484          ast_cdr_update(outbound);
00485          ast_cdr_start(outbound->cdr);
00486          ast_cdr_end(outbound->cdr);
00487          /* If the cause wasn't handled properly */
00488          if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
00489             ast_cdr_failed(outbound->cdr);
00490       } else
00491          ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00492       ast_hangup(tmpuser->ochan);
00493    }
00494 
00495 }

static void clear_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 497 of file app_followme.c.

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

Referenced by wait_for_winner().

00498 {
00499    struct findme_user *tmpuser;
00500 
00501    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00502       clear_caller(tmpuser);
00503       tmpuser->cleared = 1;
00504    }
00505 }

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

Add a new number.

Definition at line 278 of file app_followme.c.

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

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

00279 {
00280    struct number *cur;
00281    char *tmp;
00282 
00283    if (!(cur = ast_calloc(1, sizeof(*cur))))
00284       return NULL;
00285 
00286    cur->timeout = timeout;
00287    if ((tmp = strchr(number, ','))) 
00288       *tmp = '\0';
00289    ast_copy_string(cur->number, number, sizeof(cur->number));
00290    cur->order = numorder;
00291    ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00292 
00293    return cur;
00294 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 977 of file app_followme.c.

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

Referenced by app_exec().

00978 {
00979    char buf[80];
00980    time_t end;
00981    struct ast_channel *chan = data;
00982 
00983    time(&end);
00984 
00985    ast_channel_lock(chan);
00986    if (chan->cdr->answer.tv_sec) {
00987       snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec);
00988       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
00989    }
00990 
00991    if (chan->cdr->start.tv_sec) {
00992       snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec);
00993       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
00994    }
00995    ast_channel_unlock(chan);
00996 }

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

Definition at line 998 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

Referenced by app_exec().

00999 {
01000    bconfig->end_bridge_callback_data = originator;
01001 }

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

Definition at line 917 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(), ast_variable::next, profile_set_param(), SENTINEL, str, number::timeout, and var.

Referenced by app_exec().

00918 {
00919    struct ast_variable *var = ast_load_realtime("followme", "name", name, SENTINEL), *v;
00920    struct ast_config *cfg;
00921    const char *catg;
00922    struct call_followme *new;
00923    struct ast_str *str = ast_str_create(16);
00924 
00925    if (!var) {
00926       return NULL;
00927    }
00928 
00929    if (!(new = alloc_profile(name))) {
00930       return NULL;
00931    }
00932 
00933    for (v = var; v; v = v->next) {
00934       if (!strcasecmp(v->name, "active")) {
00935          if (ast_false(v->value)) {
00936             ast_mutex_destroy(&new->lock);
00937             ast_free(new);
00938             return NULL;
00939          }
00940       } else {
00941          profile_set_param(new, v->name, v->value, 0, 0);
00942       }
00943    }
00944 
00945    ast_variables_destroy(var);
00946    new->realtime = 1;
00947 
00948    /* Load numbers */
00949    if (!(cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", name, SENTINEL))) {
00950       ast_mutex_destroy(&new->lock);
00951       ast_free(new);
00952       return NULL;
00953    }
00954 
00955    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
00956       const char *numstr, *timeoutstr, *ordstr;
00957       int timeout;
00958       struct number *cur;
00959       if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
00960          continue;
00961       }
00962       if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) || sscanf(timeoutstr, "%30d", &timeout) != 1 || timeout < 1) {
00963          timeout = 25;
00964       }
00965       /* This one has to exist; it was part of the query */
00966       ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
00967       ast_str_set(&str, 0, "%s", numstr);
00968       if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
00969          AST_LIST_INSERT_TAIL(&new->numbers, cur, entry);
00970       }
00971    }
00972    ast_config_destroy(cfg);
00973 
00974    return new;
00975 }

static void findmeexec ( struct fm_args tpargs  )  [static]

Definition at line 777 of file app_followme.c.

References ast_channel::accountcode, 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_check_hangup(), ast_copy_string(), ast_debug, 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_request(), ast_set_callerid(), ast_string_field_set, ast_verb, ast_channel::cdr, fm_args::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, clear_caller(), findme_user::cleared, fm_args::cnumbers, fm_args::context, findme_user::dialarg, ast_channel::hangupcause, ast_channel::language, language, LOG_ERROR, LOG_WARNING, ast_channel::musicclass, musicclass, fm_args::namerecloc, ast_channel::nativeformats, fm_args::nextindp, number::number, findme_user::ochan, number::order, fm_args::outbound, findme_user::state, fm_args::status, fm_args::takecall, number::timeout, and wait_for_winner().

Referenced by app_exec().

00778 {
00779    struct number *nm;
00780    struct ast_channel *outbound;
00781    struct ast_channel *caller;
00782    struct ast_channel *winner = NULL;
00783    char dialarg[512];
00784    int dg, idx;
00785    char *rest, *number;
00786    struct findme_user *tmpuser;
00787    struct findme_user *fmuser;
00788    struct findme_user *headuser;
00789    struct findme_user_listptr *findme_user_list;
00790    int status;
00791 
00792    findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00793    AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00794 
00795    /* We're going to figure out what the longest possible string of digits to collect is */
00796    ynlongest = 0;
00797    if (strlen(tpargs->takecall) > ynlongest)
00798       ynlongest = strlen(tpargs->takecall);
00799    if (strlen(tpargs->nextindp) > ynlongest)
00800       ynlongest = strlen(tpargs->nextindp);
00801 
00802    idx = 1;
00803    caller = tpargs->chan;
00804    AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00805       if (nm->order == idx)
00806          break;
00807 
00808    while (nm) {
00809 
00810       ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
00811 
00812       number = ast_strdupa(nm->number);
00813       ast_debug(3, "examining %s\n", number);
00814       do {
00815          rest = strchr(number, '&');
00816          if (rest) {
00817             *rest = 0;
00818             rest++;
00819          }
00820 
00821          if (!strcmp(tpargs->context, ""))
00822             snprintf(dialarg, sizeof(dialarg), "%s", number);
00823          else
00824             snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00825 
00826          tmpuser = ast_calloc(1, sizeof(*tmpuser));
00827          if (!tmpuser) {
00828             ast_log(LOG_WARNING, "Out of memory!\n");
00829             ast_free(findme_user_list);
00830             return;
00831          }
00832 
00833          outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
00834          if (outbound) {
00835             ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
00836             ast_channel_inherit_variables(tpargs->chan, outbound);
00837             ast_channel_datastore_inherit(tpargs->chan, outbound);
00838             ast_string_field_set(outbound, language, tpargs->chan->language);
00839             ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
00840             ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
00841             ast_verb(3, "calling %s\n", dialarg);
00842             if (!ast_call(outbound,dialarg,0)) {
00843                tmpuser->ochan = outbound;
00844                tmpuser->state = 0;
00845                tmpuser->cleared = 0;
00846                ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00847                AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00848             } else {
00849                ast_verb(3, "couldn't reach at this number.\n"); 
00850                if (outbound) {
00851                   if (!outbound->cdr) 
00852                      outbound->cdr = ast_cdr_alloc();
00853                   if (outbound->cdr) {
00854                      char tmp[256];
00855 
00856                      ast_cdr_init(outbound->cdr, outbound);
00857                      snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00858                      ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00859                      ast_cdr_update(outbound);
00860                      ast_cdr_start(outbound->cdr);
00861                      ast_cdr_end(outbound->cdr);
00862                      /* If the cause wasn't handled properly */
00863                      if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
00864                         ast_cdr_failed(outbound->cdr);
00865                   } else {
00866                      ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00867                      ast_hangup(outbound);
00868                      outbound = NULL;
00869                   }
00870                }
00871             }
00872          } else 
00873             ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00874 
00875          number = rest;
00876       } while (number);
00877 
00878       status = 0;
00879       if (!AST_LIST_EMPTY(findme_user_list))
00880          winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
00881 
00882       while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00883          if (!fmuser->cleared && fmuser->ochan != winner)
00884             clear_caller(fmuser);
00885          ast_free(fmuser);
00886       }
00887 
00888       fmuser = NULL;
00889       tmpuser = NULL;
00890       headuser = NULL;
00891       if (winner)
00892          break;
00893 
00894       if (!caller || ast_check_hangup(caller)) {
00895          tpargs->status = 1;
00896          ast_free(findme_user_list);
00897          return;
00898       }
00899 
00900       idx++;
00901       AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
00902          if (nm->order == idx)
00903             break;
00904       }
00905    }
00906    ast_free(findme_user_list);
00907    if (!winner) 
00908       tpargs->status = 1;
00909    else {
00910       tpargs->status = 100;
00911       tpargs->outbound = winner;
00912    }
00913 
00914    return;
00915 }

static void free_numbers ( struct call_followme f  )  [static]

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

00190 {
00191    /* Free numbers attached to the profile */
00192    struct number *prev;
00193 
00194    while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00195       /* Free the number */
00196       ast_free(prev);
00197    AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00198 
00199    while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00200       /* Free the blacklisted number */
00201       ast_free(prev);
00202    AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00203 
00204    while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00205       /* Free the whitelisted number */
00206       ast_free(prev);
00207    AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00208 }

static void init_profile ( struct call_followme f  )  [static]

Definition at line 237 of file app_followme.c.

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

Referenced by reload_followme().

00238 {
00239    f->active = 1;
00240    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00241 }

static int load_module ( void   )  [static]

Definition at line 1178 of file app_followme.c.

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

01179 {
01180    if(!reload_followme(0))
01181       return AST_MODULE_LOAD_DECLINE;
01182 
01183    return ast_register_application_xml(app, app_exec);
01184 }

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

00247 {
00248 
00249    if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
00250       ast_copy_string(f->moh, val, sizeof(f->moh));
00251    else if (!strcasecmp(param, "context")) 
00252       ast_copy_string(f->context, val, sizeof(f->context));
00253    else if (!strcasecmp(param, "takecall"))
00254       ast_copy_string(f->takecall, val, sizeof(f->takecall));
00255    else if (!strcasecmp(param, "declinecall"))
00256       ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00257    else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00258       ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00259    else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
00260       ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00261    else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
00262       ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00263    else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00264       ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00265    else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
00266       ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00267    else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
00268       ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00269    else if (failunknown) {
00270       if (linenum >= 0)
00271          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00272       else
00273          ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00274    }
00275 }

static int reload ( void   )  [static]

Definition at line 1186 of file app_followme.c.

References reload_followme().

01187 {
01188    reload_followme(1);
01189 
01190    return 0;
01191 }

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

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

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

static int unload_module ( void   )  [static]

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

01161 {
01162    struct call_followme *f;
01163 
01164    ast_unregister_application(app);
01165 
01166    /* Free Memory. Yeah! I'm free! */
01167    AST_RWLIST_WRLOCK(&followmes);
01168    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01169       free_numbers(f);
01170       ast_free(f);
01171    }
01172 
01173    AST_RWLIST_UNLOCK(&followmes);
01174 
01175    return 0;
01176 }

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

Definition at line 509 of file app_followme.c.

References AST_CAUSE_NORMAL_CLEARING, AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, 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_read(), ast_sched_runq(), ast_sched_wait(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), ast_frame::data, findme_user::digts, f, ast_frame::frametype, 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, 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().

00510 {
00511    struct ast_channel *watchers[256];
00512    int pos;
00513    struct ast_channel *winner;
00514    struct ast_frame *f;
00515    int ctstatus = 0;
00516    int dg;
00517    struct findme_user *tmpuser;
00518    int to = 0;
00519    int livechannels = 0;
00520    int tmpto;
00521    long totalwait = 0, wtd = 0, towas = 0;
00522    char *callfromname;
00523    char *pressbuttonname;
00524 
00525    /* ------------ wait_for_winner_channel start --------------- */ 
00526 
00527    callfromname = ast_strdupa(tpargs->callfromprompt);
00528    pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00529 
00530    if (AST_LIST_EMPTY(findme_user_list)) {
00531       ast_verb(3, "couldn't reach at this number.\n");
00532       return NULL;
00533    }
00534 
00535    if (!caller) {
00536       ast_verb(3, "Original caller hungup. Cleanup.\n");
00537       clear_calling_tree(findme_user_list);
00538       return NULL;
00539    }
00540 
00541    totalwait = nm->timeout * 1000;
00542 
00543    while (!ctstatus) {
00544       to = 1000;
00545       pos = 1; 
00546       livechannels = 0;
00547       watchers[0] = caller;
00548 
00549       dg = 0;
00550       winner = NULL;
00551       AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00552          if (tmpuser->state >= 0 && tmpuser->ochan) {
00553             if (tmpuser->state == 3) 
00554                tmpuser->digts += (towas - wtd);
00555             if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00556                ast_verb(3, "We've been waiting for digits longer than we should have.\n");
00557                if (!ast_strlen_zero(namerecloc)) {
00558                   tmpuser->state = 1;
00559                   tmpuser->digts = 0;
00560                   if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00561                      ast_sched_runq(tmpuser->ochan->sched);
00562                   } else {
00563                      ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00564                      return NULL;
00565                   }
00566                } else {
00567                   tmpuser->state = 2;
00568                   tmpuser->digts = 0;
00569                   if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00570                      ast_sched_runq(tmpuser->ochan->sched);
00571                   else {
00572                      ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00573                      return NULL;
00574                   }
00575                }
00576             }
00577             if (tmpuser->ochan->stream) {
00578                ast_sched_runq(tmpuser->ochan->sched);
00579                tmpto = ast_sched_wait(tmpuser->ochan->sched);
00580                if (tmpto > 0 && tmpto < to)
00581                   to = tmpto;
00582                else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00583                   ast_stopstream(tmpuser->ochan);
00584                   if (tmpuser->state == 1) {
00585                      ast_verb(3, "Playback of the call-from file appears to be done.\n");
00586                      if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00587                         tmpuser->state = 2;
00588                      } else {
00589                         ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00590                         memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00591                         tmpuser->ynidx = 0;
00592                         if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00593                            tmpuser->state = 3;
00594                         else {
00595                            ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00596                            return NULL;
00597                         } 
00598                      }
00599                   } else if (tmpuser->state == 2) {
00600                      ast_verb(3, "Playback of name file appears to be done.\n");
00601                      memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00602                      tmpuser->ynidx = 0;
00603                      if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00604                         tmpuser->state = 3;
00605                      } else {
00606                         return NULL;
00607                      } 
00608                   } else if (tmpuser->state == 3) {
00609                      ast_verb(3, "Playback of the next step file appears to be done.\n");
00610                      tmpuser->digts = 0;
00611                   }
00612                }
00613             }
00614             watchers[pos++] = tmpuser->ochan;
00615             livechannels++;
00616          }
00617       }
00618 
00619       tmpto = to;
00620       if (to < 0) {
00621          to = 1000;
00622          tmpto = 1000;
00623       }
00624       towas = to;
00625       winner = ast_waitfor_n(watchers, pos, &to);
00626       tmpto -= to;
00627       totalwait -= tmpto;
00628       wtd = to;
00629       if (totalwait <= 0) {
00630          ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00631          clear_calling_tree(findme_user_list);
00632          return NULL;
00633       }
00634       if (winner) {
00635          /* Need to find out which channel this is */
00636          dg = 0;
00637          while ((winner != watchers[dg]) && (dg < 256))
00638             dg++;
00639          AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
00640             if (tmpuser->ochan == winner)
00641                break;
00642          f = ast_read(winner);
00643          if (f) {
00644             if (f->frametype == AST_FRAME_CONTROL) {
00645                switch(f->subclass) {
00646                case AST_CONTROL_HANGUP:
00647                   ast_verb(3, "%s received a hangup frame.\n", winner->name);
00648                   if (f->data.uint32) {
00649                      winner->hangupcause = f->data.uint32;
00650                   }
00651                   if (dg == 0) {
00652                      ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
00653                      clear_calling_tree(findme_user_list);
00654                      ctstatus = -1;
00655                   }
00656                   break;
00657                case AST_CONTROL_ANSWER:
00658                   ast_verb(3, "%s answered %s\n", winner->name, caller->name);
00659                   /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
00660                   winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00661                   caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00662                   ast_verb(3, "Starting playback of %s\n", callfromname);
00663                   if (dg > 0) {
00664                      if (!ast_strlen_zero(namerecloc)) {
00665                         if (!ast_streamfile(winner, callfromname, winner->language)) {
00666                            ast_sched_runq(winner->sched);
00667                            tmpuser->state = 1;
00668                         } else {
00669                            ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00670                            ast_frfree(f);
00671                            return NULL;
00672                         }
00673                      } else {
00674                         tmpuser->state = 2;
00675                         if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00676                            ast_sched_runq(tmpuser->ochan->sched);
00677                         else {
00678                            ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00679                            ast_frfree(f);
00680                            return NULL;
00681                         }
00682                      }
00683                   }
00684                   break;
00685                case AST_CONTROL_BUSY:
00686                   ast_verb(3, "%s is busy\n", winner->name);
00687                   break;
00688                case AST_CONTROL_CONGESTION:
00689                   ast_verb(3, "%s is circuit-busy\n", winner->name);
00690                   break;
00691                case AST_CONTROL_RINGING:
00692                   ast_verb(3, "%s is ringing\n", winner->name);
00693                   break;
00694                case AST_CONTROL_PROGRESS:
00695                   ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
00696                   break;
00697                case AST_CONTROL_VIDUPDATE:
00698                   ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
00699                   break;
00700                case AST_CONTROL_SRCUPDATE:
00701                   ast_verb(3, "%s requested a source update, passing it to %s\n", winner->name, caller->name);
00702                   break;
00703                case AST_CONTROL_PROCEEDING:
00704                   ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
00705                   break;
00706                case AST_CONTROL_HOLD:
00707                   ast_verb(3, "Call on %s placed on hold\n", winner->name);
00708                   break;
00709                case AST_CONTROL_UNHOLD:
00710                   ast_verb(3, "Call on %s left from hold\n", winner->name);
00711                   break;
00712                case AST_CONTROL_OFFHOOK:
00713                case AST_CONTROL_FLASH:
00714                   /* Ignore going off hook and flash */
00715                   break;
00716                case -1:
00717                   ast_verb(3, "%s stopped sounds\n", winner->name);
00718                   break;
00719                default:
00720                   ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
00721                   break;
00722                }
00723             } 
00724             if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00725                if (winner->stream)
00726                   ast_stopstream(winner);
00727                tmpuser->digts = 0;
00728                ast_debug(1, "DTMF received: %c\n",(char) f->subclass);
00729                tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
00730                tmpuser->ynidx++;
00731                ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00732                if (tmpuser->ynidx >= ynlongest) {
00733                   ast_debug(1, "reached longest possible match - doing evals\n");
00734                   if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00735                      ast_debug(1, "Match to take the call!\n");
00736                      ast_frfree(f);
00737                      return tmpuser->ochan;
00738                   }
00739                   if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00740                      ast_debug(1, "Next in dial plan step requested.\n");
00741                      *status = 1;
00742                      ast_frfree(f);
00743                      return NULL;
00744                   }
00745 
00746                }
00747             }
00748 
00749             ast_frfree(f);
00750          } else {
00751             if (winner) {
00752                ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);                   
00753                if (!dg) {
00754                   clear_calling_tree(findme_user_list);
00755                   return NULL;
00756                } else {
00757                   tmpuser->state = -1;
00758                   ast_hangup(winner);  
00759                   livechannels--;
00760                   ast_debug(1, "live channels left %d\n", livechannels);
00761                   if (!livechannels) {
00762                      ast_verb(3, "no live channels left. exiting.\n");
00763                      return NULL;
00764                   }
00765                }
00766             }
00767          }
00768 
00769       } else
00770          ast_debug(1, "timed out waiting for action\n");
00771    }
00772 
00773    /* --- WAIT FOR WINNER NUMBER END! -----------*/
00774    return NULL;
00775 }


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

Definition at line 1197 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 96 of file app_followme.c.

Definition at line 1197 of file app_followme.c.

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

Definition at line 178 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 175 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 174 of file app_followme.c.

const char* featuredigittostr [static]

Definition at line 173 of file app_followme.c.

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

Definition at line 169 of file app_followme.c.

Referenced by app_exec().

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

Definition at line 177 of file app_followme.c.

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

Definition at line 179 of file app_followme.c.

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

Definition at line 180 of file app_followme.c.

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

Definition at line 181 of file app_followme.c.

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

Definition at line 183 of file app_followme.c.

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

Definition at line 182 of file app_followme.c.

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

Definition at line 177 of file app_followme.c.

int ynlongest = 0 [static]

Definition at line 171 of file app_followme.c.


Generated by  doxygen 1.6.2