Mon Jun 27 16:50:58 2011

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) }

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 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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT },}
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 

Definition at line 161 of file app_followme.c.

00161      {
00162    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00163    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00164    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00165    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3)
00166 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1212 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1212 of file app_followme.c.

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

Allocate and initialize followme profile.

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

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

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

Definition at line 1017 of file app_followme.c.

References ast_channel::_state, args, 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_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::context, create_followme_number(), end_bridge_callback(), end_bridge_callback_data_fixup(), f, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), LOG_ERROR, 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::plsholdprompt, S_OR, fm_args::sorryprompt, fm_args::status, fm_args::statusprompt, fm_args::takecall, THRESHOLD_SILENCE, and number::timeout.

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

static void clear_caller ( struct findme_user tmpuser  )  [static]

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

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

static void clear_calling_tree ( struct findme_user_listptr findme_user_list  )  [static]

Definition at line 502 of file app_followme.c.

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

Referenced by wait_for_winner().

00503 {
00504    struct findme_user *tmpuser;
00505 
00506    AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00507       clear_caller(tmpuser);
00508       tmpuser->cleared = 1;
00509    }
00510 }

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

Add a new number.

Definition at line 282 of file app_followme.c.

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

Referenced by app_exec(), and reload_followme().

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

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 991 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.

00992 {
00993    char buf[80];
00994    time_t end;
00995    struct ast_channel *chan = data;
00996 
00997    time(&end);
00998 
00999    ast_channel_lock(chan);
01000    if (chan->cdr->answer.tv_sec) {
01001       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
01002       pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01003    }
01004 
01005    if (chan->cdr->start.tv_sec) {
01006       snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
01007       pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01008    }
01009    ast_channel_unlock(chan);
01010 }

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

Definition at line 1012 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

01013 {
01014    bconfig->end_bridge_callback_data = originator;
01015 }

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

Definition at line 931 of file app_followme.c.

References alloc_profile(), ast_false(), ast_free, ast_load_realtime(), ast_mutex_destroy, ast_str_create(), SENTINEL, str, and var.

Referenced by app_exec().

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

static void findmeexec ( struct fm_args tpargs  )  [static]

Definition at line 782 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_exists_extension(), ast_free, ast_hangup(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_request(), ast_set_callerid(), ast_strdupa, ast_string_field_set, ast_verb, ast_channel::caller, fm_args::chan, clear_caller(), findme_user::cleared, fm_args::cnumbers, fm_args::context, free, ast_party_caller::id, ast_channel::language, language, LOG_ERROR, LOG_WARNING, ast_channel::musicclass, musicclass, ast_party_id::name, fm_args::namerecloc, ast_channel::nativeformats, fm_args::nextindp, ast_party_id::number, number::number, findme_user::ochan, number::order, fm_args::outbound, S_COR, fm_args::status, ast_party_name::str, ast_party_number::str, fm_args::takecall, number::timeout, ast_party_name::valid, ast_party_number::valid, and wait_for_winner().

Referenced by app_exec().

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

static void free_numbers ( struct call_followme f  )  [static]

Definition at line 193 of file app_followme.c.

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, and f.

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

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

static void init_profile ( struct call_followme f  )  [static]

Definition at line 241 of file app_followme.c.

References ast_copy_string(), and f.

Referenced by reload_followme().

00242 {
00243    f->active = 1;
00244    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00245 }

static int load_module ( void   )  [static]

Definition at line 1193 of file app_followme.c.

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

01194 {
01195    if(!reload_followme(0))
01196       return AST_MODULE_LOAD_DECLINE;
01197 
01198    return ast_register_application_xml(app, app_exec);
01199 }

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 250 of file app_followme.c.

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

Referenced by reload_followme().

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

static int reload ( void   )  [static]

Definition at line 1201 of file app_followme.c.

References reload_followme().

01202 {
01203    reload_followme(1);
01204 
01205    return 0;
01206 }

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

Definition at line 302 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_strdupa, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), 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().

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

static int unload_module ( void   )  [static]

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

01176 {
01177    struct call_followme *f;
01178 
01179    ast_unregister_application(app);
01180 
01181    /* Free Memory. Yeah! I'm free! */
01182    AST_RWLIST_WRLOCK(&followmes);
01183    while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01184       free_numbers(f);
01185       ast_free(f);
01186    }
01187 
01188    AST_RWLIST_UNLOCK(&followmes);
01189 
01190    return 0;
01191 }

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]

Definition at line 514 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_strdupa, ast_streamfile(), ast_strlen_zero(), ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), findme_user::digts, f, 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, fm_args::takecall, number::timeout, ast_channel::timingfunc, findme_user::yn, and findme_user::ynidx.

Referenced by findmeexec().

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


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

Definition at line 1212 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 98 of file app_followme.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1212 of file app_followme.c.

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

Definition at line 182 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 179 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 178 of file app_followme.c.

Referenced by ast_bridge_call(), and load_config().

const char* featuredigittostr [static]

Definition at line 177 of file app_followme.c.

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

Definition at line 173 of file app_followme.c.

Referenced by app_exec().

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

Definition at line 181 of file app_followme.c.

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

Definition at line 183 of file app_followme.c.

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

Definition at line 184 of file app_followme.c.

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

Definition at line 185 of file app_followme.c.

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

Definition at line 187 of file app_followme.c.

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

Definition at line 186 of file app_followme.c.

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

Definition at line 181 of file app_followme.c.

int ynlongest = 0 [static]

Definition at line 175 of file app_followme.c.


Generated on Mon Jun 27 16:50:58 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7