Mon Oct 8 12:39:07 2012

Asterisk developer's documentation


app_followme.c File Reference

Find-Me Follow-Me application. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/paths.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/dsp.h"
#include "asterisk/app.h"

Go to the source code of this file.

Data Structures

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

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 void __reg_module (void)
static void __unreg_module (void)
static struct call_followmealloc_profile (const char *fmname)
 Allocate and initialize followme profile.
static int app_exec (struct ast_channel *chan, const char *data)
static void clear_caller (struct findme_user *tmpuser)
static void clear_calling_tree (struct findme_user_listptr *findme_user_list)
static struct numbercreate_followme_number (const char *number, int timeout, int numorder)
 Add a new number.
static void destroy_calling_tree (struct findme_user_listptr *findme_user_list)
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static struct call_followmefind_realtime (const char *name)
static void findmeexec (struct fm_args *tpargs)
static void free_numbers (struct call_followme *f)
static void init_profile (struct call_followme *f)
static int load_module (void)
static void profile_set_param (struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
 Set parameter in profile from configuration file.
static int reload (void)
static int reload_followme (int reload)
 Reload followme application module.
static int unload_module (void)
static struct ast_channelwait_for_winner (struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "FollowMe"
static struct ast_module_infoast_module_info = &__mod_info
static char callfromprompt [PATH_MAX] = "followme/call-from"
static const char * defaultmoh = "default"
static int featuredigittimeout = 5000
static const char * featuredigittostr
static struct ast_app_option followme_opts [128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG },}
static char nextindp [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 void __reg_module ( void   )  [static]

Definition at line 1342 of file app_followme.c.

static void __unreg_module ( void   )  [static]

Definition at line 1342 of file app_followme.c.

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

Allocate and initialize followme profile.

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

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

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 }

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(), and destroy_calling_tree().

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(), findme_user::cleared, and number::entry.

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]

Add a new number.

Definition at line 307 of file app_followme.c.

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

Referenced by app_exec(), 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, findme_user::connected, and number::entry.

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.

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.

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

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

Definition at line 1019 of file app_followme.c.

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

Referenced by app_exec().

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_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_request(), ast_string_field_set, ast_verb, ast_channel::caller, ast_channel::cdr, fm_args::chan, fm_args::cnumbers, ast_channel::connected, fm_args::context, number::entry, ast_channel::hangupcause, ast_party_caller::id, language, LOG_ERROR, LOG_WARNING, musicclass, ast_channel::nativeformats, ast_party_id::number, number::number, number::order, S_COR, ast_party_number::str, number::timeout, and ast_party_number::valid.

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, AST_LIST_REMOVE_HEAD, number::entry, and f.

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 ast_copy_string(), and f.

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(), f, LOG_WARNING, and ast_format::name.

Referenced by 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 alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_variable_browse(), ast_variable_retrieve(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, create_followme_number(), number::entry, f, free_numbers(), init_profile(), LOG_ERROR, LOG_WARNING, ast_format::name, profile_set_param(), number::timeout, and var.

Referenced by load_module(), and reload().

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[90];
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             ast_copy_string(numberstr, var->value, sizeof(numberstr));
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(), number::entry, f, and free_numbers().

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

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

Definition at line 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(), fm_args::callfromprompt, clear_calling_tree(), findme_user::connected, connected, fm_args::connected_in, findme_user::digts, number::entry, f, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE, fm_args::followmeflags, ast_channel::hangupcause, ast_channel::language, LOG_NOTICE, LOG_WARNING, ast_channel::name, fm_args::nextindp, fm_args::norecordingprompt, findme_user::ochan, fm_args::optionsprompt, findme_user::pending_connected_update, fm_args::pending_in_connected_update, ast_channel::sched, findme_user::state, ast_channel::stream, fm_args::takecall, number::timeout, ast_channel::timingfunc, findme_user::yn, and findme_user::ynidx.

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

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

Definition at line 1342 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 103 of file app_followme.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1342 of file app_followme.c.

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

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

Referenced by ast_bridge_call(), and process_config().

const char* featuredigittostr [static]

Definition at line 201 of file app_followme.c.

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

Definition at line 199 of file app_followme.c.

Referenced by app_exec().

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 Mon Oct 8 12:39:07 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7