Thu Apr 28 2011 17:15:27

Asterisk developer's documentation


app_followme.c File Reference

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

#include "asterisk.h"
#include <signal.h>
#include "asterisk/paths.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/dsp.h"
#include "asterisk/app.h"
Include dependency graph for app_followme.c:

Go to the source code of this file.

Data Structures

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

Enumerations

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

Functions

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

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "FollowMe"
static struct ast_module_infoast_module_info = &__mod_info
static char callfromprompt [PATH_MAX] = "followme/call-from"
static const char * defaultmoh = "default"
static int featuredigittimeout = 5000
static const char * featuredigittostr
static struct ast_app_option followme_opts [128] = { [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG }, [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG },}
static struct followmes followmes
static char nextindp [20] = "2"
static char norecordingprompt [PATH_MAX] = "followme/no-recording"
static char optionsprompt [PATH_MAX] = "followme/options"
static char plsholdprompt [PATH_MAX] = "followme/pls-hold-while-try"
static char sorryprompt [PATH_MAX] = "followme/sorry"
static char statusprompt [PATH_MAX] = "followme/status"
static char takecall [20] = "1"
static int ynlongest = 0

Detailed Description

Find-Me Follow-Me application.

Author:
BJ Weschke <bweschke@btwtech.com>

Definition in file app_followme.c.


Enumeration Type Documentation

anonymous enum
Enumerator:
FOLLOWMEFLAG_STATUSMSG 
FOLLOWMEFLAG_RECORDNAME 
FOLLOWMEFLAG_UNREACHABLEMSG 

Definition at line 159 of file app_followme.c.


Function Documentation

static void __fini_followmes ( void  ) [static]

Definition at line 186 of file app_followme.c.

{
static void __init_followmes ( void  ) [static]

Definition at line 186 of file app_followme.c.

{
static void __reg_module ( void  ) [static]

Definition at line 1203 of file app_followme.c.

static void __unreg_module ( void  ) [static]

Definition at line 1203 of file app_followme.c.

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

Allocate and initialize followme profile.

Definition at line 212 of file app_followme.c.

References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init(), call_followme::blnumbers, call_followme::callfromprompt, call_followme::context, f, call_followme::lock, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::numbers, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, call_followme::takecall, and call_followme::wlnumbers.

Referenced by find_realtime(), and reload_followme().

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

Definition at line 1009 of file app_followme.c.

References ast_channel::_state, call_followme::active, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_bridge_call(), ast_channel_make_compatible(), ast_config_AST_SPOOL_DIR, ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_get_threshold_from_settings(), AST_FEATURE_AUTOMON, AST_FEATURE_REDIRECT, ast_fileexists(), ast_free, ast_hangup(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_moh_start(), ast_moh_stop(), ast_mutex_lock(), ast_mutex_unlock(), ast_play_and_record(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), call_followme::callfromprompt, fm_args::callfromprompt, chan, fm_args::chan, fm_args::cnumbers, call_followme::context, fm_args::context, create_followme_number(), end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, f, ast_bridge_config::features_callee, ast_bridge_config::features_caller, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), ast_channel::language, call_followme::lock, LOG_ERROR, LOG_WARNING, call_followme::moh, fm_args::mohclass, ast_channel::name, call_followme::name, fm_args::namerecloc, call_followme::nextindp, fm_args::nextindp, call_followme::norecordingprompt, fm_args::norecordingprompt, number::number, call_followme::numbers, call_followme::optionsprompt, fm_args::optionsprompt, number::order, fm_args::outbound, call_followme::plsholdprompt, fm_args::plsholdprompt, call_followme::realtime, S_OR, call_followme::sorryprompt, fm_args::sorryprompt, fm_args::status, call_followme::statusprompt, fm_args::statusprompt, call_followme::takecall, fm_args::takecall, THRESHOLD_SILENCE, number::timeout, and ast_channel::uniqueid.

Referenced by load_module().

{
   struct fm_args targs = { 0, };
   struct ast_bridge_config config;
   struct call_followme *f;
   struct number *nm, *newnm;
   int res = 0;
   char *argstr;
   char namerecloc[255];
   int duration = 0;
   struct ast_channel *caller;
   struct ast_channel *outbound;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(followmeid);
      AST_APP_ARG(options);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
      return -1;
   }

   if (!(argstr = ast_strdupa((char *)data))) {
      ast_log(LOG_ERROR, "Out of memory!\n");
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, argstr);

   if (ast_strlen_zero(args.followmeid)) {
      ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
      return -1;
   }

   AST_RWLIST_RDLOCK(&followmes);
   AST_RWLIST_TRAVERSE(&followmes, f, entry) {
      if (!strcasecmp(f->name, args.followmeid) && (f->active))
         break;
   }
   AST_RWLIST_UNLOCK(&followmes);

   ast_debug(1, "New profile %s.\n", args.followmeid);

   if (!f) {
      f = find_realtime(args.followmeid);
   }

   if (!f) {
      ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
      return 0;
   }

   /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
   if (args.options) 
      ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);

   /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
   ast_mutex_lock(&f->lock);
   targs.mohclass = ast_strdupa(f->moh);
   ast_copy_string(targs.context, f->context, sizeof(targs.context));
   ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
   ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
   ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
   ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
   ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
   ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
   ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
   ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
   /* Copy the numbers we're going to use into another list in case the master list should get modified 
      (and locked) while we're trying to do a follow-me */
   AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
   AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
      newnm = create_followme_number(nm->number, nm->timeout, nm->order);
      AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
   }
   ast_mutex_unlock(&f->lock);

   /* Answer the call */
   if (chan->_state != AST_STATE_UP) {
      ast_answer(chan);
   }

   if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG)) 
      ast_stream_and_wait(chan, targs.statusprompt, "");

   snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
   duration = 5;

   if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME)) 
      if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0)
         goto outrun;

   if (!ast_fileexists(namerecloc, NULL, chan->language))
      ast_copy_string(namerecloc, "", sizeof(namerecloc));

   if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
      goto outrun;
   if (ast_waitstream(chan, "") < 0)
      goto outrun;
   ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);

   targs.status = 0;
   targs.chan = chan;
   ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));

   findmeexec(&targs);

   while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
      ast_free(nm);

   if (!ast_strlen_zero(namerecloc))
      unlink(namerecloc);

   if (targs.status != 100) {
      ast_moh_stop(chan);
      if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) 
         ast_stream_and_wait(chan, targs.sorryprompt, "");
      res = 0;
   } else {
      caller = chan;
      outbound = targs.outbound;
      /* Bridge the two channels. */

      memset(&config, 0, sizeof(config));
      ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
      ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
      ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
      config.end_bridge_callback = end_bridge_callback;
      config.end_bridge_callback_data = chan;
      config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;

      ast_moh_stop(caller);
      /* Be sure no generators are left on it */
      ast_deactivate_generator(caller);
      /* Make sure channels are compatible */
      res = ast_channel_make_compatible(caller, outbound);
      if (res < 0) {
         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
         ast_hangup(outbound);
         goto outrun;
      }
      res = ast_bridge_call(caller, outbound, &config);
      if (outbound)
         ast_hangup(outbound);
   }

   outrun:

   if (f->realtime) {
      /* Not in list */
      free_numbers(f);
      ast_free(f);
   }

   return res;
}
static void clear_caller ( struct findme_user tmpuser) [static]

Definition at line 468 of file app_followme.c.

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

Referenced by clear_calling_tree(), and findmeexec().

{
   struct ast_channel *outbound;

   if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
      outbound = tmpuser->ochan;
      if (!outbound->cdr) {
         outbound->cdr = ast_cdr_alloc();
         if (outbound->cdr)
            ast_cdr_init(outbound->cdr, outbound);
      }
      if (outbound->cdr) {
         char tmp[256];

         snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
         ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
         ast_cdr_update(outbound);
         ast_cdr_start(outbound->cdr);
         ast_cdr_end(outbound->cdr);
         /* If the cause wasn't handled properly */
         if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
            ast_cdr_failed(outbound->cdr);
      } else
         ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
      ast_hangup(tmpuser->ochan);
   }

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

Definition at line 497 of file app_followme.c.

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

Referenced by wait_for_winner().

{
   struct findme_user *tmpuser;

   AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
      clear_caller(tmpuser);
      tmpuser->cleared = 1;
   }
}
static struct number* create_followme_number ( char *  number,
int  timeout,
int  numorder 
) [static, read]

Add a new number.

Definition at line 278 of file app_followme.c.

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

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

{
   struct number *cur;
   char *tmp;

   if (!(cur = ast_calloc(1, sizeof(*cur))))
      return NULL;

   cur->timeout = timeout;
   if ((tmp = strchr(number, ','))) 
      *tmp = '\0';
   ast_copy_string(cur->number, number, sizeof(cur->number));
   cur->order = numorder;
   ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);

   return cur;
}
static void end_bridge_callback ( void *  data) [static]

Definition at line 983 of file app_followme.c.

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

Referenced by app_exec().

{
   char buf[80];
   time_t end;
   struct ast_channel *chan = data;

   time(&end);

   ast_channel_lock(chan);
   if (chan->cdr->answer.tv_sec) {
      snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
      pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
   }

   if (chan->cdr->start.tv_sec) {
      snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
      pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
   }
   ast_channel_unlock(chan);
}
static void end_bridge_callback_data_fixup ( struct ast_bridge_config bconfig,
struct ast_channel originator,
struct ast_channel terminator 
) [static]

Definition at line 1004 of file app_followme.c.

References ast_bridge_config::end_bridge_callback_data.

Referenced by app_exec().

{
   bconfig->end_bridge_callback_data = originator;
}
static struct call_followme* find_realtime ( const char *  name) [static, read]

Definition at line 923 of file app_followme.c.

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

Referenced by app_exec().

{
   struct ast_variable *var = ast_load_realtime("followme", "name", name, SENTINEL), *v;
   struct ast_config *cfg;
   const char *catg;
   struct call_followme *new;
   struct ast_str *str = ast_str_create(16);

   if (!var) {
      return NULL;
   }

   if (!(new = alloc_profile(name))) {
      return NULL;
   }

   for (v = var; v; v = v->next) {
      if (!strcasecmp(v->name, "active")) {
         if (ast_false(v->value)) {
            ast_mutex_destroy(&new->lock);
            ast_free(new);
            return NULL;
         }
      } else {
         profile_set_param(new, v->name, v->value, 0, 0);
      }
   }

   ast_variables_destroy(var);
   new->realtime = 1;

   /* Load numbers */
   if (!(cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", name, SENTINEL))) {
      ast_mutex_destroy(&new->lock);
      ast_free(new);
      return NULL;
   }

   for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
      const char *numstr, *timeoutstr, *ordstr;
      int timeout;
      struct number *cur;
      if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
         continue;
      }
      if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) || sscanf(timeoutstr, "%30d", &timeout) != 1 || timeout < 1) {
         timeout = 25;
      }
      /* This one has to exist; it was part of the query */
      ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
      ast_str_set(&str, 0, "%s", numstr);
      if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
         AST_LIST_INSERT_TAIL(&new->numbers, cur, entry);
      }
   }
   ast_config_destroy(cfg);

   return new;
}
static void findmeexec ( struct fm_args tpargs) [static]

Definition at line 777 of file app_followme.c.

References ast_channel::accountcode, accountcode, ast_best_codec(), ast_call(), ast_calloc, ast_cause2str(), ast_cdr_alloc(), ast_cdr_disposition(), ast_cdr_end(), ast_cdr_failed(), ast_cdr_init(), ast_cdr_setapp(), ast_cdr_start(), ast_cdr_update(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_check_hangup(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, ast_hangup(), AST_LIST_EMPTY, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_request(), ast_set_callerid(), ast_strdupa, ast_string_field_set, ast_verb, ast_channel::cdr, fm_args::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, clear_caller(), findme_user::cleared, fm_args::cnumbers, fm_args::context, findme_user::dialarg, free, ast_channel::hangupcause, ast_channel::language, language, LOG_ERROR, LOG_WARNING, ast_channel::musicclass, musicclass, fm_args::namerecloc, ast_channel::nativeformats, fm_args::nextindp, number::number, findme_user::ochan, number::order, fm_args::outbound, findme_user::state, fm_args::status, status, fm_args::takecall, number::timeout, and wait_for_winner().

Referenced by app_exec().

{
   struct number *nm;
   struct ast_channel *outbound;
   struct ast_channel *caller;
   struct ast_channel *winner = NULL;
   char dialarg[512];
   int dg, idx;
   char *rest, *number;
   struct findme_user *tmpuser;
   struct findme_user *fmuser;
   struct findme_user *headuser;
   struct findme_user_listptr *findme_user_list;
   int status;

   findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
   AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);

   /* We're going to figure out what the longest possible string of digits to collect is */
   ynlongest = 0;
   if (strlen(tpargs->takecall) > ynlongest)
      ynlongest = strlen(tpargs->takecall);
   if (strlen(tpargs->nextindp) > ynlongest)
      ynlongest = strlen(tpargs->nextindp);

   idx = 1;
   caller = tpargs->chan;
   AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
      if (nm->order == idx)
         break;

   while (nm) {
      ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);

      number = ast_strdupa(nm->number);
      ast_debug(3, "examining %s\n", number);
      do {
         rest = strchr(number, '&');
         if (rest) {
            *rest = 0;
            rest++;
         }

         /* We check if that context exists, before creating the ast_channel struct needed */
         if (!ast_exists_extension(caller, tpargs->context, number, 1, caller->cid.cid_num)) {
            /* XXX Should probably restructure to simply skip this item, instead of returning. XXX */
            ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
            free(findme_user_list);
            return;
         }

         if (!strcmp(tpargs->context, ""))
            snprintf(dialarg, sizeof(dialarg), "%s", number);
         else
            snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);

         tmpuser = ast_calloc(1, sizeof(*tmpuser));
         if (!tmpuser) {
            ast_free(findme_user_list);
            return;
         }

         outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
         if (outbound) {
            ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
            ast_channel_inherit_variables(tpargs->chan, outbound);
            ast_channel_datastore_inherit(tpargs->chan, outbound);
            ast_string_field_set(outbound, language, tpargs->chan->language);
            ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode);
            ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass);
            ast_verb(3, "calling %s\n", dialarg);
            if (!ast_call(outbound,dialarg,0)) {
               tmpuser->ochan = outbound;
               tmpuser->state = 0;
               tmpuser->cleared = 0;
               ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
               AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
            } else {
               ast_verb(3, "couldn't reach at this number.\n"); 
               if (outbound) {
                  if (!outbound->cdr) 
                     outbound->cdr = ast_cdr_alloc();
                  if (outbound->cdr) {
                     char tmp[256];

                     ast_cdr_init(outbound->cdr, outbound);
                     snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
                     ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
                     ast_cdr_update(outbound);
                     ast_cdr_start(outbound->cdr);
                     ast_cdr_end(outbound->cdr);
                     /* If the cause wasn't handled properly */
                     if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
                        ast_cdr_failed(outbound->cdr);
                  } else {
                     ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
                     ast_hangup(outbound);
                     outbound = NULL;
                  }
               }
            }
         } else 
            ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));

         number = rest;
      } while (number);

      status = 0;
      if (!AST_LIST_EMPTY(findme_user_list))
         winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);

      while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
         if (!fmuser->cleared && fmuser->ochan != winner)
            clear_caller(fmuser);
         ast_free(fmuser);
      }

      fmuser = NULL;
      tmpuser = NULL;
      headuser = NULL;
      if (winner)
         break;

      if (!caller || ast_check_hangup(caller)) {
         tpargs->status = 1;
         ast_free(findme_user_list);
         return;
      }

      idx++;
      AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
         if (nm->order == idx)
            break;
      }
   }
   ast_free(findme_user_list);
   if (!winner) 
      tpargs->status = 1;
   else {
      tpargs->status = 100;
      tpargs->outbound = winner;
   }

   return;
}
static void free_numbers ( struct call_followme f) [static]

Definition at line 189 of file app_followme.c.

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, call_followme::blnumbers, call_followme::numbers, and call_followme::wlnumbers.

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

{
   /* Free numbers attached to the profile */
   struct number *prev;

   while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
      /* Free the number */
      ast_free(prev);
   AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);

   while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
      /* Free the blacklisted number */
      ast_free(prev);
   AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);

   while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
      /* Free the whitelisted number */
      ast_free(prev);
   AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
}
static void init_profile ( struct call_followme f) [static]

Definition at line 237 of file app_followme.c.

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

Referenced by reload_followme().

{
   f->active = 1;
   ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
}
static int load_module ( void  ) [static]
static void profile_set_param ( struct call_followme f,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Set parameter in profile from configuration file.

Definition at line 246 of file app_followme.c.

References ast_copy_string(), ast_log(), call_followme::callfromprompt, call_followme::context, LOG_WARNING, call_followme::moh, call_followme::name, call_followme::nextindp, call_followme::norecordingprompt, call_followme::optionsprompt, call_followme::plsholdprompt, call_followme::sorryprompt, call_followme::statusprompt, and call_followme::takecall.

Referenced by find_realtime(), and reload_followme().

{

   if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music")) 
      ast_copy_string(f->moh, val, sizeof(f->moh));
   else if (!strcasecmp(param, "context")) 
      ast_copy_string(f->context, val, sizeof(f->context));
   else if (!strcasecmp(param, "takecall"))
      ast_copy_string(f->takecall, val, sizeof(f->takecall));
   else if (!strcasecmp(param, "declinecall"))
      ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
   else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
      ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
   else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt")) 
      ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
   else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt")) 
      ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
   else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
      ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
   else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt")) 
      ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
   else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt")) 
      ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
   else if (failunknown) {
      if (linenum >= 0)
         ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
      else
         ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
   }
}
static int reload ( void  ) [static]

Definition at line 1192 of file app_followme.c.

References reload_followme().

{
   reload_followme(1);

   return 0;
}
static int reload_followme ( int  reload) [static]

Reload followme application module.

Definition at line 297 of file app_followme.c.

References call_followme::active, alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_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, call_followme::numbers, profile_set_param(), number::timeout, ast_variable::value, and var.

Referenced by load_module(), and reload().

{
   struct call_followme *f;
   struct ast_config *cfg;
   char *cat = NULL, *tmp;
   struct ast_variable *var;
   struct number *cur, *nm;
   char numberstr[90];
   int timeout;
   char *timeoutstr;
   int numorder;
   const char *takecallstr;
   const char *declinecallstr;
   const char *tmpstr;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

   if (!(cfg = ast_config_load("followme.conf", config_flags))) {
      ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n");
      return 0;
   }

   AST_RWLIST_WRLOCK(&followmes);

   /* Reset Global Var Values */
   featuredigittimeout = 5000;

   /* Mark all profiles as inactive for the moment */
   AST_RWLIST_TRAVERSE(&followmes, f, entry) {
      f->active = 0;
   }

   featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");

   if (!ast_strlen_zero(featuredigittostr)) {
      if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
         featuredigittimeout = 5000;
   }

   if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
      ast_copy_string(takecall, takecallstr, sizeof(takecall));
   }

   if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
      ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
   }

   if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
   }

   if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
   }


   if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
   }

   if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
   }

   if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
   }

   if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
   } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
      ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
   }

   /* Chug through config file */
   while ((cat = ast_category_browse(cfg, cat))) {
      int new = 0;

      if (!strcasecmp(cat, "general"))
         continue;

      /* Look for an existing one */
      AST_LIST_TRAVERSE(&followmes, f, entry) {
         if (!strcasecmp(f->name, cat))
            break;
      }

      ast_debug(1, "New profile %s.\n", cat);

      if (!f) {
         /* Make one then */
         f = alloc_profile(cat);
         new = 1;
      }

      /* Totally fail if we fail to find/create an entry */
      if (!f)
         continue;

      if (!new)
         ast_mutex_lock(&f->lock);
      /* Re-initialize the profile */
      init_profile(f);
      free_numbers(f);
      var = ast_variable_browse(cfg, cat);
      while (var) {
         if (!strcasecmp(var->name, "number")) {
            int idx = 0;

            /* Add a new number */
            ast_copy_string(numberstr, var->value, sizeof(numberstr));
            if ((tmp = strchr(numberstr, ','))) {
               *tmp++ = '\0';
               timeoutstr = ast_strdupa(tmp);
               if ((tmp = strchr(timeoutstr, ','))) {
                  *tmp++ = '\0';
                  numorder = atoi(tmp);
                  if (numorder < 0)
                     numorder = 0;
               } else 
                  numorder = 0;
               timeout = atoi(timeoutstr);
               if (timeout < 0) 
                  timeout = 25;
            } else {
               timeout = 25;
               numorder = 0;
            }

            if (!numorder) {
               idx = 1;
               AST_LIST_TRAVERSE(&f->numbers, nm, entry) 
                  idx++;
               numorder = idx;
            }
            cur = create_followme_number(numberstr, timeout, numorder);
            AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
         } else {
            profile_set_param(f, var->name, var->value, var->lineno, 1);
            ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
         }
         var = var->next;
      } /* End while(var) loop */

      if (!new) 
         ast_mutex_unlock(&f->lock);
      else
         AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
   }

   ast_config_destroy(cfg);

   AST_RWLIST_UNLOCK(&followmes);

   return 1;
}
static int unload_module ( void  ) [static]
static struct ast_channel* wait_for_winner ( struct findme_user_listptr findme_user_list,
struct number nm,
struct ast_channel caller,
char *  namerecloc,
int *  status,
struct fm_args tpargs 
) [static, read]

Definition at line 509 of file app_followme.c.

References AST_CAUSE_NORMAL_CLEARING, AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_HOLD, AST_CONTROL_OFFHOOK, AST_CONTROL_PROCEEDING, AST_CONTROL_PROGRESS, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_read(), ast_sched_runq(), ast_sched_wait(), ast_stopstream(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_verb, ast_waitfor_n(), fm_args::callfromprompt, clear_calling_tree(), ast_frame::data, findme_user::digts, f, ast_frame::frametype, ast_channel::hangupcause, ast_channel::language, LOG_NOTICE, LOG_WARNING, ast_channel::name, fm_args::nextindp, fm_args::norecordingprompt, findme_user::ochan, fm_args::optionsprompt, ast_channel::sched, findme_user::state, ast_channel::stream, ast_frame::subclass, fm_args::takecall, number::timeout, ast_channel::timingfunc, ast_frame::uint32, findme_user::yn, and findme_user::ynidx.

Referenced by findmeexec().

{
   struct ast_channel *watchers[256];
   int pos;
   struct ast_channel *winner;
   struct ast_frame *f;
   int ctstatus = 0;
   int dg;
   struct findme_user *tmpuser;
   int to = 0;
   int livechannels = 0;
   int tmpto;
   long totalwait = 0, wtd = 0, towas = 0;
   char *callfromname;
   char *pressbuttonname;

   /* ------------ wait_for_winner_channel start --------------- */ 

   callfromname = ast_strdupa(tpargs->callfromprompt);
   pressbuttonname = ast_strdupa(tpargs->optionsprompt);

   if (AST_LIST_EMPTY(findme_user_list)) {
      ast_verb(3, "couldn't reach at this number.\n");
      return NULL;
   }

   if (!caller) {
      ast_verb(3, "Original caller hungup. Cleanup.\n");
      clear_calling_tree(findme_user_list);
      return NULL;
   }

   totalwait = nm->timeout * 1000;

   while (!ctstatus) {
      to = 1000;
      pos = 1; 
      livechannels = 0;
      watchers[0] = caller;

      dg = 0;
      winner = NULL;
      AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
         if (tmpuser->state >= 0 && tmpuser->ochan) {
            if (tmpuser->state == 3) 
               tmpuser->digts += (towas - wtd);
            if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
               ast_verb(3, "We've been waiting for digits longer than we should have.\n");
               if (!ast_strlen_zero(namerecloc)) {
                  tmpuser->state = 1;
                  tmpuser->digts = 0;
                  if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
                     ast_sched_runq(tmpuser->ochan->sched);
                  } else {
                     ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
                     return NULL;
                  }
               } else {
                  tmpuser->state = 2;
                  tmpuser->digts = 0;
                  if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
                     ast_sched_runq(tmpuser->ochan->sched);
                  else {
                     ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
                     return NULL;
                  }
               }
            }
            if (tmpuser->ochan->stream) {
               ast_sched_runq(tmpuser->ochan->sched);
               tmpto = ast_sched_wait(tmpuser->ochan->sched);
               if (tmpto > 0 && tmpto < to)
                  to = tmpto;
               else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
                  ast_stopstream(tmpuser->ochan);
                  if (tmpuser->state == 1) {
                     ast_verb(3, "Playback of the call-from file appears to be done.\n");
                     if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
                        tmpuser->state = 2;
                     } else {
                        ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
                        memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
                        tmpuser->ynidx = 0;
                        if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
                           tmpuser->state = 3;
                        else {
                           ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
                           return NULL;
                        } 
                     }
                  } else if (tmpuser->state == 2) {
                     ast_verb(3, "Playback of name file appears to be done.\n");
                     memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
                     tmpuser->ynidx = 0;
                     if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
                        tmpuser->state = 3;
                     } else {
                        return NULL;
                     } 
                  } else if (tmpuser->state == 3) {
                     ast_verb(3, "Playback of the next step file appears to be done.\n");
                     tmpuser->digts = 0;
                  }
               }
            }
            watchers[pos++] = tmpuser->ochan;
            livechannels++;
         }
      }

      tmpto = to;
      if (to < 0) {
         to = 1000;
         tmpto = 1000;
      }
      towas = to;
      winner = ast_waitfor_n(watchers, pos, &to);
      tmpto -= to;
      totalwait -= tmpto;
      wtd = to;
      if (totalwait <= 0) {
         ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
         clear_calling_tree(findme_user_list);
         return NULL;
      }
      if (winner) {
         /* Need to find out which channel this is */
         dg = 0;
         while ((winner != watchers[dg]) && (dg < 256))
            dg++;
         AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
            if (tmpuser->ochan == winner)
               break;
         f = ast_read(winner);
         if (f) {
            if (f->frametype == AST_FRAME_CONTROL) {
               switch(f->subclass) {
               case AST_CONTROL_HANGUP:
                  ast_verb(3, "%s received a hangup frame.\n", winner->name);
                  if (f->data.uint32) {
                     winner->hangupcause = f->data.uint32;
                  }
                  if (dg == 0) {
                     ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
                     clear_calling_tree(findme_user_list);
                     ctstatus = -1;
                  }
                  break;
               case AST_CONTROL_ANSWER:
                  ast_verb(3, "%s answered %s\n", winner->name, caller->name);
                  /* If call has been answered, then the eventual hangup is likely to be normal hangup */ 
                  winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
                  caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
                  ast_verb(3, "Starting playback of %s\n", callfromname);
                  if (dg > 0) {
                     if (!ast_strlen_zero(namerecloc)) {
                        if (!ast_streamfile(winner, callfromname, winner->language)) {
                           ast_sched_runq(winner->sched);
                           tmpuser->state = 1;
                        } else {
                           ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
                           ast_frfree(f);
                           return NULL;
                        }
                     } else {
                        tmpuser->state = 2;
                        if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
                           ast_sched_runq(tmpuser->ochan->sched);
                        else {
                           ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
                           ast_frfree(f);
                           return NULL;
                        }
                     }
                  }
                  break;
               case AST_CONTROL_BUSY:
                  ast_verb(3, "%s is busy\n", winner->name);
                  break;
               case AST_CONTROL_CONGESTION:
                  ast_verb(3, "%s is circuit-busy\n", winner->name);
                  break;
               case AST_CONTROL_RINGING:
                  ast_verb(3, "%s is ringing\n", winner->name);
                  break;
               case AST_CONTROL_PROGRESS:
                  ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name);
                  break;
               case AST_CONTROL_VIDUPDATE:
                  ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name);
                  break;
               case AST_CONTROL_SRCUPDATE:
                  ast_verb(3, "%s requested a source update, passing it to %s\n", winner->name, caller->name);
                  break;
               case AST_CONTROL_PROCEEDING:
                  ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name);
                  break;
               case AST_CONTROL_HOLD:
                  ast_verb(3, "Call on %s placed on hold\n", winner->name);
                  break;
               case AST_CONTROL_UNHOLD:
                  ast_verb(3, "Call on %s left from hold\n", winner->name);
                  break;
               case AST_CONTROL_OFFHOOK:
               case AST_CONTROL_FLASH:
                  /* Ignore going off hook and flash */
                  break;
               case -1:
                  ast_verb(3, "%s stopped sounds\n", winner->name);
                  break;
               default:
                  ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
                  break;
               }
            } 
            if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
               if (winner->stream)
                  ast_stopstream(winner);
               tmpuser->digts = 0;
               ast_debug(1, "DTMF received: %c\n",(char) f->subclass);
               tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
               tmpuser->ynidx++;
               ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
               if (tmpuser->ynidx >= ynlongest) {
                  ast_debug(1, "reached longest possible match - doing evals\n");
                  if (!strcmp(tmpuser->yn, tpargs->takecall)) {
                     ast_debug(1, "Match to take the call!\n");
                     ast_frfree(f);
                     return tmpuser->ochan;
                  }
                  if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
                     ast_debug(1, "Next in dial plan step requested.\n");
                     *status = 1;
                     ast_frfree(f);
                     return NULL;
                  }

               }
            }

            ast_frfree(f);
         } else {
            if (winner) {
               ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);                   
               if (!dg) {
                  clear_calling_tree(findme_user_list);
                  return NULL;
               } else {
                  tmpuser->state = -1;
                  ast_hangup(winner);  
                  livechannels--;
                  ast_debug(1, "live channels left %d\n", livechannels);
                  if (!livechannels) {
                     ast_verb(3, "no live channels left. exiting.\n");
                     return NULL;
                  }
               }
            }
         }

      } else
         ast_debug(1, "timed out waiting for action\n");
   }

   /* --- WAIT FOR WINNER NUMBER END! -----------*/
   return NULL;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Find-Me/Follow-Me Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1203 of file app_followme.c.

char* app = "FollowMe" [static]

Definition at line 96 of file app_followme.c.

Definition at line 1203 of file app_followme.c.

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

Definition at line 178 of file app_followme.c.

const char* defaultmoh = "default" [static]

Default Music-On-Hold Class

Definition at line 175 of file app_followme.c.

int featuredigittimeout = 5000 [static]

Feature Digit Timeout

Definition at line 174 of file app_followme.c.

const char* featuredigittostr [static]

Definition at line 173 of file app_followme.c.

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

Definition at line 169 of file app_followme.c.

Referenced by app_exec().

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

Definition at line 177 of file app_followme.c.

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

Definition at line 179 of file app_followme.c.

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

Definition at line 180 of file app_followme.c.

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

Definition at line 181 of file app_followme.c.

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

Definition at line 183 of file app_followme.c.

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

Definition at line 182 of file app_followme.c.

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

Definition at line 177 of file app_followme.c.

int ynlongest = 0 [static]

Definition at line 171 of file app_followme.c.