Asterisk - The Open Source Telephony Project GIT-master-27fb039
Loading...
Searching...
No Matches
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.
 
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.
 
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.
 
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 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.
 
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 = ASTERISK_GPL_KEY , .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 = AST_RWLIST_HEAD_INIT_VALUE
 
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 154 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 240 of file app_followme.c.

240 {
241 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
242 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
245 FOLLOWMEFLAG_NOANSWER = (1 << 4),
250};
@ FOLLOWMEFLAG_UNREACHABLEMSG
@ FOLLOWMEFLAG_DISABLEHOLDPROMPT
@ FOLLOWMEFLAG_NOANSWER
@ FOLLOWMEFLAG_PREDIAL_CALLER
@ FOLLOWMEFLAG_IGNORE_CONNECTEDLINE
@ FOLLOWMEFLAG_RECORDNAME
@ FOLLOWMEFLAG_DISABLEOPTIMIZATION
@ FOLLOWMEFLAG_PREDIAL_CALLEE
@ FOLLOWMEFLAG_STATUSMSG

◆ anonymous enum

anonymous enum
Enumerator
FOLLOWMEFLAG_ARG_PREDIAL_CALLER 
FOLLOWMEFLAG_ARG_PREDIAL_CALLEE 
FOLLOWMEFLAG_ARG_ARRAY_SIZE 

Definition at line 252 of file app_followme.c.

252 {
255
256 /* note: this entry _MUST_ be the last one in the enum */
258};
@ FOLLOWMEFLAG_ARG_ARRAY_SIZE
@ FOLLOWMEFLAG_ARG_PREDIAL_CALLER
@ FOLLOWMEFLAG_ARG_PREDIAL_CALLEE

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1660 of file app_followme.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1660 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 314 of file app_followme.c.

315{
316 struct call_followme *f;
317
318 if (!(f = ast_calloc(1, sizeof(*f))))
319 return NULL;
320
321 ast_mutex_init(&f->lock);
322 ast_copy_string(f->name, fmname, sizeof(f->name));
326 return f;
327}
#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.
#define ast_mutex_init(pmutex)
Definition lock.h:193
#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.
struct call_followme::wlnumbers wlnumbers
char name[AST_MAX_EXTENSION]
struct call_followme::blnumbers blnumbers
struct call_followme::numbers numbers
ast_mutex_t lock

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

1347{
1348 struct fm_args *targs;
1350 struct call_followme *f;
1351 struct number *nm, *newnm;
1352 int res = 0;
1353 char *argstr;
1354 struct ast_channel *caller;
1355 struct ast_channel *outbound;
1357 AST_APP_ARG(followmeid);
1359 );
1360 char *opt_args[FOLLOWMEFLAG_ARG_ARRAY_SIZE];
1361 int max_forwards;
1362
1363 if (ast_strlen_zero(data)) {
1364 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1365 return -1;
1366 }
1367
1368 ast_channel_lock(chan);
1370 ast_channel_unlock(chan);
1371
1372 if (max_forwards <= 0) {
1373 ast_log(LOG_WARNING, "Unable to execute FollowMe on channel %s. Max forwards exceeded\n",
1374 ast_channel_name(chan));
1375 return -1;
1376 }
1377
1378 argstr = ast_strdupa((char *) data);
1379
1380 AST_STANDARD_APP_ARGS(args, argstr);
1381
1382 if (ast_strlen_zero(args.followmeid)) {
1383 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
1384 return -1;
1385 }
1386
1387 targs = ast_calloc(1, sizeof(*targs));
1388 if (!targs) {
1389 return -1;
1390 }
1391
1393 AST_RWLIST_TRAVERSE(&followmes, f, entry) {
1394 if (!strcasecmp(f->name, args.followmeid) && (f->active))
1395 break;
1396 }
1398
1399 ast_debug(1, "New profile %s.\n", args.followmeid);
1400
1401 if (!f) {
1402 f = find_realtime(args.followmeid);
1403 }
1404
1405 if (!f) {
1406 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
1407 ast_free(targs);
1408 return 0;
1409 }
1410
1411 /* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */
1412 if (args.options) {
1413 ast_app_parse_options(followme_opts, &targs->followmeflags, opt_args, args.options);
1414 }
1415
1416 /* Lock the profile lock and copy out everything we need to run with before unlocking it again */
1417 ast_mutex_lock(&f->lock);
1419 targs->mohclass = ast_strdupa(f->moh);
1420 ast_copy_string(targs->context, f->context, sizeof(targs->context));
1421 ast_copy_string(targs->takecall, f->takecall, sizeof(targs->takecall));
1422 ast_copy_string(targs->nextindp, f->nextindp, sizeof(targs->nextindp));
1423 ast_copy_string(targs->callfromprompt, f->callfromprompt, sizeof(targs->callfromprompt));
1425 ast_copy_string(targs->optionsprompt, f->optionsprompt, sizeof(targs->optionsprompt));
1426 ast_copy_string(targs->plsholdprompt, f->plsholdprompt, sizeof(targs->plsholdprompt));
1427 ast_copy_string(targs->statusprompt, f->statusprompt, sizeof(targs->statusprompt));
1428 ast_copy_string(targs->sorryprompt, f->sorryprompt, sizeof(targs->sorryprompt));
1429 ast_copy_string(targs->connprompt, f->connprompt, sizeof(targs->connprompt));
1430 /* Copy the numbers we're going to use into another list in case the master list should get modified
1431 (and locked) while we're trying to do a follow-me */
1433 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
1434 newnm = create_followme_number(nm->number, nm->timeout, nm->order);
1435 if (newnm) {
1436 AST_LIST_INSERT_TAIL(&targs->cnumbers, newnm, entry);
1437 }
1438 }
1440
1441 /* PREDIAL: Preprocess any callee gosub arguments. */
1445 targs->predial_callee =
1447 }
1448
1449 /* PREDIAL: Run gosub on the caller's channel */
1454 }
1455
1456 /* Forget the 'N' option if the call is already up. */
1457 if (ast_channel_state(chan) == AST_STATE_UP) {
1459 }
1460
1463 } else {
1464 /* Answer the call */
1465 if (ast_channel_state(chan) != AST_STATE_UP) {
1466 ast_answer(chan);
1467 }
1468
1470 ast_stream_and_wait(chan, targs->statusprompt, "");
1471 }
1472
1474 int duration = 5;
1475
1476 snprintf(targs->namerecloc, sizeof(targs->namerecloc), "%s/followme.%s",
1478 if (ast_play_and_record(chan, "vm-rec-name", targs->namerecloc, 5, REC_FORMAT, &duration,
1480 goto outrun;
1481 }
1482 if (!ast_fileexists(targs->namerecloc, NULL, ast_channel_language(chan))) {
1483 targs->namerecloc[0] = '\0';
1484 }
1485 }
1486
1488 if (ast_streamfile(chan, targs->plsholdprompt, ast_channel_language(chan))) {
1489 goto outrun;
1490 }
1491 if (ast_waitstream(chan, "") < 0)
1492 goto outrun;
1493 }
1494 ast_moh_start(chan, targs->mohclass, NULL);
1495 }
1496
1497 ast_channel_lock(chan);
1499 ast_channel_unlock(chan);
1500
1501 outbound = findmeexec(targs, chan);
1502 if (!outbound) {
1504 if (ast_channel_state(chan) != AST_STATE_UP) {
1505 ast_answer(chan);
1506 }
1507 } else {
1508 ast_moh_stop(chan);
1509 }
1510
1512 ast_stream_and_wait(chan, targs->sorryprompt, "");
1513 }
1514 res = 0;
1515 } else {
1516 caller = chan;
1517
1518 /* Play "connecting" message to the winner, if configured. */
1519 if (!ast_strlen_zero(targs->connprompt)) {
1521 ast_stream_and_wait(outbound, targs->connprompt, "");
1523 }
1524
1525 /* Bridge the two channels. */
1526
1527 memset(&config, 0, sizeof(config));
1528 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1529 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
1530 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
1531 config.end_bridge_callback = end_bridge_callback;
1532 config.end_bridge_callback_data = chan;
1533 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
1534
1535 /* Update connected line to caller if available. */
1536 if (targs->pending_out_connected_update) {
1537 if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0)) {
1539 }
1540 }
1541
1545 }
1546 } else {
1548 }
1549
1550 /* Be sure no generators are left on it */
1552 /* Make sure channels are compatible */
1553 res = ast_channel_make_compatible(caller, outbound);
1554 if (res < 0) {
1555 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));
1557 goto outrun;
1558 }
1559
1560 /* Update connected line to winner if changed. */
1561 if (targs->pending_in_connected_update) {
1562 if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0)) {
1564 }
1565 }
1566
1567 /* Put winner on hold if caller requested. */
1568 if (targs->pending_hold) {
1569 if (ast_strlen_zero(targs->suggested_moh)) {
1571 } else {
1573 targs->suggested_moh, strlen(targs->suggested_moh) + 1);
1574 }
1575 }
1576
1577 res = ast_bridge_call(caller, outbound, &config);
1578 }
1579
1580outrun:
1581 while ((nm = AST_LIST_REMOVE_HEAD(&targs->cnumbers, entry))) {
1582 ast_free(nm);
1583 }
1584 if (!ast_strlen_zero(targs->namerecloc)) {
1585 int ret;
1586 char fn[PATH_MAX + sizeof(REC_FORMAT)];
1587
1588 snprintf(fn, sizeof(fn), "%s.%s", targs->namerecloc,
1589 REC_FORMAT);
1590 ret = unlink(fn);
1591 if (ret != 0) {
1592 ast_log(LOG_NOTICE, "Failed to delete recorded name file %s: %d (%s)\n",
1593 fn, errno, strerror(errno));
1594 } else {
1595 ast_debug(2, "deleted recorded prompt %s.\n", fn);
1596 }
1597 }
1598 ast_free((char *) targs->predial_callee);
1601 ast_free(targs);
1602
1603 if (f->realtime) {
1604 /* Not in list */
1605 free_numbers(f);
1606 ast_free(f);
1607 }
1608
1609 return res;
1610}
#define REC_FORMAT
static struct ast_channel * findmeexec(struct fm_args *tpargs, struct ast_channel *caller)
static const struct ast_app_option followme_opts[128]
static char * app
static struct number * create_followme_number(const char *number, int timeout, int numorder)
Add a new number.
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)
#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[]
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
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:10382
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition channel.c:2058
#define ast_channel_lock(chan)
Definition channel.h:2982
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition channel.c:6715
const char * ast_channel_uniqueid(const struct ast_channel *chan)
void ast_deactivate_generator(struct ast_channel *chan)
Definition channel.c:2890
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
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:9137
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:4648
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:8337
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.
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition channel.c:2803
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition channel.c:4270
#define ast_channel_unlock(chan)
Definition channel.h:2983
@ AST_FEATURE_REDIRECT
Definition channel.h:1084
@ AST_FEATURE_AUTOMON
Definition channel.h:1087
ast_channel_state
ast_channel states
@ AST_STATE_UP
@ 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:2013
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:694
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition file.c:1312
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:1912
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1148
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition file.c:1874
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:2154
#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:3066
@ AST_CONTROL_RINGING
#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.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_RWLIST_TRAVERSE
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define ast_mutex_unlock(a)
Definition lock.h:197
#define ast_mutex_lock(a)
Definition lock.h:196
int errno
int ast_max_forwards_get(struct ast_channel *chan)
Get the current max forwards for a particular channel.
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:7778
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition channel.c:7788
const char * ast_config_AST_SPOOL_DIR
Definition options.c:155
static struct @519 args
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
bridge configuration
Definition channel.h:1096
Main Channel structure associated with a channel.
const char * data
struct ast_party_caller caller
Channel Caller ID information.
char connprompt[PATH_MAX]
unsigned int enable_callee_prompt
char sorryprompt[PATH_MAX]
char callfromprompt[PATH_MAX]
char takecall[MAX_YN_STRING]
char nextindp[MAX_YN_STRING]
char optionsprompt[PATH_MAX]
char statusprompt[PATH_MAX]
char plsholdprompt[PATH_MAX]
char moh[MAX_MUSICCLASS]
char context[AST_MAX_CONTEXT]
char norecordingprompt[PATH_MAX]
unsigned int active
char connprompt[PATH_MAX]
unsigned int pending_in_connected_update
char namerecloc[PATH_MAX]
struct fm_args::cnumbers cnumbers
unsigned int enable_callee_prompt
char sorryprompt[PATH_MAX]
char callfromprompt[PATH_MAX]
char takecall[MAX_YN_STRING]
unsigned int pending_hold
struct ast_party_connected_line connected_out
unsigned int pending_out_connected_update
char suggested_moh[MAX_MUSICCLASS]
char nextindp[MAX_YN_STRING]
char * mohclass
char optionsprompt[PATH_MAX]
char statusprompt[PATH_MAX]
char plsholdprompt[PATH_MAX]
struct ast_flags followmeflags
char context[AST_MAX_CONTEXT]
char norecordingprompt[PATH_MAX]
const char * predial_callee
struct ast_party_connected_line connected_in
Channel datastore data for max forwards.
Number structure.
long timeout
char number[512]
static struct test_options options
#define ast_test_flag(p, flag)
Definition utils.h:64
#define ast_clear_flag(p, flag)
Definition utils.h:78
#define ast_set_flag(p, flag)
Definition utils.h:71
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition utils.c:2379

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.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1660 of file app_followme.c.

◆ clear_caller()

static void clear_caller ( struct findme_user tmpuser)
static

Definition at line 606 of file app_followme.c.

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

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

621{
622 struct findme_user *tmpuser;
623
624 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
625 if (!tmpuser->answered) {
626 clear_caller(tmpuser);
627 }
628 }
629}
static void clear_caller(struct findme_user *tmpuser)
struct findme_user::@27 entry
unsigned int answered

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

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

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

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

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

632{
635 ast_free(node);
636}

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

639{
640 struct findme_user *fmuser;
641
642 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
643 destroy_calling_node(fmuser);
644 }
645}
static void destroy_calling_node(struct findme_user *node)

References AST_LIST_REMOVE_HEAD, destroy_calling_node(), and findme_user::entry.

Referenced by findmeexec().

◆ end_bridge_callback()

static void end_bridge_callback ( void *  data)
static

Definition at line 1325 of file app_followme.c.

1326{
1327 char buf[80];
1328 time_t end;
1329 struct ast_channel *chan = data;
1330
1331 time(&end);
1332
1333 ast_channel_lock(chan);
1334 snprintf(buf, sizeof(buf), "%d", ast_channel_get_up_time(chan));
1335 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
1336 snprintf(buf, sizeof(buf), "%d", ast_channel_get_duration(chan));
1337 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
1338 ast_channel_unlock(chan);
1339}
int ast_channel_get_up_time(struct ast_channel *chan)
Obtain how long it has been since the channel was answered.
Definition channel.c:2843
int ast_channel_get_duration(struct ast_channel *chan)
Obtain how long the channel since the channel was created.
Definition channel.c:2828
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 1341 of file app_followme.c.

1342{
1343 bconfig->end_bridge_callback_data = originator;
1344}
void * end_bridge_callback_data
Definition channel.h:1111

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

1244{
1245 struct ast_variable *var;
1246 struct ast_variable *v;
1247 struct ast_config *cfg;
1248 const char *catg;
1249 struct call_followme *new_follower;
1250 struct ast_str *str;
1251
1252 str = ast_str_create(16);
1253 if (!str) {
1254 return NULL;
1255 }
1256
1257 var = ast_load_realtime("followme", "name", name, SENTINEL);
1258 if (!var) {
1259 ast_free(str);
1260 return NULL;
1261 }
1262
1263 if (!(new_follower = alloc_profile(name))) {
1265 ast_free(str);
1266 return NULL;
1267 }
1268 init_profile(new_follower, 0);
1269
1270 for (v = var; v; v = v->next) {
1271 if (!strcasecmp(v->name, "active")) {
1272 if (ast_false(v->value)) {
1273 ast_mutex_destroy(&new_follower->lock);
1274 ast_free(new_follower);
1276 ast_free(str);
1277 return NULL;
1278 }
1279 } else {
1280 profile_set_param(new_follower, v->name, v->value, 0, 0);
1281 }
1282 }
1283
1285 new_follower->realtime = 1;
1286
1287 /* Load numbers */
1288 cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
1289 name, SENTINEL);
1290 if (!cfg) {
1291 ast_mutex_destroy(&new_follower->lock);
1292 ast_free(new_follower);
1293 ast_free(str);
1294 return NULL;
1295 }
1296
1297 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
1298 const char *numstr;
1299 const char *timeoutstr;
1300 const char *ordstr;
1301 int timeout;
1302 struct number *cur;
1303
1304 if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
1305 continue;
1306 }
1307 if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
1308 || sscanf(timeoutstr, "%30d", &timeout) != 1
1309 || timeout < 1) {
1310 timeout = 25;
1311 }
1312 /* This one has to exist; it was part of the query */
1313 ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
1314 ast_str_set(&str, 0, "%s", numstr);
1315 if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
1316 AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
1317 }
1318 }
1319 ast_config_destroy(cfg);
1320
1321 ast_free(str);
1322 return new_follower;
1323}
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 struct call_followme * alloc_profile(const char *fmname)
Allocate and initialize followme profile.
static void init_profile(struct call_followme *f, int activate)
const char * str
Definition app_jack.c:150
#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:3324
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
#define ast_mutex_destroy(a)
Definition lock.h:195
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:2252
#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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
Support for dynamic strings.
Definition strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
struct number::@25 entry

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(), number::entry, 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 1032 of file app_followme.c.

1033{
1034 struct number *nm;
1035 struct ast_channel *winner = NULL;
1036 char num[512];
1037 int dg, idx;
1038 char *rest, *number;
1039 struct findme_user *tmpuser;
1040 struct findme_user *fmuser;
1041 struct findme_user_listptr findme_user_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
1043
1044 for (idx = 1; !ast_check_hangup(caller); ++idx) {
1045 /* Find next followme numbers to dial. */
1046 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
1047 if (nm->order == idx) {
1048 break;
1049 }
1050 }
1051 if (!nm) {
1052 ast_verb(3, "No more steps left.\n");
1053 break;
1054 }
1055
1056 ast_debug(2, "Number(s) %s timeout %ld\n", nm->number, nm->timeout);
1057
1058 /*
1059 * Put all active outgoing channels into autoservice.
1060 *
1061 * This needs to be done because ast_exists_extension() may put
1062 * the caller into autoservice.
1063 */
1064 AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
1065 if (tmpuser->ochan) {
1066 ast_autoservice_start(tmpuser->ochan);
1067 }
1068 }
1069
1070 /* Create all new outgoing calls */
1071 ast_copy_string(num, nm->number, sizeof(num));
1072 for (number = num; number; number = rest) {
1073 struct ast_channel *outbound;
1074 struct ast_format_cap *caps;
1075
1076 rest = strchr(number, '&');
1077 if (rest) {
1078 *rest++ = 0;
1079 }
1080
1081 /* We check if the extension exists, before creating the ast_channel struct */
1082 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))) {
1083 ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
1084 continue;
1085 }
1086
1087 tmpuser = ast_calloc(1, sizeof(*tmpuser));
1088 if (!tmpuser) {
1089 continue;
1090 }
1091
1092 if (ast_strlen_zero(tpargs->context)) {
1093 snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s%s",
1094 number,
1096 ? "/n" : "/m");
1097 } else {
1098 snprintf(tmpuser->dialarg, sizeof(tmpuser->dialarg), "%s@%s%s",
1099 number, tpargs->context,
1101 ? "/n" : "/m");
1102 }
1103
1104 /* Capture nativeformats reference in case it gets changed */
1105 ast_channel_lock(caller);
1106 caps = ao2_bump(ast_channel_nativeformats(caller));
1107 ast_channel_unlock(caller);
1108
1109 outbound = ast_request("Local", caps, NULL, caller, tmpuser->dialarg, &dg);
1110
1111 ao2_cleanup(caps);
1112
1113 if (!outbound) {
1114 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
1115 tmpuser->dialarg, ast_cause2str(dg));
1116 ast_free(tmpuser);
1117 continue;
1118 }
1119
1120 ast_channel_lock_both(caller, outbound);
1122 ast_channel_inherit_variables(caller, outbound);
1123 ast_channel_datastore_inherit(caller, outbound);
1125 ast_channel_language_set(outbound, ast_channel_language(caller));
1127 ast_channel_musicclass_set(outbound, ast_channel_musicclass(caller));
1128 ast_channel_unlock(outbound);
1129 ast_channel_unlock(caller);
1130
1131 tmpuser->ochan = outbound;
1132 tmpuser->state = 0;
1133 AST_LIST_INSERT_TAIL(&new_user_list, tmpuser, entry);
1134 }
1135
1136 /*
1137 * PREDIAL: Run gosub on all of the new callee channels
1138 *
1139 * We run the callee predial before ast_call() in case the user
1140 * wishes to do something on the newly created channels before
1141 * the channel does anything important.
1142 */
1143 if (tpargs->predial_callee && !AST_LIST_EMPTY(&new_user_list)) {
1144 /* Put caller into autoservice. */
1145 ast_autoservice_start(caller);
1146
1147 /* Run predial on all new outgoing calls. */
1148 AST_LIST_TRAVERSE(&new_user_list, tmpuser, entry) {
1149 ast_pre_call(tmpuser->ochan, tpargs->predial_callee);
1150 }
1151
1152 /* Take caller out of autoservice. */
1153 if (ast_autoservice_stop(caller)) {
1154 /*
1155 * Caller hungup.
1156 *
1157 * Destoy all new outgoing calls.
1158 */
1159 while ((tmpuser = AST_LIST_REMOVE_HEAD(&new_user_list, entry))) {
1160 destroy_calling_node(tmpuser);
1161 }
1162
1163 /* Take all active outgoing channels out of autoservice. */
1164 AST_LIST_TRAVERSE(&findme_user_list, tmpuser, entry) {
1165 if (tmpuser->ochan) {
1166 ast_autoservice_stop(tmpuser->ochan);
1167 }
1168 }
1169 break;
1170 }
1171 }
1172
1173 /* Start all new outgoing calls */
1174 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_user_list, tmpuser, entry) {
1175 ast_verb(3, "calling Local/%s\n", tmpuser->dialarg);
1176 if (ast_call(tmpuser->ochan, tmpuser->dialarg, 0)) {
1177 ast_verb(3, "couldn't reach at this number.\n");
1179
1180 /* Destroy this failed new outgoing call. */
1181 destroy_calling_node(tmpuser);
1182 continue;
1183 }
1184
1185 ast_channel_publish_dial(caller, tmpuser->ochan, tmpuser->dialarg, NULL);
1186 }
1188
1189 /* Take all active outgoing channels out of autoservice. */
1190 AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
1191 if (tmpuser->ochan && ast_autoservice_stop(tmpuser->ochan)) {
1192 /* Existing outgoing call hungup. */
1194 destroy_calling_node(tmpuser);
1195 }
1196 }
1198
1199 if (AST_LIST_EMPTY(&new_user_list)) {
1200 /* No new channels remain at this order level. If there were any at all. */
1201 continue;
1202 }
1203
1204 /* Add new outgoing channels to the findme list. */
1205 AST_LIST_APPEND_LIST(&findme_user_list, &new_user_list, entry);
1206
1207 winner = wait_for_winner(&findme_user_list, nm, caller, tpargs);
1208 if (!winner) {
1209 /* Remove all dead outgoing nodes. */
1210 AST_LIST_TRAVERSE_SAFE_BEGIN(&findme_user_list, tmpuser, entry) {
1211 if (!tmpuser->ochan) {
1213 destroy_calling_node(tmpuser);
1214 }
1215 }
1217 continue;
1218 }
1219
1220 /* Destroy losing calls up to the winner. The rest will be destroyed later. */
1221 while ((fmuser = AST_LIST_REMOVE_HEAD(&findme_user_list, entry))) {
1222 if (fmuser->ochan == winner) {
1223 /*
1224 * Pass any connected line info up.
1225 *
1226 * NOTE: This code must be in line with destroy_calling_node().
1227 */
1228 tpargs->connected_out = fmuser->connected;
1230 ast_free(fmuser);
1231 break;
1232 } else {
1233 /* Destroy losing call. */
1234 destroy_calling_node(fmuser);
1235 }
1236 }
1237 break;
1238 }
1239 destroy_calling_tree(&findme_user_list);
1240 return winner;
1241}
static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
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)
#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:6456
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition channel.h:1525
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:2989
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:2358
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:6429
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:6439
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:6349
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.
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.
int ast_max_forwards_decrement(struct ast_channel *chan)
Decrement the max forwards count for a particular channel.
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:4196
#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]
struct ast_party_connected_line connected
unsigned int pending_connected_update

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

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

References ast_free, AST_LIST_HEAD_INIT_NOLOCK, AST_LIST_REMOVE_HEAD, call_followme::blnumbers, number::entry, 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 329 of file app_followme.c.

330{
332 f->context[0] = '\0';
333 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
334 ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
335 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
343 if (activate) {
344 f->active = 1;
345 }
346}
static char connprompt[PATH_MAX]
static char sorryprompt[PATH_MAX]
static char callfromprompt[PATH_MAX]
static char takecall[MAX_YN_STRING]
static char nextindp[MAX_YN_STRING]
static char optionsprompt[PATH_MAX]
static char statusprompt[PATH_MAX]
static char plsholdprompt[PATH_MAX]
static char norecordingprompt[PATH_MAX]
static int enable_callee_prompt
static const char * defaultmoh

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

1641{
1642 if(!reload_followme(0))
1644
1646}
static const char app_exec[]
Definition app_exec.c:138
static int reload_followme(int reload)
Reload followme application module.
@ 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:640

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

352{
353
354 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
355 ast_copy_string(f->moh, val, sizeof(f->moh));
356 else if (!strcasecmp(param, "context"))
357 ast_copy_string(f->context, val, sizeof(f->context));
358 else if (!strcasecmp(param, "enable_callee_prompt"))
360 else if (!strcasecmp(param, "takecall"))
361 ast_copy_string(f->takecall, val, sizeof(f->takecall));
362 else if (!strcasecmp(param, "declinecall"))
363 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
364 else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
366 else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
368 else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
370 else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
372 else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
374 else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
376 else if (!strcasecmp(param, "followme-connecting-prompt") || !strcasecmp(param, "connecting_prompt")) {
377 ast_copy_string(f->connprompt, val, sizeof(f->connprompt));
378 } else if (failunknown) {
379 if (linenum >= 0)
380 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
381 else
382 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
383 }
384}
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:2235

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

596{
597 struct findme_user *tmpuser;
598
599 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
600 if (tmpuser->ochan && tmpuser->ochan != exception) {
602 }
603 }
604}
jack_status_t status
Definition app_jack.c:149
FILE * in
Definition utils/frame.c:33

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

Referenced by wait_for_winner().

◆ reload()

static int reload ( void  )
static

Definition at line 1648 of file app_followme.c.

1649{
1650 reload_followme(1);
1651
1652 return 0;
1653}

References reload_followme().

Referenced by reload_followme().

◆ reload_followme()

static int reload_followme ( int  reload)
static

Reload followme application module.

Definition at line 407 of file app_followme.c.

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

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, and var.

Referenced by load_module(), and reload().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1612 of file app_followme.c.

1613{
1614 struct call_followme *f;
1615
1617
1618 /* Free Memory. Yeah! I'm free! */
1620 while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
1621 free_numbers(f);
1622 ast_free(f);
1623 }
1624
1626
1627 return 0;
1628}
#define AST_RWLIST_REMOVE_HEAD
int ast_unregister_application(const char *app)
Unregister an application.
Definition pbx_app.c:392
struct call_followme::@26 entry

References app, ast_free, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_unregister_application(), call_followme::entry, 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 647 of file app_followme.c.

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

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

Definition at line 1660 of file app_followme.c.

◆ app

char* app = "FollowMe"
static

Definition at line 151 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 1660 of file app_followme.c.

◆ callfromprompt

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

Definition at line 279 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ connprompt

char connprompt[PATH_MAX] = ""
static

Definition at line 285 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 274 of file app_followme.c.

Referenced by init_profile().

◆ enable_callee_prompt

int enable_callee_prompt = 1
static

Definition at line 278 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ featuredigittimeout

int featuredigittimeout = 5000
static

Feature Digit Timeout

Definition at line 273 of file app_followme.c.

Referenced by reload_followme(), and wait_for_winner().

◆ featuredigittostr

const char* featuredigittostr
static

Definition at line 272 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 270 of file app_followme.c.

Referenced by app_exec().

◆ followmes

◆ nextindp

char nextindp[MAX_YN_STRING] = "2"
static

Definition at line 277 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ norecordingprompt

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

Definition at line 280 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ optionsprompt

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

Definition at line 281 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 282 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ sorryprompt

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

Definition at line 284 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ statusprompt

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

Definition at line 283 of file app_followme.c.

Referenced by init_profile(), and reload_followme().

◆ takecall

char takecall[MAX_YN_STRING] = "1"
static

Definition at line 276 of file app_followme.c.

Referenced by init_profile(), and reload_followme().