Mon Aug 31 12:30:15 2015

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  findme_user
struct  fm_args
struct  number
 Number structure. More...

Defines

#define MAX_YN_STRING   20

Enumerations

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

Functions

static struct call_followmealloc_profile (const char *fmname)
 Allocate and initialize followme profile.
static int app_exec (struct ast_channel *chan, const char *data)
 AST_APP_OPTIONS (followme_opts,{AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME), AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT), AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE), AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG), AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG),})
 AST_LIST_HEAD_NOLOCK (findme_user_listptr, findme_user)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,"Find-Me/Follow-Me Application",.load=load_module,.unload=unload_module,.reload=reload,)
static AST_RWLIST_HEAD_STATIC (followmes, call_followme)
static void clear_caller (struct findme_user *tmpuser)
static void clear_calling_tree (struct findme_user_listptr *findme_user_list)
static struct numbercreate_followme_number (const char *number, int timeout, int numorder)
 Add a new number.
static void destroy_calling_tree (struct findme_user_listptr *findme_user_list)
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static struct call_followmefind_realtime (const char *name)
static void findmeexec (struct fm_args *tpargs)
static void free_numbers (struct call_followme *f)
static void init_profile (struct call_followme *f)
static int load_module (void)
static void profile_set_param (struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
 Set parameter in profile from configuration file.
static int reload (void)
static int reload_followme (int reload)
 Reload followme application module.
static int unload_module (void)
static struct ast_channelwait_for_winner (struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)

Variables

static char * app = "FollowMe"
static char callfromprompt [PATH_MAX] = "followme/call-from"
static const char * defaultmoh = "default"
static int featuredigittimeout = 5000
static const char * featuredigittostr
static char nextindp [MAX_YN_STRING] = "2"
static char norecordingprompt [PATH_MAX] = "followme/no-recording"
static char optionsprompt [PATH_MAX] = "followme/options"
static char plsholdprompt [PATH_MAX] = "followme/pls-hold-while-try"
static char sorryprompt [PATH_MAX] = "followme/sorry"
static char statusprompt [PATH_MAX] = "followme/status"
static char takecall [MAX_YN_STRING] = "1"

Detailed Description

Find-Me Follow-Me application.

Author:
BJ Weschke <bweschke@btwtech.com>

Definition in file app_followme.c.


Define Documentation

#define MAX_YN_STRING   20

Maximum accept/decline DTMF string plus terminator.

Definition at line 106 of file app_followme.c.


Enumeration Type Documentation

anonymous enum
Enumerator:
FOLLOWMEFLAG_STATUSMSG 
FOLLOWMEFLAG_RECORDNAME 
FOLLOWMEFLAG_UNREACHABLEMSG 
FOLLOWMEFLAG_DISABLEHOLDPROMPT 
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE 

Definition at line 185 of file app_followme.c.

00185      {
00186    FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00187    FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00188    FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00189    FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00190    FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4),
00191 };


Function Documentation

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

Allocate and initialize followme profile.

Definition at line 241 of file app_followme.c.

References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, call_followme::callfromprompt, call_followme::context, f, call_followme::lock, 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().

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

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

Definition at line 1126 of file app_followme.c.

References ast_channel::_state, call_followme::active, args, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_connected_line_macro(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_unlock, ast_channel_update_connected_line(), ast_config_AST_SPOOL_DIR, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_get_threshold_from_settings(), AST_FEATURE_AUTOMON, AST_FEATURE_REDIRECT, ast_fileexists(), ast_free, ast_hangup(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_moh_start(), ast_moh_stop(), ast_mutex_lock, ast_mutex_unlock, ast_party_connected_line_free(), ast_play_and_record(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_channel::caller, call_followme::callfromprompt, fm_args::chan, call_followme::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(), FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, free_numbers(), call_followme::lock, LOG_WARNING, call_followme::moh, fm_args::mohclass, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, number::number, call_followme::optionsprompt, number::order, call_followme::plsholdprompt, call_followme::realtime, S_OR, call_followme::sorryprompt, call_followme::statusprompt, call_followme::takecall, THRESHOLD_SILENCE, and number::timeout.

Referenced by load_module().

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

AST_APP_OPTIONS ( followme_opts   ) 
AST_LIST_HEAD_NOLOCK ( findme_user_listptr  ,
findme_user   
)
AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_DEFAULT  ,
"Find-Me/Follow-Me Application"  ,
load = load_module,
unload = unload_module,
reload = reload 
)
static AST_RWLIST_HEAD_STATIC ( followmes  ,
call_followme   
) [static]
static void clear_caller ( struct findme_user tmpuser  )  [static]

Definition at line 499 of file app_followme.c.

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

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

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

static void clear_calling_tree ( struct findme_user_listptr *  findme_user_list  )  [static]

Definition at line 533 of file app_followme.c.

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

Referenced by wait_for_winner().

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

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

Add a new number.

Definition at line 307 of file app_followme.c.

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

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

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

static void destroy_calling_tree ( struct findme_user_listptr *  findme_user_list  )  [static]

Definition at line 543 of file app_followme.c.

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

Referenced by findmeexec().

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

static void end_bridge_callback ( void *  data  )  [static]

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

Referenced by app_exec().

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

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

Definition at line 1121 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

Referenced by app_exec().

01122 {
01123    bconfig->end_bridge_callback_data = originator;
01124 }

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

Definition at line 1019 of file app_followme.c.

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

Referenced by app_exec().

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

static void findmeexec ( struct fm_args tpargs  )  [static]

Definition at line 873 of file app_followme.c.

References accountcode, ast_best_codec(), ast_call(), ast_calloc, ast_cause2str(), ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, ast_hangup(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_party_connected_line_free(), ast_request(), ast_string_field_set, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, clear_caller(), findme_user::cleared, findme_user::connected, ast_channel::connected, destroy_calling_tree(), findme_user::dialarg, ast_channel::hangupcause, ast_party_caller::id, language, LOG_ERROR, LOG_WARNING, musicclass, ast_channel::nativeformats, ast_party_id::number, number::number, findme_user::ochan, number::order, findme_user::pending_connected_update, S_COR, findme_user::state, ast_party_number::str, number::timeout, ast_party_number::valid, and wait_for_winner().

Referenced by app_exec().

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

static void free_numbers ( struct call_followme f  )  [static]

Definition at line 218 of file app_followme.c.

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, and AST_LIST_REMOVE_HEAD.

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

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

static void init_profile ( struct call_followme f  )  [static]

Definition at line 266 of file app_followme.c.

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

Referenced by reload_followme().

00267 {
00268    f->active = 1;
00269    ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00270 }

static int load_module ( void   )  [static]

Definition at line 1323 of file app_followme.c.

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

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

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

Set parameter in profile from configuration file.

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

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

static int reload ( void   )  [static]

Definition at line 1331 of file app_followme.c.

References reload_followme().

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

static int reload_followme ( int  reload  )  [static]

Reload followme application module.

Definition at line 327 of file app_followme.c.

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

Referenced by load_module(), and reload().

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

static int unload_module ( void   )  [static]

Definition at line 1305 of file app_followme.c.

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

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

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

Definition at line 557 of file app_followme.c.

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

Referenced by findmeexec().

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


Variable Documentation

char* app = "FollowMe" [static]

Definition at line 103 of file app_followme.c.

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

Definition at line 207 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 203 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 202 of file app_followme.c.

const char* featuredigittostr [static]

Definition at line 201 of file app_followme.c.

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

Definition at line 206 of file app_followme.c.

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

Definition at line 208 of file app_followme.c.

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

Definition at line 209 of file app_followme.c.

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

Definition at line 210 of file app_followme.c.

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

Definition at line 212 of file app_followme.c.

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

Definition at line 211 of file app_followme.c.

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

Definition at line 205 of file app_followme.c.


Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1