Asterisk - The Open Source Telephony Project GIT-master-a358458
Data Structures | Macros | Enumerations | Functions | Variables
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"
#include "asterisk/stasis_channels.h"
#include "asterisk/max_forwards.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
 

Macros

#define MAX_YN_STRING   20
 
#define REC_FORMAT   "sln"
 

Enumerations

enum  {
  FOLLOWMEFLAG_STATUSMSG = (1 << 0) , FOLLOWMEFLAG_RECORDNAME = (1 << 1) , FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2) , FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3) ,
  FOLLOWMEFLAG_NOANSWER = (1 << 4) , FOLLOWMEFLAG_DISABLEOPTIMIZATION = (1 << 5) , FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 6) , FOLLOWMEFLAG_PREDIAL_CALLER = (1 << 7) ,
  FOLLOWMEFLAG_PREDIAL_CALLEE = (1 << 8)
}
 
enum  { FOLLOWMEFLAG_ARG_PREDIAL_CALLER , FOLLOWMEFLAG_ARG_PREDIAL_CALLEE , FOLLOWMEFLAG_ARG_ARRAY_SIZE }
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static struct call_followmealloc_profile (const char *fmname)
 Allocate and initialize followme profile. More...
 
static int app_exec (struct ast_channel *chan, const char *data)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static void clear_caller (struct findme_user *tmpuser)
 
static void clear_unanswered_calls (struct findme_user_listptr *findme_user_list)
 
static struct numbercreate_followme_number (const char *number, int timeout, int numorder)
 Add a new number. More...
 
static void destroy_calling_node (struct findme_user *node)
 
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 struct ast_channelfindmeexec (struct fm_args *tpargs, struct ast_channel *caller)
 
static void free_numbers (struct call_followme *f)
 
static void init_profile (struct call_followme *f, int activate)
 
static int load_module (void)
 Load the module. More...
 
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. More...
 
static void publish_dial_end_event (struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status)
 
static int reload (void)
 
static int reload_followme (int reload)
 Reload followme application module. More...
 
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, 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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, }
 
static char * app = "FollowMe"
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static char callfromprompt [PATH_MAX] = "followme/call-from"
 
static char connprompt [PATH_MAX] = ""
 
static const char * defaultmoh = "default"
 
static int enable_callee_prompt = 1
 
static int featuredigittimeout = 5000
 
static const char * featuredigittostr
 
static const struct ast_app_option followme_opts [128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'B' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLER , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLER + 1 }, [ 'b' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLEE , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLEE + 1 }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'l' ] = { .flag = FOLLOWMEFLAG_DISABLEOPTIMIZATION }, [ 'N' ] = { .flag = FOLLOWMEFLAG_NOANSWER }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG }, }
 
static struct followmes followmes = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 
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 bwesc.nosp@m.hke@.nosp@m.btwte.nosp@m.ch.c.nosp@m.om

Definition in file app_followme.c.

Macro Definition Documentation

◆ MAX_YN_STRING

#define MAX_YN_STRING   20

Maximum accept/decline DTMF string plus terminator.

Definition at line 151 of file app_followme.c.

◆ REC_FORMAT

#define REC_FORMAT   "sln"

Definition at line 67 of file app_followme.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
FOLLOWMEFLAG_STATUSMSG 
FOLLOWMEFLAG_RECORDNAME 
FOLLOWMEFLAG_UNREACHABLEMSG 
FOLLOWMEFLAG_DISABLEHOLDPROMPT 
FOLLOWMEFLAG_NOANSWER 
FOLLOWMEFLAG_DISABLEOPTIMIZATION 
FOLLOWMEFLAG_IGNORE_CONNECTEDLINE 
FOLLOWMEFLAG_PREDIAL_CALLER 
FOLLOWMEFLAG_PREDIAL_CALLEE 

Definition at line 237 of file app_followme.c.

237 {
238 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
239 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
242 FOLLOWMEFLAG_NOANSWER = (1 << 4),
247};
@ FOLLOWMEFLAG_UNREACHABLEMSG
Definition: app_followme.c:240
@ FOLLOWMEFLAG_DISABLEHOLDPROMPT
Definition: app_followme.c:241
@ FOLLOWMEFLAG_NOANSWER
Definition: app_followme.c:242
@ FOLLOWMEFLAG_PREDIAL_CALLER
Definition: app_followme.c:245
@ FOLLOWMEFLAG_IGNORE_CONNECTEDLINE
Definition: app_followme.c:244
@ FOLLOWMEFLAG_RECORDNAME
Definition: app_followme.c:239
@ FOLLOWMEFLAG_DISABLEOPTIMIZATION
Definition: app_followme.c:243
@ FOLLOWMEFLAG_PREDIAL_CALLEE
Definition: app_followme.c:246
@ FOLLOWMEFLAG_STATUSMSG
Definition: app_followme.c:238

◆ anonymous enum

anonymous enum
Enumerator
FOLLOWMEFLAG_ARG_PREDIAL_CALLER 
FOLLOWMEFLAG_ARG_PREDIAL_CALLEE 
FOLLOWMEFLAG_ARG_ARRAY_SIZE 

Definition at line 249 of file app_followme.c.

249 {
252
253 /* note: this entry _MUST_ be the last one in the enum */
255};
@ FOLLOWMEFLAG_ARG_ARRAY_SIZE
Definition: app_followme.c:254
@ FOLLOWMEFLAG_ARG_PREDIAL_CALLER
Definition: app_followme.c:250
@ FOLLOWMEFLAG_ARG_PREDIAL_CALLEE
Definition: app_followme.c:251

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1657 of file app_followme.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1657 of file app_followme.c.

◆ alloc_profile()

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

Allocate and initialize followme profile.

Definition at line 311 of file app_followme.c.

312{
313 struct call_followme *f;
314
315 if (!(f = ast_calloc(1, sizeof(*f))))
316 return NULL;
317
318 ast_mutex_init(&f->lock);
319 ast_copy_string(f->name, fmname, sizeof(f->name));
323 return f;
324}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define NULL
Definition: resample.c:96
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Data structure for followme scripts.
Definition: app_followme.c:162
struct call_followme::wlnumbers wlnumbers
char name[AST_MAX_EXTENSION]
Definition: app_followme.c:164
struct call_followme::blnumbers blnumbers
struct call_followme::numbers numbers
ast_mutex_t lock
Definition: app_followme.c:163

References ast_calloc, ast_copy_string(), AST_LIST_HEAD_INIT_NOLOCK, ast_mutex_init, call_followme::blnumbers, call_followme::lock, call_followme::name, NULL, call_followme::numbers, and call_followme::wlnumbers.

Referenced by find_realtime(), and reload_followme().

◆ app_exec()

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

Definition at line 1343 of file app_followme.c.

1344{
1345 struct fm_args *targs;
1347 struct call_followme *f;
1348 struct number *nm, *newnm;
1349 int res = 0;
1350 char *argstr;
1351 struct ast_channel *caller;
1352 struct ast_channel *outbound;
1354 AST_APP_ARG(followmeid);
1356 );
1357 char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
1358 int max_forwards;
1359
1360 if (ast_strlen_zero(data)) {
1361 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1362 return -1;
1363 }
1364
1365 ast_channel_lock(chan);
1367 ast_channel_unlock(chan);
1368
1369 if (max_forwards <= 0) {
1370 ast_log(LOG_WARNING, "Unable to execute FollowMe on channel %s. Max forwards exceeded\n",
1371 ast_channel_name(chan));
1372 return -1;
1373 }
1374
1375 argstr = ast_strdupa((char *) data);
1376
1377 AST_STANDARD_APP_ARGS(args, argstr);
1378
1379 if (ast_strlen_zero(args.followmeid)) {
1380 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1381 return -1;
1382 }
1383
1384 targs = ast_calloc(1, sizeof(*targs));
1385 if (!targs) {
1386 return -1;
1387 }
1388
1391 if (!strcasecmp(f->name, args.followmeid) && (f->active))
1392 break;
1393 }
1395
1396 ast_debug(1, "New profile %s.\n", args.followmeid);
1397
1398 if (!f) {
1399 f = find_realtime(args.followmeid);
1400 }
1401
1402 if (!f) {
1403 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
1404 ast_free(targs);
1405 return 0;
1406 }
1407
1408 /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
1409 if (args.options) {
1410 ast_app_parse_options(followme_opts, &targs->followmeflags, opt_args, args.options);
1411 }
1412
1413 /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
1414 ast_mutex_lock(&f->lock);
1416 targs->mohclass = ast_strdupa(f->moh);
1417 ast_copy_string(targs->context, f->context, sizeof(targs->context));
1418 ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
1419 ast_copy_string(targs->nextindp, f->nextindp, sizeof(targs->nextindp));
1420 ast_copy_string(targs->callfromprompt, f->callfromprompt, sizeof(targs->callfromprompt));
1422 ast_copy_string(targs->optionsprompt, f->optionsprompt, sizeof(targs->optionsprompt));
1423 ast_copy_string(targs->plsholdprompt, f->plsholdprompt, sizeof(targs->plsholdprompt));
1424 ast_copy_string(targs->statusprompt, f->statusprompt, sizeof(targs->statusprompt));
1425 ast_copy_string(targs->sorryprompt, f->sorryprompt, sizeof(targs->sorryprompt));
1426 ast_copy_string(targs->connprompt, f->connprompt, sizeof(targs->connprompt));
1427 /* Copy the numbers we're going to use into another list in case the master list should get modified
1428 (and locked) while we're trying to do a follow-me */
1430 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
1431 newnm = create_followme_number(nm->number, nm->timeout, nm->order);
1432 if (newnm) {
1433 AST_LIST_INSERT_TAIL(&targs->cnumbers, newnm, entry);
1434 }
1435 }
1437
1438 /* PREDIAL: Preprocess any callee gosub arguments. */
1442 targs->predial_callee =
1444 }
1445
1446 /* PREDIAL: Run gosub on the caller's channel */
1451 }
1452
1453 /* Forget the 'N' option if the call is already up. */
1454 if (ast_channel_state(chan) == AST_STATE_UP) {
1456 }
1457
1460 } else {
1461 /* Answer the call */
1462 if (ast_channel_state(chan) != AST_STATE_UP) {
1463 ast_answer(chan);
1464 }
1465
1467 ast_stream_and_wait(chan, targs->statusprompt, "");
1468 }
1469
1471 int duration = 5;
1472
1473 snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
1475 if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, REC_FORMAT, &duration,
1477 goto outrun;
1478 }
1479 if (!ast_fileexists(targs->namerecloc, NULL, ast_channel_language(chan))) {
1480 targs->namerecloc[0] = '\0';
1481 }
1482 }
1483
1485 if (ast_streamfile(chan, targs->plsholdprompt, ast_channel_language(chan))) {
1486 goto outrun;
1487 }
1488 if (ast_waitstream(chan, "") < 0)
1489 goto outrun;
1490 }
1491 ast_moh_start(chan, targs->mohclass, NULL);
1492 }
1493
1494 ast_channel_lock(chan);
1496 ast_channel_unlock(chan);
1497
1498 outbound = findmeexec(targs, chan);
1499 if (!outbound) {
1501 if (ast_channel_state(chan) != AST_STATE_UP) {
1502 ast_answer(chan);
1503 }
1504 } else {
1505 ast_moh_stop(chan);
1506 }
1507
1509 ast_stream_and_wait(chan, targs->sorryprompt, "");
1510 }
1511 res = 0;
1512 } else {
1513 caller = chan;
1514
1515 /* Play "connecting" message to the winner, if configured. */
1516 if (!ast_strlen_zero(targs->connprompt)) {
1518 ast_stream_and_wait(outbound, targs->connprompt, "");
1520 }
1521
1522 /* Bridge the two channels. */
1523
1524 memset(&config, 0, sizeof(config));
1525 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1526 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
1527 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
1528 config.end_bridge_callback = end_bridge_callback;
1529 config.end_bridge_callback_data = chan;
1530 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
1531
1532 /* Update connected line to caller if available. */
1533 if (targs->pending_out_connected_update) {
1534 if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0)) {
1536 }
1537 }
1538
1542 }
1543 } else {
1545 }
1546
1547 /* Be sure no generators are left on it */
1549 /* Make sure channels are compatible */
1550 res = ast_channel_make_compatible(caller, outbound);
1551 if (res < 0) {
1552 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(caller), ast_channel_name(outbound));
1554 goto outrun;
1555 }
1556
1557 /* Update connected line to winner if changed. */
1558 if (targs->pending_in_connected_update) {
1559 if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0)) {
1561 }
1562 }
1563
1564 /* Put winner on hold if caller requested. */
1565 if (targs->pending_hold) {
1566 if (ast_strlen_zero(targs->suggested_moh)) {
1568 } else {
1570 targs->suggested_moh, strlen(targs->suggested_moh) + 1);
1571 }
1572 }
1573
1574 res = ast_bridge_call(caller, outbound, &config);
1575 }
1576
1577outrun:
1578 while ((nm = AST_LIST_REMOVE_HEAD(&targs->cnumbers, entry))) {
1579 ast_free(nm);
1580 }
1581 if (!ast_strlen_zero(targs->namerecloc)) {
1582 int ret;
1583 char fn[PATH_MAX + sizeof(REC_FORMAT)];
1584
1585 snprintf(fn, sizeof(fn), "%s.%s", targs->namerecloc,
1586 REC_FORMAT);
1587 ret = unlink(fn);
1588 if (ret != 0) {
1589 ast_log(LOG_NOTICE, "Failed to delete recorded name file %s: %d (%s)\n",
1590 fn, errno, strerror(errno));
1591 } else {
1592 ast_debug(2, "deleted recorded prompt %s.\n", fn);
1593 }
1594 }
1595 ast_free((char *) targs->predial_callee);
1598 ast_free(targs);
1599
1600 if (f->realtime) {
1601 /* Not in list */
1602 free_numbers(f);
1603 ast_free(f);
1604 }
1605
1606 return res;
1607}
#define REC_FORMAT
Definition: app_followme.c:67
static struct ast_channel * findmeexec(struct fm_args *tpargs, struct ast_channel *caller)
static const struct ast_app_option followme_opts[128]
Definition: app_followme.c:267
static char * app
Definition: app_followme.c:148
static struct number * create_followme_number(const char *number, int timeout, int numorder)
Add a new number.
Definition: app_followme.c:384
static struct call_followme * find_realtime(const char *name)
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 void free_numbers(struct call_followme *f)
Definition: app_followme.c:288
#define PATH_MAX
Definition: asterisk.h:40
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static const char config[]
Definition: chan_ooh323.c:111
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *connected_info, int frame)
Run a connected line interception subroutine and update a channel's connected line information.
Definition: channel.c:10338
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2072
#define ast_channel_lock(chan)
Definition: channel.h:2922
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition: channel.c:6720
@ AST_FEATURE_REDIRECT
Definition: channel.h:1064
@ AST_FEATURE_AUTOMON
Definition: channel.h:1067
const char * ast_channel_uniqueid(const struct ast_channel *chan)
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2893
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
Definition: channel.c:9093
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition: channel.c:4653
const char * ast_channel_language(const struct ast_channel *chan)
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition: channel.c:8293
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
Put chan into autoservice while hanging up peer.
Definition: autoservice.c:342
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
#define ast_channel_unlock(chan)
Definition: channel.h:2923
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
@ THRESHOLD_SILENCE
Definition: dsp.h:73
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
Bridge a call, optionally allowing redirection.
Definition: features.c:685
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
const char * ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
Add missing context/exten to subroutine argument string.
Definition: main/app.c:278
#define AST_APP_ARG(name)
Define an application argument.
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path)
Record a file based on input from a channel. Use default accept and cancel DTMF. This function will p...
Definition: main/app.c:2144
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Definition: main/app.c:297
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
@ AST_CONTROL_RINGING
@ AST_CONTROL_HOLD
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_NOTICE
#define LOG_WARNING
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
int errno
int ast_max_forwards_get(struct ast_channel *chan)
Get the current max forwards for a particular channel.
Definition: max_forwards.c:121
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7766
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
bridge configuration
Definition: channel.h:1076
Main Channel structure associated with a channel.
const char * data
struct ast_party_caller caller
Channel Caller ID information.
char connprompt[PATH_MAX]
Definition: app_followme.c:179
unsigned int enable_callee_prompt
Definition: app_followme.c:170
char sorryprompt[PATH_MAX]
Definition: app_followme.c:178
char callfromprompt[PATH_MAX]
Definition: app_followme.c:173
char takecall[MAX_YN_STRING]
Definition: app_followme.c:171
char nextindp[MAX_YN_STRING]
Definition: app_followme.c:172
char optionsprompt[PATH_MAX]
Definition: app_followme.c:175
char statusprompt[PATH_MAX]
Definition: app_followme.c:177
char plsholdprompt[PATH_MAX]
Definition: app_followme.c:176
char moh[MAX_MUSICCLASS]
Definition: app_followme.c:165
char context[AST_MAX_CONTEXT]
Definition: app_followme.c:166
char norecordingprompt[PATH_MAX]
Definition: app_followme.c:174
unsigned int active
Definition: app_followme.c:167
Definition: search.h:40
char connprompt[PATH_MAX]
Definition: app_followme.c:216
unsigned int pending_in_connected_update
Definition: app_followme.c:197
char namerecloc[PATH_MAX]
Definition: app_followme.c:207
struct fm_args::cnumbers cnumbers
unsigned int enable_callee_prompt
Definition: app_followme.c:203
char sorryprompt[PATH_MAX]
Definition: app_followme.c:215
char callfromprompt[PATH_MAX]
Definition: app_followme.c:210
char takecall[MAX_YN_STRING]
Definition: app_followme.c:208
unsigned int pending_hold
Definition: app_followme.c:201
struct ast_party_connected_line connected_out
Definition: app_followme.c:195
unsigned int pending_out_connected_update
Definition: app_followme.c:199
char suggested_moh[MAX_MUSICCLASS]
Definition: app_followme.c:205
char nextindp[MAX_YN_STRING]
Definition: app_followme.c:209
char * mohclass
Definition: app_followme.c:188
char optionsprompt[PATH_MAX]
Definition: app_followme.c:212
char statusprompt[PATH_MAX]
Definition: app_followme.c:214
char plsholdprompt[PATH_MAX]
Definition: app_followme.c:213
struct ast_flags followmeflags
Definition: app_followme.c:217
char context[AST_MAX_CONTEXT]
Definition: app_followme.c:206
char norecordingprompt[PATH_MAX]
Definition: app_followme.c:211
const char * predial_callee
Definition: app_followme.c:191
struct ast_party_connected_line connected_in
Definition: app_followme.c:193
Channel datastore data for max forwards.
Definition: max_forwards.c:29
Number structure.
Definition: app_followme.c:154
long timeout
Definition: app_followme.c:156
char number[512]
Definition: app_followme.c:155
int order
Definition: app_followme.c:157
const char * args
static struct test_options options
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define ast_set_flag(p, flag)
Definition: utils.h:70
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343

References call_followme::active, app, args, ast_answer(), AST_APP_ARG, ast_app_exec_sub(), ast_app_expand_sub_args(), ast_app_parse_options(), ast_autoservice_chan_hangup_peer(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_channel_caller(), ast_channel_connected_line_sub(), ast_channel_language(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_update_connected_line(), ast_clear_flag, ast_config_AST_SPOOL_DIR, ast_connected_line_copy_from_caller(), AST_CONTROL_HOLD, AST_CONTROL_RINGING, 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_indicate(), ast_indicate_data(), AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, ast_log, ast_max_forwards_get(), ast_moh_start(), ast_moh_stop(), ast_mutex_lock, ast_mutex_unlock, ast_party_connected_line_free(), ast_play_and_record(), ast_replace_subargument_delimiter(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, ast_set_flag, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_strdupa, ast_stream_and_wait(), ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitstream(), ast_channel::caller, call_followme::callfromprompt, fm_args::callfromprompt, fm_args::cnumbers, config, fm_args::connected_in, fm_args::connected_out, call_followme::connprompt, fm_args::connprompt, call_followme::context, fm_args::context, create_followme_number(), ast_channel::data, call_followme::enable_callee_prompt, fm_args::enable_callee_prompt, end_bridge_callback(), end_bridge_callback_data_fixup(), errno, find_realtime(), findmeexec(), followme_opts, FOLLOWMEFLAG_ARG_ARRAY_SIZE, FOLLOWMEFLAG_ARG_PREDIAL_CALLEE, FOLLOWMEFLAG_ARG_PREDIAL_CALLER, FOLLOWMEFLAG_DISABLEHOLDPROMPT, FOLLOWMEFLAG_NOANSWER, FOLLOWMEFLAG_PREDIAL_CALLEE, FOLLOWMEFLAG_PREDIAL_CALLER, FOLLOWMEFLAG_RECORDNAME, FOLLOWMEFLAG_STATUSMSG, FOLLOWMEFLAG_UNREACHABLEMSG, fm_args::followmeflags, free_numbers(), call_followme::lock, LOG_NOTICE, LOG_WARNING, call_followme::moh, fm_args::mohclass, call_followme::name, fm_args::namerecloc, call_followme::nextindp, fm_args::nextindp, call_followme::norecordingprompt, fm_args::norecordingprompt, NULL, number::number, call_followme::numbers, options, call_followme::optionsprompt, fm_args::optionsprompt, number::order, PATH_MAX, fm_args::pending_hold, fm_args::pending_in_connected_update, fm_args::pending_out_connected_update, call_followme::plsholdprompt, fm_args::plsholdprompt, fm_args::predial_callee, call_followme::realtime, REC_FORMAT, call_followme::sorryprompt, fm_args::sorryprompt, call_followme::statusprompt, fm_args::statusprompt, fm_args::suggested_moh, call_followme::takecall, fm_args::takecall, THRESHOLD_SILENCE, and number::timeout.

Referenced by load_module().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1657 of file app_followme.c.

◆ clear_caller()

static void clear_caller ( struct findme_user tmpuser)
static

Definition at line 603 of file app_followme.c.

604{
605 struct ast_channel *outbound;
606
607 if (!tmpuser->ochan) {
608 /* Call already cleared. */
609 return;
610 }
611
612 outbound = tmpuser->ochan;
613 ast_hangup(outbound);
614 tmpuser->ochan = NULL;
615}
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
struct ast_channel * ochan
Definition: app_followme.c:221

References ast_hangup(), NULL, and findme_user::ochan.

Referenced by clear_unanswered_calls(), destroy_calling_node(), and wait_for_winner().

◆ clear_unanswered_calls()

static void clear_unanswered_calls ( struct findme_user_listptr findme_user_list)
static

Definition at line 617 of file app_followme.c.

618{
619 struct findme_user *tmpuser;
620
621 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
622 if (!tmpuser->answered) {
623 clear_caller(tmpuser);
624 }
625 }
626}
static void clear_caller(struct findme_user *tmpuser)
Definition: app_followme.c:603
unsigned int answered
Definition: app_followme.c:231

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

Referenced by wait_for_winner().

◆ create_followme_number()

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

Add a new number.

Definition at line 384 of file app_followme.c.

385{
386 struct number *cur;
387 char *buf = ast_strdupa(number);
388 char *tmp;
389
390 if (!(cur = ast_calloc(1, sizeof(*cur))))
391 return NULL;
392
393 cur->timeout = timeout;
394 if ((tmp = strchr(buf, ',')))
395 *tmp = '\0';
396 ast_copy_string(cur->number, buf, sizeof(cur->number));
397 cur->order = numorder;
398 ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
399
400 return cur;
401}
static int tmp()
Definition: bt_open.c:389
char buf[BUFSIZE]
Definition: eagi_proxy.c:66

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

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

◆ destroy_calling_node()

static void destroy_calling_node ( struct findme_user node)
static

Definition at line 628 of file app_followme.c.

629{
632 ast_free(node);
633}
Definition: test_heap.c:38

References ast_free, ast_party_connected_line_free(), and clear_caller().

Referenced by destroy_calling_tree(), and findmeexec().

◆ destroy_calling_tree()

static void destroy_calling_tree ( struct findme_user_listptr findme_user_list)
static

Definition at line 635 of file app_followme.c.

636{
637 struct findme_user *fmuser;
638
639 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
640 destroy_calling_node(fmuser);
641 }
642}
static void destroy_calling_node(struct findme_user *node)
Definition: app_followme.c:628

References AST_LIST_REMOVE_HEAD, and destroy_calling_node().

Referenced by findmeexec().

◆ end_bridge_callback()

static void end_bridge_callback ( void *  data)
static

Definition at line 1322 of file app_followme.c.

1323{
1324 char buf[80];
1325 time_t end;
1326 struct ast_channel *chan = data;
1327
1328 time(&end);
1329
1330 ast_channel_lock(chan);
1331 snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
1332 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
1333 snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
1334 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
1335 ast_channel_unlock(chan);
1336}
int ast_channel_get_up_time(struct ast_channel *chan)
Obtain how long it has been since the channel was answered.
Definition: channel.c:2845
int ast_channel_get_duration(struct ast_channel *chan)
Obtain how long the channel since the channel was created.
Definition: channel.c:2830
char * end
Definition: eagi_proxy.c:73
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.

References ast_channel_get_duration(), ast_channel_get_up_time(), ast_channel_lock, ast_channel_unlock, buf, ast_channel::data, end, and pbx_builtin_setvar_helper().

Referenced by app_exec().

◆ end_bridge_callback_data_fixup()

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

Definition at line 1338 of file app_followme.c.

1339{
1340 bconfig->end_bridge_callback_data = originator;
1341}
void * end_bridge_callback_data
Definition: channel.h:1091

References ast_bridge_config::end_bridge_callback_data.

Referenced by app_exec().

◆ find_realtime()

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

Definition at line 1240 of file app_followme.c.

1241{
1242 struct ast_variable *var;
1243 struct ast_variable *v;
1244 struct ast_config *cfg;
1245 const char *catg;
1246 struct call_followme *new_follower;
1247 struct ast_str *str;
1248
1249 str = ast_str_create(16);
1250 if (!str) {
1251 return NULL;
1252 }
1253
1254 var = ast_load_realtime("followme", "name", name, SENTINEL);
1255 if (!var) {
1256 ast_free(str);
1257 return NULL;
1258 }
1259
1260 if (!(new_follower = alloc_profile(name))) {
1262 ast_free(str);
1263 return NULL;
1264 }
1265 init_profile(new_follower, 0);
1266
1267 for (v = var; v; v = v->next) {
1268 if (!strcasecmp(v->name, "active")) {
1269 if (ast_false(v->value)) {
1270 ast_mutex_destroy(&new_follower->lock);
1271 ast_free(new_follower);
1273 ast_free(str);
1274 return NULL;
1275 }
1276 } else {
1277 profile_set_param(new_follower, v->name, v->value, 0, 0);
1278 }
1279 }
1280
1282 new_follower->realtime = 1;
1283
1284 /* Load numbers */
1285 cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
1286 name, SENTINEL);
1287 if (!cfg) {
1288 ast_mutex_destroy(&new_follower->lock);
1289 ast_free(new_follower);
1290 ast_free(str);
1291 return NULL;
1292 }
1293
1294 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
1295 const char *numstr;
1296 const char *timeoutstr;
1297 const char *ordstr;
1298 int timeout;
1299 struct number *cur;
1300
1301 if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
1302 continue;
1303 }
1304 if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
1305 || sscanf(timeoutstr, "%30d", &timeout) != 1
1306 || timeout < 1) {
1307 timeout = 25;
1308 }
1309 /* This one has to exist; it was part of the query */
1310 ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
1311 ast_str_set(&str, 0, "%s", numstr);
1312 if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
1313 AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
1314 }
1315 }
1316 ast_config_destroy(cfg);
1317
1318 ast_free(str);
1319 return new_follower;
1320}
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.
Definition: app_followme.c:348
static struct call_followme * alloc_profile(const char *fmname)
Allocate and initialize followme profile.
Definition: app_followme.c:311
static void init_profile(struct call_followme *f, int activate)
Definition: app_followme.c:326
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:605
#define SENTINEL
Definition: compiler.h:87
static const char name[]
Definition: format_mp3.c:68
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3506
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_mutex_destroy(a)
Definition: lock.h:188
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next

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(), init_profile(), call_followme::lock, name, ast_variable::name, ast_variable::next, NULL, call_followme::numbers, profile_set_param(), call_followme::realtime, SENTINEL, str, number::timeout, ast_variable::value, and var.

Referenced by app_exec().

◆ findmeexec()

static struct ast_channel * findmeexec ( struct fm_args tpargs,
struct ast_channel caller 
)
static

Definition at line 1029 of file app_followme.c.

1030{
1031 struct number *nm;
1032 struct ast_channel *winner = NULL;
1033 char num[512];
1034 int dg, idx;
1035 char *rest, *number;
1036 struct findme_user *tmpuser;
1037 struct findme_user *fmuser;
1038 struct findme_user_listptr findme_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
1040
1041 for (idx = 1; !ast_check_hangup(caller); ++idx) {
1042 /* Find next followme numbers to dial. */
1043 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
1044 if (nm->order == idx) {
1045 break;
1046 }
1047 }
1048 if (!nm) {
1049 ast_verb(3, "No more steps left.\n");
1050 break;
1051 }
1052
1053 ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
1054
1055 /*
1056 * Put all active outgoing channels into autoservice.
1057 *
1058 * This needs to be done because ast_exists_extension() may put
1059 * the caller into autoservice.
1060 */
1061 AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
1062 if (tmpuser->ochan) {
1063 ast_autoservice_start(tmpuser->ochan);
1064 }
1065 }
1066
1067 /* Create all new outgoing calls */
1068 ast_copy_string(num, nm->number, sizeof(num));
1069 for (number = num; number; number = rest) {
1070 struct ast_channel *outbound;
1071 struct ast_format_cap *caps;
1072
1073 rest = strchr(number, '&');
1074 if (rest) {
1075 *rest++ = 0;
1076 }
1077
1078 /* We check if the extension exists, before creating the ast_channel struct */
1079 if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(ast_channel_caller(caller)->id.number.valid, ast_channel_caller(caller)->id.number.str, NULL))) {
1080 ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
1081 continue;
1082 }
1083
1084 tmpuser = ast_calloc(1, sizeof(*tmpuser));
1085 if (!tmpuser) {
1086 continue;
1087 }
1088
1089 if (ast_strlen_zero(tpargs->context)) {
1090 snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s%s",
1091 number,
1093 ? "/n" : "/m");
1094 } else {
1095 snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s@%s%s",
1096 number, tpargs->context,
1098 ? "/n" : "/m");
1099 }
1100
1101 /* Capture nativeformats reference in case it gets changed */
1102 ast_channel_lock(caller);
1103 caps = ao2_bump(ast_channel_nativeformats(caller));
1104 ast_channel_unlock(caller);
1105
1106 outbound = ast_request("Local", caps, NULL, caller, tmpuser->dialarg, &dg);
1107
1108 ao2_cleanup(caps);
1109
1110 if (!outbound) {
1111 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
1112 tmpuser->dialarg, ast_cause2str(dg));
1113 ast_free(tmpuser);
1114 continue;
1115 }
1116
1117 ast_channel_lock_both(caller, outbound);
1119 ast_channel_inherit_variables(caller, outbound);
1120 ast_channel_datastore_inherit(caller, outbound);
1122 ast_channel_language_set(outbound, ast_channel_language(caller));
1124 ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
1125 ast_channel_unlock(outbound);
1126 ast_channel_unlock(caller);
1127
1128 tmpuser->ochan = outbound;
1129 tmpuser->state = 0;
1130 AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
1131 }
1132
1133 /*
1134 * PREDIAL: Run gosub on all of the new callee channels
1135 *
1136 * We run the callee predial before ast_call() in case the user
1137 * wishes to do something on the newly created channels before
1138 * the channel does anything important.
1139 */
1140 if (tpargs->predial_callee && !AST_LIST_EMPTY(&new_user_list)) {
1141 /* Put caller into autoservice. */
1142 ast_autoservice_start(caller);
1143
1144 /* Run predial on all new outgoing calls. */
1145 AST_LIST_TRAVERSE(&new_user_list, tmpuser, entry) {
1146 ast_pre_call(tmpuser->ochan, tpargs->predial_callee);
1147 }
1148
1149 /* Take caller out of autoservice. */
1150 if (ast_autoservice_stop(caller)) {
1151 /*
1152 * Caller hungup.
1153 *
1154 * Destoy all new outgoing calls.
1155 */
1156 while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
1157 destroy_calling_node(tmpuser);
1158 }
1159
1160 /* Take all active outgoing channels out of autoservice. */
1161 AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
1162 if (tmpuser->ochan) {
1163 ast_autoservice_stop(tmpuser->ochan);
1164 }
1165 }
1166 break;
1167 }
1168 }
1169
1170 /* Start all new outgoing calls */
1171 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
1172 ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
1173 if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
1174 ast_verb(3, "couldn't reach at this number.\n");
1176
1177 /* Destroy this failed new outgoing call. */
1178 destroy_calling_node(tmpuser);
1179 continue;
1180 }
1181
1182 ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL);
1183 }
1185
1186 /* Take all active outgoing channels out of autoservice. */
1187 AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
1188 if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
1189 /* Existing outgoing call hungup. */
1191 destroy_calling_node(tmpuser);
1192 }
1193 }
1195
1196 if (AST_LIST_EMPTY(&new_user_list)) {
1197 /* No new channels remain at this order level. If there were any at all. */
1198 continue;
1199 }
1200
1201 /* Add new outgoing channels to the findme list. */
1202 AST_LIST_APPEND_LIST(&findme_user_list, &new_user_list, entry);
1203
1204 winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
1205 if (!winner) {
1206 /* Remove all dead outgoing nodes. */
1207 AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
1208 if (!tmpuser->ochan) {
1210 destroy_calling_node(tmpuser);
1211 }
1212 }
1214 continue;
1215 }
1216
1217 /* Destroy losing calls up to the winner. The rest will be destroyed later. */
1218 while ((fmuser = AST_LIST_REMOVE_HEAD(&findme_user_list, entry))) {
1219 if (fmuser->ochan == winner) {
1220 /*
1221 * Pass any connected line info up.
1222 *
1223 * NOTE: This code must be in line with destroy_calling_node().
1224 */
1225 tpargs->connected_out = fmuser->connected;
1227 ast_free(fmuser);
1228 break;
1229 } else {
1230 /* Destroy losing call. */
1231 destroy_calling_node(fmuser);
1232 }
1233 }
1234 break;
1235 }
1236 destroy_calling_tree(&findme_user_list);
1237 return winner;
1238}
static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
Definition: app_followme.c:635
static struct ast_channel * wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, struct fm_args *tpargs)
Definition: app_followme.c:644
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6461
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition: channel.h:1477
const char * ast_channel_musicclass(const struct ast_channel *chan)
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2929
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
Definition: channel.c:2368
void ast_channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Definition: channel.c:6434
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
Definition: channel.c:6771
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
const char * ast_cause2str(int cause) attribute_pure
Gives the string form of a given cause code.
Definition: channel.c:612
int ast_pre_call(struct ast_channel *chan, const char *sub_args)
Execute a Gosub call on the channel before a call is placed.
Definition: channel.c:6444
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6354
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer, const char *dialstring, const char *dialstatus)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
#define LOG_ERROR
#define ast_verb(level,...)
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
Definition: linkedlists.h:252
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
Definition: linkedlists.h:783
int ast_max_forwards_decrement(struct ast_channel *chan)
Decrement the max forwards count for a particular channel.
Definition: max_forwards.c:135
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
char dialarg[768]
Definition: app_followme.c:227
struct ast_party_connected_line connected
Definition: app_followme.c:223
unsigned int pending_connected_update
Definition: app_followme.c:233

References ao2_bump, ao2_cleanup, ast_autoservice_start(), ast_autoservice_stop(), ast_call(), ast_calloc, ast_cause2str(), ast_channel_caller(), ast_channel_connected(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_language(), ast_channel_lock, ast_channel_lock_both, ast_channel_musicclass(), ast_channel_nativeformats(), ast_channel_publish_dial(), ast_channel_req_accountcodes(), AST_CHANNEL_REQUESTOR_BRIDGE_PEER, ast_channel_unlock, ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, ast_exists_extension(), ast_free, AST_LIST_APPEND_LIST, AST_LIST_EMPTY, AST_LIST_HEAD_NOLOCK_INIT_VALUE, AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_CURRENT, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log, ast_max_forwards_decrement(), ast_pre_call(), ast_request(), ast_strlen_zero(), ast_test_flag, ast_verb, fm_args::cnumbers, findme_user::connected, fm_args::connected_out, fm_args::context, destroy_calling_node(), destroy_calling_tree(), findme_user::dialarg, FOLLOWMEFLAG_DISABLEOPTIMIZATION, fm_args::followmeflags, LOG_ERROR, LOG_WARNING, NULL, number::number, findme_user::ochan, number::order, findme_user::pending_connected_update, fm_args::pending_out_connected_update, fm_args::predial_callee, S_COR, findme_user::state, number::timeout, and wait_for_winner().

Referenced by app_exec().

◆ free_numbers()

static void free_numbers ( struct call_followme f)
static

Definition at line 288 of file app_followme.c.

289{
290 /* Free numbers attached to the profile */
291 struct number *prev;
292
293 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
294 /* Free the number */
295 ast_free(prev);
297
298 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
299 /* Free the blacklisted number */
300 ast_free(prev);
302
303 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
304 /* Free the whitelisted number */
305 ast_free(prev);
307}

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

◆ init_profile()

static void init_profile ( struct call_followme f,
int  activate 
)
static

Definition at line 326 of file app_followme.c.

327{
329 f->context[0] = '\0';
330 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
331 ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
332 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
340 if (activate) {
341 f->active = 1;
342 }
343}
static char connprompt[PATH_MAX]
Definition: app_followme.c:282
static char sorryprompt[PATH_MAX]
Definition: app_followme.c:281
static char callfromprompt[PATH_MAX]
Definition: app_followme.c:276
static char takecall[MAX_YN_STRING]
Definition: app_followme.c:273
static char nextindp[MAX_YN_STRING]
Definition: app_followme.c:274
static char optionsprompt[PATH_MAX]
Definition: app_followme.c:278
static char statusprompt[PATH_MAX]
Definition: app_followme.c:280
static char plsholdprompt[PATH_MAX]
Definition: app_followme.c:279
static char norecordingprompt[PATH_MAX]
Definition: app_followme.c:277
static int enable_callee_prompt
Definition: app_followme.c:275
static const char * defaultmoh
Definition: app_followme.c:271

References call_followme::active, ast_copy_string(), call_followme::callfromprompt, callfromprompt, call_followme::connprompt, connprompt, call_followme::context, defaultmoh, call_followme::enable_callee_prompt, enable_callee_prompt, call_followme::moh, call_followme::nextindp, nextindp, call_followme::norecordingprompt, norecordingprompt, call_followme::optionsprompt, optionsprompt, call_followme::plsholdprompt, plsholdprompt, call_followme::sorryprompt, sorryprompt, call_followme::statusprompt, statusprompt, call_followme::takecall, and takecall.

Referenced by find_realtime(), and reload_followme().

◆ load_module()

static int load_module ( void  )
static

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 1637 of file app_followme.c.

1638{
1639 if(!reload_followme(0))
1641
1643}
static int reload_followme(int reload)
Reload followme application module.
Definition: app_followme.c:404
static int app_exec(struct ast_channel *chan, const char *data)
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626

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

◆ profile_set_param()

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

349{
350
351 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
352 ast_copy_string(f->moh, val, sizeof(f->moh));
353 else if (!strcasecmp(param, "context"))
354 ast_copy_string(f->context, val, sizeof(f->context));
355 else if (!strcasecmp(param, "enable_callee_prompt"))
357 else if (!strcasecmp(param, "takecall"))
358 ast_copy_string(f->takecall, val, sizeof(f->takecall));
359 else if (!strcasecmp(param, "declinecall"))
360 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
361 else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
363 else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
365 else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
367 else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
369 else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
371 else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
373 else if (!strcasecmp(param, "followme-connecting-prompt") || !strcasecmp(param, "connecting_prompt")) {
374 ast_copy_string(f->connprompt, val, sizeof(f->connprompt));
375 } else if (failunknown) {
376 if (linenum >= 0)
377 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
378 else
379 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
380 }
381}
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
Definition: ast_expr2.c:325

References ast_copy_string(), ast_log, ast_true(), call_followme::callfromprompt, call_followme::connprompt, call_followme::context, call_followme::enable_callee_prompt, 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().

◆ publish_dial_end_event()

static void publish_dial_end_event ( struct ast_channel in,
struct findme_user_listptr findme_user_list,
struct ast_channel exception,
const char *  status 
)
static

Definition at line 592 of file app_followme.c.

593{
594 struct findme_user *tmpuser;
595
596 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
597 if (tmpuser->ochan && tmpuser->ochan != exception) {
599 }
600 }
601}
jack_status_t status
Definition: app_jack.c:146
FILE * in
Definition: utils/frame.c:33

References ast_channel_publish_dial(), AST_LIST_TRAVERSE, in, NULL, findme_user::ochan, and status.

Referenced by wait_for_winner().

◆ reload()

static int reload ( void  )
static

Definition at line 1645 of file app_followme.c.

1646{
1647 reload_followme(1);
1648
1649 return 0;
1650}

References reload_followme().

Referenced by reload_followme().

◆ reload_followme()

static int reload_followme ( int  reload)
static

Reload followme application module.

Definition at line 404 of file app_followme.c.

405{
406 struct call_followme *f;
407 struct ast_config *cfg;
408 char *cat = NULL, *tmp;
409 struct ast_variable *var;
410 struct number *cur, *nm;
411 int timeout;
412 int numorder;
413 const char* enable_callee_prompt_str;
414 const char *takecallstr;
415 const char *declinecallstr;
416 const char *tmpstr;
417 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
418
419 if (!(cfg = ast_config_load("followme.conf", config_flags))) {
420 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
421 return 0;
422 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
423 return 0;
424 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
425 ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
426 return 0;
427 }
428
430
431 /* Reset Global Var Values */
432 featuredigittimeout = 5000;
433
434 /* Mark all profiles as inactive for the moment */
436 f->active = 0;
437 }
438
439 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
440
442 if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
443 featuredigittimeout = 5000;
444 }
445
446 if ((enable_callee_prompt_str = ast_variable_retrieve(cfg, "general",
447 "enable_callee_prompt")) &&
448 !ast_strlen_zero(enable_callee_prompt_str)) {
449 enable_callee_prompt = ast_true(enable_callee_prompt_str);
450 }
451
452 if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
453 ast_copy_string(takecall, takecallstr, sizeof(takecall));
454 }
455
456 if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
457 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
458 }
459
460 if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
462 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
464 }
465
466 if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
468 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
470 }
471
472
473 if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
475 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
477 }
478
479 if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
481 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
483 }
484
485 if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
486 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
487 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
488 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
489 }
490
491 if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
492 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
493 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
494 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
495 }
496
497 if ((tmpstr = ast_variable_retrieve(cfg, "general", "connecting-prompt")) && !ast_strlen_zero(tmpstr)) {
498 ast_copy_string(connprompt, tmpstr, sizeof(connprompt));
499 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "connecting_prompt")) && !ast_strlen_zero(tmpstr)) {
500 ast_copy_string(connprompt, tmpstr, sizeof(connprompt));
501 }
502
503
504 /* Chug through config file */
505 while ((cat = ast_category_browse(cfg, cat))) {
506 int new = 0;
507
508 if (!strcasecmp(cat, "general"))
509 continue;
510
511 /* Look for an existing one */
513 if (!strcasecmp(f->name, cat))
514 break;
515 }
516
517 ast_debug(1, "New profile %s.\n", cat);
518
519 if (!f) {
520 /* Make one then */
521 f = alloc_profile(cat);
522 new = 1;
523 }
524
525 /* Totally fail if we fail to find/create an entry */
526 if (!f)
527 continue;
528
529 if (!new)
530 ast_mutex_lock(&f->lock);
531 /* Re-initialize the profile */
532 init_profile(f, 1);
533 free_numbers(f);
534 var = ast_variable_browse(cfg, cat);
535 while (var) {
536 if (!strcasecmp(var->name, "number")) {
537 int idx = 0;
538 char copy[strlen(var->value) + 1];
539 char *numberstr;
540
541 /* Add a new number */
542 strcpy(copy, var->value); /* safe */
543 numberstr = copy;
544 if ((tmp = strchr(numberstr, ','))) {
545 *tmp++ = '\0';
546 timeout = atoi(tmp);
547 if (timeout < 0) {
548 timeout = 25;
549 }
550 if ((tmp = strchr(tmp, ','))) {
551 *tmp++ = '\0';
552 numorder = atoi(tmp);
553 if (numorder < 0)
554 numorder = 0;
555 } else
556 numorder = 0;
557 } else {
558 timeout = 25;
559 numorder = 0;
560 }
561
562 if (!numorder) {
563 idx = 1;
565 idx++;
566 numorder = idx;
567 }
568 cur = create_followme_number(numberstr, timeout, numorder);
569 if (cur) {
571 }
572 } else {
573 profile_set_param(f, var->name, var->value, var->lineno, 1);
574 ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
575 }
576 var = var->next;
577 } /* End while(var) loop */
578
579 if (!new)
581 else
583 }
584
586
588
589 return 1;
590}
static int featuredigittimeout
Definition: app_followme.c:270
static const char * featuredigittostr
Definition: app_followme.c:269
static int reload(void)
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:718
Structure used to handle boolean flags.
Definition: utils.h:199

References call_followme::active, alloc_profile(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_debug, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, ast_log, ast_mutex_lock, ast_mutex_unlock, AST_RWLIST_INSERT_HEAD, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), callfromprompt, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, connprompt, copy(), create_followme_number(), enable_callee_prompt, featuredigittimeout, featuredigittostr, free_numbers(), init_profile(), call_followme::lock, LOG_ERROR, LOG_WARNING, call_followme::name, nextindp, norecordingprompt, NULL, call_followme::numbers, optionsprompt, plsholdprompt, profile_set_param(), reload(), sorryprompt, statusprompt, takecall, number::timeout, tmp(), and var.

Referenced by load_module(), and reload().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1609 of file app_followme.c.

1610{
1611 struct call_followme *f;
1612
1614
1615 /* Free Memory. Yeah! I'm free! */
1617 while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
1618 free_numbers(f);
1619 ast_free(f);
1620 }
1621
1623
1624 return 0;
1625}
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392

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

◆ wait_for_winner()

static struct ast_channel * wait_for_winner ( struct findme_user_listptr findme_user_list,
struct number nm,
struct ast_channel caller,
struct fm_args tpargs 
)
static

Definition at line 644 of file app_followme.c.

645{
647 struct ast_channel *watchers[256];
648 int pos;
649 struct ast_channel *winner;
650 struct ast_frame *f;
651 struct findme_user *tmpuser;
652 int to = 0;
653 int livechannels;
654 int tmpto;
655 long totalwait = 0, wtd = 0, towas = 0;
656 char *callfromname;
657 char *pressbuttonname;
658
659 /* ------------ wait_for_winner_channel start --------------- */
660
661 callfromname = ast_strdupa(tpargs->callfromprompt);
662 pressbuttonname = ast_strdupa(tpargs->optionsprompt);
663
664 totalwait = nm->timeout * 1000;
665
666 for (;;) {
667 to = 1000;
668 pos = 1;
669 livechannels = 0;
670 watchers[0] = caller;
671
672 winner = NULL;
673 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
674 if (!tmpuser->ochan) {
675 continue;
676 }
677 if (tmpuser->state == 3) {
678 tmpuser->digts += (towas - wtd);
679 }
680 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
681 ast_verb(3, "<%s> We've been waiting for digits longer than we should have.\n",
682 ast_channel_name(tmpuser->ochan));
683 if (tpargs->enable_callee_prompt) {
684 if (!ast_strlen_zero(tpargs->namerecloc)) {
685 tmpuser->state = 1;
686 tmpuser->digts = 0;
687 if (!ast_streamfile(tmpuser->ochan, callfromname, ast_channel_language(tmpuser->ochan))) {
689 } else {
690 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
691 clear_caller(tmpuser);
692 continue;
693 }
694 } else {
695 tmpuser->state = 2;
696 tmpuser->digts = 0;
697 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
699 else {
700 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
701 clear_caller(tmpuser);
702 continue;
703 }
704 }
705 } else {
706 tmpuser->state = 3;
707 }
708 }
709 if (ast_channel_stream(tmpuser->ochan)) {
711 tmpto = ast_sched_wait(ast_channel_sched(tmpuser->ochan));
712 if (tmpto > 0 && tmpto < to)
713 to = tmpto;
714 else if (tmpto < 0 && !ast_channel_timingfunc(tmpuser->ochan)) {
715 ast_stopstream(tmpuser->ochan);
716 switch (tmpuser->state) {
717 case 1:
718 ast_verb(3, "<%s> Playback of the call-from file appears to be done.\n",
719 ast_channel_name(tmpuser->ochan));
720 if (!ast_streamfile(tmpuser->ochan, tpargs->namerecloc, ast_channel_language(tmpuser->ochan))) {
721 tmpuser->state = 2;
722 } else {
723 ast_log(LOG_NOTICE, "<%s> Unable to playback %s. Maybe the caller didn't record their name?\n",
724 ast_channel_name(tmpuser->ochan), tpargs->namerecloc);
725 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
726 tmpuser->ynidx = 0;
727 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan)))
728 tmpuser->state = 3;
729 else {
730 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
731 clear_caller(tmpuser);
732 continue;
733 }
734 }
735 break;
736 case 2:
737 ast_verb(3, "<%s> Playback of name file appears to be done.\n",
738 ast_channel_name(tmpuser->ochan));
739 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
740 tmpuser->ynidx = 0;
741 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, ast_channel_language(tmpuser->ochan))) {
742 tmpuser->state = 3;
743 } else {
744 clear_caller(tmpuser);
745 continue;
746 }
747 break;
748 case 3:
749 ast_verb(3, "<%s> Playback of the next step file appears to be done.\n",
750 ast_channel_name(tmpuser->ochan));
751 tmpuser->digts = 0;
752 break;
753 default:
754 break;
755 }
756 }
757 }
758 watchers[pos++] = tmpuser->ochan;
759 livechannels++;
760 }
761 if (!livechannels) {
762 ast_verb(3, "No live channels left for this step.\n");
763 return NULL;
764 }
765
766 tmpto = to;
767 towas = to;
768 winner = ast_waitfor_n(watchers, pos, &to);
769 tmpto -= to;
770 totalwait -= tmpto;
771 wtd = to;
772 if (totalwait <= 0) {
773 ast_verb(3, "We've hit our timeout for this step. Dropping unanswered calls and starting the next step.\n");
774 clear_unanswered_calls(findme_user_list);
775 return NULL;
776 }
777 if (winner) {
778 /* Need to find out which channel this is */
779 if (winner != caller) {
780 /* The winner is an outgoing channel. */
781 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
782 if (tmpuser->ochan == winner) {
783 break;
784 }
785 }
786 } else {
787 tmpuser = NULL;
788 }
789
790 f = ast_read(winner);
791 if (f) {
792 if (f->frametype == AST_FRAME_CONTROL) {
793 switch (f->subclass.integer) {
795 ast_verb(3, "%s received a hangup frame.\n", ast_channel_name(winner));
796 if (f->data.uint32) {
798 }
799 if (!tmpuser) {
800 ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
801 publish_dial_end_event(caller, findme_user_list, NULL, "CANCEL");
802 ast_frfree(f);
803 return NULL;
804 }
805 clear_caller(tmpuser);
806 break;
808 if (!tmpuser) {
809 /* The caller answered? We want an outgoing channel to answer. */
810 break;
811 }
812 ast_verb(3, "%s answered %s\n", ast_channel_name(winner), ast_channel_name(caller));
813 ast_channel_publish_dial(caller, winner, NULL, "ANSWER");
814 publish_dial_end_event(caller, findme_user_list, winner, "CANCEL");
815 tmpuser->answered = 1;
816 /* If call has been answered, then the eventual hangup is likely to be normal hangup */
819 if (tpargs->enable_callee_prompt) {
820 ast_verb(3, "Starting playback of %s\n", callfromname);
821 if (!ast_strlen_zero(tpargs->namerecloc)) {
822 if (!ast_streamfile(winner, callfromname, ast_channel_language(winner))) {
824 tmpuser->state = 1;
825 } else {
826 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
827 clear_caller(tmpuser);
828 }
829 } else {
830 tmpuser->state = 2;
831 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, ast_channel_language(tmpuser->ochan)))
833 else {
834 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
835 clear_caller(tmpuser);
836 }
837 }
838 } else {
839 ast_debug(1, "Taking call with no prompt\n");
840 ast_frfree(f);
841 return tmpuser->ochan;
842 }
843 break;
844 case AST_CONTROL_BUSY:
845 ast_verb(3, "%s is busy\n", ast_channel_name(winner));
846 if (tmpuser) {
847 /* Outbound call was busy. Drop it. */
848 ast_channel_publish_dial(caller, winner, NULL, "BUSY");
849 clear_caller(tmpuser);
850 }
851 break;
853 ast_verb(3, "%s is circuit-busy\n", ast_channel_name(winner));
854 if (tmpuser) {
855 /* Outbound call was congested. Drop it. */
856 ast_channel_publish_dial(caller, winner, NULL, "CONGESTION");
857 clear_caller(tmpuser);
858 }
859 break;
861 ast_verb(3, "%s is ringing\n", ast_channel_name(winner));
862 ast_channel_publish_dial(caller, winner, NULL, "RINGING");
863 break;
865 ast_verb(3, "%s is making progress\n", ast_channel_name(winner));
866 ast_channel_publish_dial(caller, winner, NULL, "PROGRESS");
867 break;
869 ast_verb(3, "%s requested a video update\n", ast_channel_name(winner));
870 break;
872 ast_verb(3, "%s requested a source update\n", ast_channel_name(winner));
873 break;
875 ast_verb(3, "%s is proceeding\n", ast_channel_name(winner));
876 ast_channel_publish_dial(caller, winner, NULL, "PROCEEDING");
877 break;
878 case AST_CONTROL_HOLD:
879 ast_verb(3, "%s placed call on hold\n", ast_channel_name(winner));
880 if (!tmpuser) {
881 /* Caller placed outgoing calls on hold. */
882 tpargs->pending_hold = 1;
883 if (f->data.ptr) {
885 sizeof(tpargs->suggested_moh));
886 } else {
887 tpargs->suggested_moh[0] = '\0';
888 }
889 } else {
890 /*
891 * Outgoing call placed caller on hold.
892 *
893 * Ignore because the outgoing call should not be able to place
894 * the caller on hold until after they are bridged.
895 */
896 }
897 break;
899 ast_verb(3, "%s removed call from hold\n", ast_channel_name(winner));
900 if (!tmpuser) {
901 /* Caller removed outgoing calls from hold. */
902 tpargs->pending_hold = 0;
903 } else {
904 /*
905 * Outgoing call removed caller from hold.
906 *
907 * Ignore because the outgoing call should not be able to place
908 * the caller on hold until after they are bridged.
909 */
910 }
911 break;
914 /* Ignore going off hook and flash */
915 break;
918 ast_verb(3, "Connected line update from %s prevented.\n",
919 ast_channel_name(winner));
920 break;
921 }
922 if (!tmpuser) {
923 /*
924 * Hold connected line update from caller until we have a
925 * winner.
926 */
927 ast_verb(3,
928 "%s connected line has changed. Saving it until we have a winner.\n",
929 ast_channel_name(winner));
933 &connected, NULL);
934 tpargs->pending_in_connected_update = 1;
935 }
937 } else {
938 ast_verb(3,
939 "%s connected line has changed. Saving it until answer.\n",
940 ast_channel_name(winner));
944 &connected, NULL);
945 tmpuser->pending_connected_update = 1;
946 }
948 }
949 break;
951 /*
952 * Ignore because we are masking the FollowMe search progress to
953 * the caller.
954 */
955 break;
957 ast_indicate_data(caller, f->subclass.integer, f->data.ptr, f->datalen);
958 break;
959 case -1:
960 ast_verb(3, "%s stopped sounds\n", ast_channel_name(winner));
961 break;
962 default:
963 ast_debug(1, "Dunno what to do with control type %d from %s\n",
964 f->subclass.integer, ast_channel_name(winner));
965 break;
966 }
967 }
968 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
969 int cmp_len;
970
971 if (ast_channel_stream(winner))
972 ast_stopstream(winner);
973 tmpuser->digts = 0;
974 ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
975 if (tmpuser->ynidx < ARRAY_LEN(tmpuser->yn) - 1) {
976 tmpuser->yn[tmpuser->ynidx++] = f->subclass.integer;
977 } else {
978 /* Discard oldest digit. */
979 memmove(tmpuser->yn, tmpuser->yn + 1,
980 sizeof(tmpuser->yn) - 2 * sizeof(tmpuser->yn[0]));
981 tmpuser->yn[ARRAY_LEN(tmpuser->yn) - 2] = f->subclass.integer;
982 }
983 ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
984 cmp_len = strlen(tpargs->takecall);
985 if (cmp_len <= tmpuser->ynidx
986 && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->takecall)) {
987 ast_debug(1, "Match to take the call!\n");
988 ast_frfree(f);
989 return tmpuser->ochan;
990 }
991 cmp_len = strlen(tpargs->nextindp);
992 if (cmp_len <= tmpuser->ynidx
993 && !strcmp(tmpuser->yn + (tmpuser->ynidx - cmp_len), tpargs->nextindp)) {
994 ast_debug(1, "Declined to take the call.\n");
995 clear_caller(tmpuser);
996 }
997 }
998
999 ast_frfree(f);
1000 } else {
1001 ast_debug(1, "we didn't get a frame. hanging up.\n");
1002 if (!tmpuser) {
1003 /* Caller hung up. */
1004 ast_verb(3, "The calling channel hungup. Need to drop everyone.\n");
1005 return NULL;
1006 }
1007 /* Outgoing channel hung up. */
1008 ast_channel_publish_dial(caller, winner, NULL, "NOANSWER");
1009 clear_caller(tmpuser);
1010 }
1011 } else {
1012 ast_debug(1, "timed out waiting for action\n");
1013 }
1014 }
1015
1016 /* Unreachable. */
1017}
static void clear_unanswered_calls(struct findme_user_listptr *findme_user_list)
Definition: app_followme.c:617
static void publish_dial_end_event(struct ast_channel *in, struct findme_user_listptr *findme_user_list, struct ast_channel *exception, const char *status)
Definition: app_followme.c:592
#define AST_CAUSE_NORMAL_CLEARING
Definition: causes.h:106
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Definition: channel.c:3157
void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src, const struct ast_set_party_connected_line *update)
Set the connected line information based on another connected line source.
Definition: channel.c:2054
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
Parse connected line indication frame data.
Definition: channel.c:8785
ast_timing_func_t ast_channel_timingfunc(const struct ast_channel *chan)
struct ast_sched_context * ast_channel_sched(const struct ast_channel *chan)
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
Initialize the given connected line structure using the given guide for a set update operation.
Definition: channel.c:2045
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
char connected
Definition: eagi_proxy.c:82
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_SRCUPDATE
@ AST_CONTROL_PROGRESS
@ AST_CONTROL_OFFHOOK
@ AST_CONTROL_BUSY
@ AST_CONTROL_UNHOLD
@ AST_CONTROL_VIDUPDATE
@ AST_CONTROL_PROCEEDING
@ AST_CONTROL_REDIRECTING
@ AST_CONTROL_CONGESTION
@ AST_CONTROL_ANSWER
@ AST_CONTROL_HANGUP
@ AST_CONTROL_CONNECTED_LINE
@ AST_CONTROL_FLASH
@ AST_CONTROL_PVT_CAUSE_CODE
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:786
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
Definition: sched.c:433
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
Connected Line/Party information.
Definition: channel.h:456
char yn[MAX_YN_STRING]
Definition: app_followme.c:229
#define ARRAY_LEN(a)
Definition: utils.h:666

References findme_user::answered, ARRAY_LEN, AST_CAUSE_NORMAL_CLEARING, ast_channel_hangupcause_set(), ast_channel_language(), ast_channel_name(), ast_channel_publish_dial(), ast_channel_sched(), ast_channel_stream(), ast_channel_timingfunc(), 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_PVT_CAUSE_CODE, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_VIDUPDATE, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_indicate_data(), 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_caller(), clear_unanswered_calls(), findme_user::connected, connected, fm_args::connected_in, ast_frame::data, ast_frame::datalen, findme_user::digts, fm_args::enable_callee_prompt, featuredigittimeout, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE, fm_args::followmeflags, ast_frame::frametype, ast_frame_subclass::integer, LOG_NOTICE, LOG_WARNING, fm_args::namerecloc, fm_args::nextindp, fm_args::norecordingprompt, NULL, findme_user::ochan, fm_args::optionsprompt, findme_user::pending_connected_update, fm_args::pending_hold, fm_args::pending_in_connected_update, ast_frame::ptr, publish_dial_end_event(), findme_user::state, ast_frame::subclass, fm_args::suggested_moh, fm_args::takecall, number::timeout, ast_frame::uint32, findme_user::yn, and findme_user::ynidx.

Referenced by findmeexec().

Variable Documentation

◆ __mod_info

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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, }
static

Definition at line 1657 of file app_followme.c.

◆ app

char* app = "FollowMe"
static

Definition at line 148 of file app_followme.c.

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

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1657 of file app_followme.c.

◆ callfromprompt

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

Definition at line 276 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ connprompt

char connprompt[PATH_MAX] = ""
static

Definition at line 282 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ defaultmoh

const char* defaultmoh = "default"
static

Default Music-On-Hold Class

Definition at line 271 of file app_followme.c.

Referenced by init_profile().

◆ enable_callee_prompt

int enable_callee_prompt = 1
static

Definition at line 275 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ featuredigittimeout

int featuredigittimeout = 5000
static

Feature Digit Timeout

Definition at line 270 of file app_followme.c.

Referenced by reload_followme(), and wait_for_winner().

◆ featuredigittostr

const char* featuredigittostr
static

Definition at line 269 of file app_followme.c.

Referenced by reload_followme().

◆ followme_opts

const struct ast_app_option followme_opts[128] = { [ 'a' ] = { .flag = FOLLOWMEFLAG_RECORDNAME }, [ 'B' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLER , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLER + 1 }, [ 'b' ] = { .flag = FOLLOWMEFLAG_PREDIAL_CALLEE , .arg_index = FOLLOWMEFLAG_ARG_PREDIAL_CALLEE + 1 }, [ 'd' ] = { .flag = FOLLOWMEFLAG_DISABLEHOLDPROMPT }, [ 'I' ] = { .flag = FOLLOWMEFLAG_IGNORE_CONNECTEDLINE }, [ 'l' ] = { .flag = FOLLOWMEFLAG_DISABLEOPTIMIZATION }, [ 'N' ] = { .flag = FOLLOWMEFLAG_NOANSWER }, [ 'n' ] = { .flag = FOLLOWMEFLAG_UNREACHABLEMSG }, [ 's' ] = { .flag = FOLLOWMEFLAG_STATUSMSG }, }
static

Definition at line 267 of file app_followme.c.

Referenced by app_exec().

◆ followmes

struct followmes followmes = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
static

◆ nextindp

char nextindp[MAX_YN_STRING] = "2"
static

Definition at line 274 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ norecordingprompt

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

Definition at line 277 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ optionsprompt

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

Definition at line 278 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ plsholdprompt

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

Definition at line 279 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ sorryprompt

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

Definition at line 281 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ statusprompt

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

Definition at line 280 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ takecall

char takecall[MAX_YN_STRING] = "1"
static

Definition at line 273 of file app_followme.c.

Referenced by init_profile(), and reload_followme().