- This is an example of how to develop an app.
- Application Skeleton is an example of creating an application for Asterisk.
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) <Year>, <Your Name Here>
*
* <Your Name Here> <<Your Email Here>>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*
* Please follow coding guidelines
* https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
*/
/*! \file
*
* \brief Skeleton application
*
* \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
*
* This is a skeleton for development of an Asterisk application
* \ingroup applications
*/
/*! \li \ref app_skel.c uses configuration file \ref app_skel.conf
* \addtogroup configuration_file Configuration Files
*/
/*!
* \page app_skel.conf app_skel.conf
* \verbinclude app_skel.conf.sample
*/
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include <math.h> /* log10 */
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/config.h"
#include "asterisk/config_options.h"
#include "asterisk/say.h"
#include "asterisk/astobj2.h"
#include "asterisk/acl.h"
#include "asterisk/netsock2.h"
#include "asterisk/strings.h"
#include "asterisk/cli.h"
/*** DOCUMENTATION
<application name="SkelGuessNumber" language="en_US">
<synopsis>
An example number guessing game
</synopsis>
<syntax>
<parameter name="level" required="true"/>
<parameter name="options">
<optionlist>
<option name="c">
<para>The computer should cheat</para>
</option>
<option name="n">
<para>How many games to play before hanging up</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This simple number guessing application is a template to build other applications
from. It shows you the basic structure to create your own Asterisk applications.</para>
</description>
</application>
<configInfo name="app_skel" language="en_US">
<configFile name="app_skel.conf">
<configObject name="globals">
<synopsis>Options that apply globally to app_skel</synopsis>
<configOption name="games">
<synopsis>The number of games a single execution of SkelGuessNumber will play</synopsis>
</configOption>
<configOption name="cheat">
<synopsis>Should the computer cheat?</synopsis>
<description><para>If enabled, the computer will ignore winning guesses.</para></description>
</configOption>
</configObject>
<configObject name="sounds">
<synopsis>Prompts for SkelGuessNumber to play</synopsis>
<configOption name="prompt" default="please-enter-your&number&queue-less-than">
<synopsis>A prompt directing the user to enter a number less than the max number</synopsis>
</configOption>
<configOption name="wrong_guess" default="vm-pls-try-again">
<synopsis>The sound file to play when a wrong guess is made</synopsis>
</configOption>
<configOption name="right_guess" default="auth-thankyou">
<synopsis>The sound file to play when a correct guess is made</synopsis>
</configOption>
<configOption name="too_low">
<synopsis>The sound file to play when a guess is too low</synopsis>
</configOption>
<configOption name="too_high">
<synopsis>The sound file to play when a guess is too high</synopsis>
</configOption>
<configOption name="lose" default="vm-goodbye">
<synopsis>The sound file to play when a player loses</synopsis>
</configOption>
</configObject>
<configObject name="level">
<synopsis>Defined levels for the SkelGuessNumber game</synopsis>
<configOption name="max_number">
<synopsis>The maximum in the range of numbers to guess (1 is the implied minimum)</synopsis>
</configOption>
<configOption name="max_guesses">
<synopsis>The maximum number of guesses before a game is considered lost</synopsis>
</configOption>
</configObject>
</configFile>
</configInfo>
***/
static char *app = "SkelGuessNumber";
enum option_flags {
OPTION_CHEAT = (1 << 0),
OPTION_NUMGAMES = (1 << 1),
};
enum option_args {
OPTION_ARG_NUMGAMES,
/* This *must* be the last value in this enum! */
OPTION_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(app_opts,{
AST_APP_OPTION('c', OPTION_CHEAT),
AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
});
/*! \brief A structure to hold global configuration-related options */
struct skel_global_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(prompt); /*!< The comma-separated list of sounds to prompt to enter a number */
AST_STRING_FIELD(wrong); /*!< The comma-separated list of sounds to indicate a wrong guess */
AST_STRING_FIELD(right); /*!< The comma-separated list of sounds to indicate a right guess */
AST_STRING_FIELD(high); /*!< The comma-separated list of sounds to indicate a high guess */
AST_STRING_FIELD(low); /*!< The comma-separated list of sounds to indicate a low guess */
AST_STRING_FIELD(lose); /*!< The comma-separated list of sounds to indicate a lost game */
);
uint32_t num_games; /*!< The number of games to play before hanging up */
unsigned char cheat:1; /*!< Whether the computer can cheat or not */
};
/*! \brief A structure to maintain level state across reloads */
struct skel_level_state {
uint32_t wins; /*!< How many wins for this level */
uint32_t losses; /*!< How many losses for this level */
double avg_guesses; /*!< The average number of guesses to win for this level */
};
/*! \brief Object to hold level config information.
* \note This object should hold a reference to an object that holds state across reloads.
* The other fields are just examples of the kind of data that might be stored in an level.
*/
struct skel_level {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(name); /*!< The name of the level */
);
uint32_t max_num; /*!< The upper value on th range of numbers to guess */
uint32_t max_guesses; /*!< The maximum number of guesses before losing */
struct skel_level_state *state; /*!< A pointer to level state that must exist across all reloads */
};
/*! \brief Information about a currently running set of games
* \note Because we want to be able to show true running information about the games
* regardless of whether or not a reload has modified what the level looks like, it
* is important to either copy the information we need from the level to the
* current_game struct, or as we do here, store a reference to the level as it is for
* the running game.
*/
struct skel_current_game {
uint32_t total_games; /*! The total number of games for this call to the app */
uint32_t games_left; /*! How many games are left to play in this set */
uint32_t cheat; /*! Whether or not cheating was enabled for the game */
struct skel_level *level_info; /*! The level information for the running game */
};
/* Treat the levels as an array--there won't be many and this will maintain the order */
#define LEVEL_BUCKETS 1
/*! \brief A container that holds all config-related information
* \note This object should contain a pointer to structs for global data and containers for
* any levels that are configured. Objects of this type will be swapped out on reload. If an
* level needs to maintain state across reloads, it needs to allocate a refcounted object to
* hold that state and ensure that a reference is passed to that state when creating a new
* level for reload. */
struct skel_config {
struct skel_global_config *global;
struct ao2_container *levels;
};
/* Config Options API callbacks */
/*! \brief Allocate a skel_config to hold a snapshot of the complete results of parsing a config
* \internal
* \returns A void pointer to a newly allocated skel_config
*/
static void *skel_config_alloc(void);
/*! \brief Allocate a skel_level based on a category in a configuration file
* \param cat The category to base the level on
* \returns A void pointer to a newly allocated skel_level
*/
static void *skel_level_alloc(const char *cat);
/*! \brief Find a skel level in the specified container
* \note This function *does not* look for a skel_level in the active container. It is used
* internally by the Config Options code to check if an level has already been added to the
* container that will be swapped for the live container on a successul reload.
*
* \param tmp_container A non-active container to search for a level
* \param category The category associated with the level to check for
* \retval non-NULL The level from the container
* \retval NULL The level does not exist in the container
*/
static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
/*! \brief An aco_type structure to link the "general" category to the skel_global_config type */
static struct aco_type global_option = {
.type = ACO_GLOBAL,
.name = "globals",
.item_offset = offsetof(struct skel_config, global),
.category_match = ACO_WHITELIST_EXACT,
.category = "general",
};
struct aco_type *global_options[] = ACO_TYPES(&global_option);
/*! \brief An aco_type structure to link the "sounds" category to the skel_global_config type */
static struct aco_type sound_option = {
.type = ACO_GLOBAL,
.name = "sounds",
.item_offset = offsetof(struct skel_config, global),
.category_match = ACO_WHITELIST_EXACT,
.category = "sounds",
};
struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
static const char *level_categories[] = {
"general",
"sounds",
NULL,
};
/*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
static struct aco_type level_option = {
.type = ACO_ITEM,
.name = "level",
.category_match = ACO_BLACKLIST_ARRAY,
.category = (const char *)level_categories,
.item_alloc = skel_level_alloc,
.item_find = skel_level_find,
.item_offset = offsetof(struct skel_config, levels),
};
struct aco_type *level_options[] = ACO_TYPES(&level_option);
struct aco_file app_skel_conf = {
.filename = "app_skel.conf",
.types = ACO_TYPES(&global_option, &sound_option, &level_option),
};
/*! \brief A global object container that will contain the skel_config that gets swapped out on reloads */
static AO2_GLOBAL_OBJ_STATIC(globals);
/*! \brief The container of active games */
static struct ao2_container *games;
/*! \brief Register information about the configs being processed by this module */
CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
.files = ACO_FILES(&app_skel_conf),
);
static void skel_global_config_destructor(void *obj)
{
struct skel_global_config *global = obj;
ast_string_field_free_memory(global);
}
static void skel_game_destructor(void *obj)
{
struct skel_current_game *game = obj;
ao2_cleanup(game->level_info);
}
static void skel_state_destructor(void *obj)
{
return;
}
static struct skel_current_game *skel_game_alloc(struct skel_level *level)
{
struct skel_current_game *game;
if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
return NULL;
}
ao2_ref(level, +1);
game->level_info = level;
return game;
}
static void skel_level_destructor(void *obj)
{
struct skel_level *level = obj;
ast_string_field_free_memory(level);
ao2_cleanup(level->state);
}
static int skel_level_hash(const void *obj, const int flags)
{
const struct skel_level *level = obj;
const char *name = (flags & OBJ_KEY) ? obj : level->name;
return ast_str_case_hash(name);
}
static int skel_level_cmp(void *obj, void *arg, int flags)
{
struct skel_level *one = obj, *two = arg;
const char *match = (flags & OBJ_KEY) ? arg : two->name;
return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
}
/*! \brief A custom bitfield handler
* \internal
* \note It is not possible to take the address of a bitfield, therefor all
* bitfields in the config struct will have to use a custom handler
* \param opt The opaque config option
* \param var The ast_variable containing the option name and value
* \param obj The object registerd for this option type
* \retval 0 Success
* \retval non-zero Failure
*/
static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct skel_global_config *global = obj;
if (!strcasecmp(var->name, "cheat")) {
global->cheat = ast_true(var->value);
} else {
return -1;
}
return 0;
}
static void play_files_helper(struct ast_channel *chan, const char *prompts)
{
char *prompt, *rest = ast_strdupa(prompts);
ast_stopstream(chan);
while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
ast_stopstream(chan);
}
}
static int app_exec(struct ast_channel *chan, const char *data)
{
int win = 0;
uint32_t guesses;
RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
struct ast_flags flags;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(level);
AST_APP_ARG(options);
);
if (!cfg) {
ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
return -1;
}
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
return -1;
}
/* We need to make a copy of the input string if we are going to modify it! */
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc == 2) {
ast_app_parse_options(app_opts, &flags, opts, args.options);
}
if (ast_strlen_zero(args.level)) {
ast_log(LOG_ERROR, "%s requires a level argument\n", app);
return -1;
}
if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
return -1;
}
if (!(game = skel_game_alloc(level))) {
return -1;
}
ao2_link(games, game);
/* Use app-specified values, or the options specified in [general] if they aren't passed to the app */
if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
game->total_games = cfg->global->num_games;
}
game->games_left = game->total_games;
game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
for (game->games_left = game->total_games; game->games_left; game->games_left--) {
uint32_t num = ast_random() % level->max_num; /* random number between 0 and level->max_num */
ast_debug(1, "They should totally should guess %u\n", num);
/* Play the prompt */
play_files_helper(chan, cfg->global->prompt);
ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
for (guesses = 0; guesses < level->max_guesses; guesses++) {
size_t buflen = log10(level->max_num) + 1;
char buf[buflen];
int guess;
buf[buflen] = '\0';
/* Read the number pressed */
ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
if (guesses < level->max_guesses - 1) {
play_files_helper(chan, cfg->global->wrong);
}
continue;
}
/* Inform whether the guess was right, low, or high */
if (guess == num && !game->cheat) {
/* win */
win = 1;
play_files_helper(chan, cfg->global->right);
guesses++;
break;
} else if (guess < num) {
play_files_helper(chan, cfg->global->low);
} else {
play_files_helper(chan, cfg->global->high);
}
if (guesses < level->max_guesses - 1) {
play_files_helper(chan, cfg->global->wrong);
}
}
/* Process game stats */
ao2_lock(level->state);
if (win) {
++level->state->wins;
level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
} else {
/* lose */
level->state->losses++;
play_files_helper(chan, cfg->global->lose);
}
ao2_unlock(level->state);
}
ao2_unlink(games, game);
return 0;
}
static struct skel_level *skel_state_alloc(const char *name)
{
struct skel_level *level;
if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
return NULL;
}
return level;
}
static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
{
return ao2_find(tmp_container, category, OBJ_KEY);
}
/*! \brief Look up an existing state object, or create a new one
* \internal
* \note Since the reload code will create a new level from scratch, it
* is important for any state that must persist between reloads to be
* in a separate refcounted object. This function allows the level alloc
* function to get a ref to an existing state object if it exists,
* otherwise it will return a reference to a newly allocated state object.
*/
static void *skel_find_or_create_state(const char *category)
{
RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
return skel_state_alloc(category);
}
ao2_ref(level->state, +1);
return level->state;
}
static void *skel_level_alloc(const char *cat)
{
struct skel_level *level;
if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
return NULL;
}
if (ast_string_field_init(level, 128)) {
ao2_ref(level, -1);
return NULL;
}
/* Since the level has state information that needs to persist between reloads,
* it is important to handle that here in the level's allocation function.
* If not separated out into its own object, the data would be destroyed on
* reload. */
if (!(level->state = skel_find_or_create_state(cat))) {
ao2_ref(level, -1);
return NULL;
}
ast_string_field_set(level, name, cat);
return level;
}
static void skel_config_destructor(void *obj)
{
struct skel_config *cfg = obj;
ao2_cleanup(cfg->global);
ao2_cleanup(cfg->levels);
}
static void *skel_config_alloc(void)
{
struct skel_config *cfg;
if (!(cfg = ao2_alloc(sizeof(*cfg), skel_config_destructor))) {
return NULL;
}
/* Allocate/initialize memory */
if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
goto error;
}
if (ast_string_field_init(cfg->global, 128)) {
goto error;
}
cfg->levels = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, LEVEL_BUCKETS,
skel_level_hash, NULL, skel_level_cmp);
if (!cfg->levels) {
goto error;
}
return cfg;
error:
ao2_ref(cfg, -1);
return NULL;
}
static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
switch(cmd) {
case CLI_INIT:
e->command = "skel show config";
e->usage =
"Usage: skel show config\n"
" List app_skel global config\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
return NULL;
}
ast_cli(a->fd, "games per call: %u\n", cfg->global->num_games);
ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
ast_cli(a->fd, "\n");
ast_cli(a->fd, "Sounds\n");
ast_cli(a->fd, " prompt: %s\n", cfg->global->prompt);
ast_cli(a->fd, " wrong guess: %s\n", cfg->global->wrong);
ast_cli(a->fd, " right guess: %s\n", cfg->global->right);
return CLI_SUCCESS;
}
static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_iterator iter;
struct skel_current_game *game;
switch(cmd) {
case CLI_INIT:
e->command = "skel show games";
e->usage =
"Usage: skel show games\n"
" List app_skel active games\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
#define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
#define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
iter = ao2_iterator_init(games, 0);
while ((game = ao2_iterator_next(&iter))) {
ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
ao2_ref(game, -1);
}
ao2_iterator_destroy(&iter);
#undef SKEL_FORMAT
#undef SKEL_FORMAT1
return CLI_SUCCESS;
}
static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
struct ao2_iterator iter;
struct skel_level *level;
switch(cmd) {
case CLI_INIT:
e->command = "skel show levels";
e->usage =
"Usage: skel show levels\n"
" List the app_skel levels\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
return NULL;
}
#define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
#define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
iter = ao2_iterator_init(cfg->levels, 0);
while ((level = ao2_iterator_next(&iter))) {
ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
ao2_ref(level, -1);
}
ao2_iterator_destroy(&iter);
#undef SKEL_FORMAT
#undef SKEL_FORMAT1
return CLI_SUCCESS;
}
static struct ast_cli_entry skel_cli[] = {
AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
};
static int reload_module(void)
{
if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
return AST_MODULE_LOAD_DECLINE;
}
return 0;
}
static int unload_module(void)
{
ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
aco_info_destroy(&cfg_info);
ao2_global_obj_release(globals);
ao2_cleanup(games);
return ast_unregister_application(app);
}
/*!
* \brief 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.
*/
static int load_module(void)
{
if (aco_info_init(&cfg_info)) {
goto error;
}
games = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
if (!games) {
goto error;
}
/* Global options */
aco_option_register(&cfg_info, "games", ACO_EXACT, global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
aco_option_register_custom(&cfg_info, "cheat", ACO_EXACT, global_options, "no", custom_bitfield_handler, 0);
/* Sound options */
aco_option_register(&cfg_info, "prompt", ACO_EXACT, sound_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
aco_option_register(&cfg_info, "wrong_guess", ACO_EXACT, sound_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
aco_option_register(&cfg_info, "right_guess", ACO_EXACT, sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
aco_option_register(&cfg_info, "too_high", ACO_EXACT, sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
aco_option_register(&cfg_info, "too_low", ACO_EXACT, sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
aco_option_register(&cfg_info, "lose", ACO_EXACT, sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
/* Level options */
aco_option_register(&cfg_info, "max_number", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_guesses));
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
goto error;
}
ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
if (ast_register_application_xml(app, app_exec)) {
goto error;
}
return AST_MODULE_LOAD_SUCCESS;
error:
aco_info_destroy(&cfg_info);
ao2_cleanup(games);
return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skeleton (sample) Application",
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
.reload = reload_module,
);
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <regex.h>
#include <sys/file.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifndef HAVE_CLOSEFROM
#include <dirent.h>
#endif
#ifdef HAVE_CAP
#include <sys/capability.h>
#endif
};
#ifdef HAVE_CAP
static cap_t child_cap;
#endif
{
for (;;) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
NULL);
if (waitpid(cur->
pid, &status, WNOHANG) != 0) {
}
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,
NULL);
}
pthread_testcancel();
}
}
#define AST_MAX_FORMATS 10
{
int res = 0, x = 0;
if (maxlen > size) {
maxlen = size;
}
if (!timeout) {
} else {
timeout = 5000;
}
}
} else {
}
for (x = strlen(collect); x < maxlen; ) {
}
if (res < 1) {
break;
}
if (res == '#') {
break;
}
collect[x++] = res;
break;
}
}
if (res >= 0) {
}
return res;
}
{
int res = 0, to, fto;
char *front, *filename;
if (maxlen)
s[0] = '\0';
if (!prompt)
prompt = "";
while ((front =
strsep(&filename,
"&"))) {
if (res)
continue;
}
if (timeout > 0) {
}
if (timeout < 0) {
fto = to = 1000000000;
}
} else {
fto = 50;
}
return res;
}
return res;
}
}
return res;
}
{
int res, to = 2000, fto = 6000;
if (res < 0) {
return res;
}
}
if (timeout > 0) {
}
if (timeout < 0) {
fto = to = 1000000000;
}
return res;
}
{
int res;
if (!macro_app) {
"Cannot run 'Macro(%s)'. The application is not available.\n", macro_args);
return -1;
}
if (autoservice_chan) {
}
res =
pbx_exec(macro_chan, macro_app, macro_args);
ast_debug(4,
"Macro exited with status %d\n", res);
if (res < 0) {
res = -1;
} else {
res = 0;
}
if (autoservice_chan) {
}
}
return res;
}
{
int res;
char *args_str;
size_t args_len;
}
args_len = strlen(macro_name) + strlen(macro_args) + 2;
if (!args_str) {
return -1;
}
snprintf(args_str, args_len, "%s,%s", macro_name, macro_args);
return res;
}
{
app_stack_callbacks = funcs;
}
{
const char *new_args;
"Cannot expand 'Gosub(%s)' arguments. The app_stack module is not available.\n",
args);
}
return new_args;
}
{
int res;
"Cannot run 'Gosub(%s)'. The app_stack module is not available.\n",
sub_args);
return -1;
}
if (autoservice_chan) {
}
res = funcs->
run_sub(sub_chan, sub_args, ignore_hangup);
if (autoservice_chan) {
}
}
return res;
}
{
int res;
char *args_str;
size_t args_len;
return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location, ignore_hangup);
}
args_len = strlen(sub_location) + strlen(sub_args) + 3;
if (!args_str) {
return -1;
}
snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args);
return res;
}
{
int is_registered;
is_registered = table ? 1 : 0;
return is_registered;
}
{
return -1;
}
return -1;
}
}
return -1;
}
return 0;
}
{
if (table && !strcmp(table->
module_name, module_name)) {
}
}
#ifdef TEST_FRAMEWORK
static int provider_is_swapped = 0;
{
if (provider_is_swapped) {
ast_log(
LOG_ERROR,
"Attempted to swap in test function table without swapping out old test table.\n");
return;
}
if (holding_table) {
}
if (!new_table) {
return;
}
provider_is_swapped = 1;
}
void ast_vm_test_swap_table_out(void)
{
if (!provider_is_swapped) {
ast_log(
LOG_ERROR,
"Attempted to swap out test function table, but none is currently installed.\n");
return;
}
if (!held_table) {
return;
}
provider_is_swapped = 0;
}
#endif
{
int is_registered;
is_registered = table ? 1 : 0;
return is_registered;
}
{
ast_log(
LOG_ERROR,
"Voicemail greeter provider missing required information.\n");
return -1;
}
return -1;
}
if (table) {
}
if (!table) {
return -1;
}
return 0;
}
{
if (table && !strcmp(table->
module_name, module_name)) {
}
}
#ifdef TEST_FRAMEWORK
static ast_vm_test_create_user_fn *ast_vm_test_create_user_func =
NULL;
static ast_vm_test_destroy_user_fn *ast_vm_test_destroy_user_func =
NULL;
void ast_install_vm_test_functions(ast_vm_test_create_user_fn *vm_test_create_user_func,
ast_vm_test_destroy_user_fn *vm_test_destroy_user_func)
{
ast_vm_test_create_user_func = vm_test_create_user_func;
ast_vm_test_destroy_user_func = vm_test_destroy_user_func;
}
void ast_uninstall_vm_test_functions(void)
{
ast_vm_test_create_user_func =
NULL;
ast_vm_test_destroy_user_func =
NULL;
}
#endif
{
if (vm_warnings++ % 10 == 0) {
ast_verb(3,
"No voicemail provider registered.\n");
}
}
#define VM_API_CALL(res, api_call, api_parms) \
do { \
struct ast_vm_functions *table; \
table = ao2_global_obj_ref(vm_provider); \
if (!table) { \
vm_warn_no_provider(); \
} else if (table->api_call) { \
ast_module_ref(table->module); \
(res) = table->api_call api_parms; \
ast_module_unref(table->module); \
} \
ao2_cleanup(table); \
} while (0)
{
if (vm_greeter_warnings++ % 10 == 0) {
ast_verb(3,
"No voicemail greeter provider registered.\n");
}
}
#define VM_GREETER_API_CALL(res, api_call, api_parms) \
do { \
struct ast_vm_greeter_functions *table; \
table = ao2_global_obj_ref(vm_greeter_provider); \
if (!table) { \
vm_greeter_warn_no_provider(); \
} else if (table->api_call) { \
ast_module_ref(table->module); \
(res) = table->api_call api_parms; \
ast_module_unref(table->module); \
} \
ao2_cleanup(table); \
} while (0)
{
int res = 0;
return res;
}
{
int res = -1;
return res;
}
{
int res = 0;
if (newmsgs) {
*newmsgs = 0;
}
if (oldmsgs) {
*oldmsgs = 0;
}
return res;
}
{
int res = 0;
if (newmsgs) {
*newmsgs = 0;
}
if (oldmsgs) {
*oldmsgs = 0;
}
if (urgentmsgs) {
*urgentmsgs = 0;
}
return res;
}
{
int res = -1;
return res;
}
{
int res = 0;
return res;
}
{
return res;
}
const char *context,
const char *folder,
int descending,
int combine_INBOX_and_OLD)
{
VM_API_CALL(res, mailbox_snapshot_create, (mailbox, context, folder, descending,
sort_val, combine_INBOX_and_OLD));
return res;
}
{
VM_API_CALL(res, mailbox_snapshot_destroy, (mailbox_snapshot));
return res;
}
const char *context,
size_t num_msgs,
const char *oldfolder,
const char *old_msg_ids[],
const char *newfolder)
{
int res = 0;
VM_API_CALL(res, msg_move, (mailbox, context, num_msgs, oldfolder, old_msg_ids,
newfolder));
return res;
}
const char *context,
size_t num_msgs,
const char *folder,
const char *msgs[])
{
int res = 0;
VM_API_CALL(res, msg_remove, (mailbox, context, num_msgs, folder, msgs));
return res;
}
const char *from_context,
const char *from_folder,
const char *to_mailbox,
const char *to_context,
const char *to_folder,
size_t num_msgs,
const char *msg_ids[],
int delete_old)
{
int res = 0;
VM_API_CALL(res, msg_forward, (from_mailbox, from_context, from_folder, to_mailbox,
to_context, to_folder, num_msgs, msg_ids, delete_old));
return res;
}
const char *mailbox,
const char *context,
const char *folder,
const char *msg_num,
{
int res = 0;
VM_API_CALL(res, msg_play, (chan, mailbox, context, folder, msg_num, cb));
return res;
}
#ifdef TEST_FRAMEWORK
int ast_vm_test_create_user(const char *context, const char *mailbox)
{
if (ast_vm_test_create_user_func) {
return ast_vm_test_create_user_func(context, mailbox);
}
return 0;
}
int ast_vm_test_destroy_user(const char *context, const char *mailbox)
{
if (ast_vm_test_destroy_user_func) {
return ast_vm_test_destroy_user_func(context, mailbox);
}
return 0;
}
#endif
{
usleep(ms * 1000);
return 0;
}
static int dtmf_stream(
struct ast_channel *chan,
const char *digits,
int between,
unsigned int duration,
int is_external)
{
const char *ptr;
int res;
int (*my_senddigit)(
struct ast_channel *chan,
char digit,
unsigned int duration);
if (is_external) {
} else {
}
if (!between) {
between = 100;
}
}
res = my_sleep(chan, 100);
if (res) {
goto dtmf_stream_cleanup;
}
for (ptr = digits; *ptr; ptr++) {
if (*ptr == 'w') {
res = my_sleep(chan, 500);
if (res) {
break;
}
} else if (*ptr == 'W') {
res = my_sleep(chan, 1000);
if (res) {
break;
}
} else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
if (*ptr == 'f' || *ptr == 'F') {
} else {
my_senddigit(chan, *ptr, duration);
}
res = my_sleep(chan, between);
if (res) {
break;
}
} else {
ast_log(
LOG_WARNING,
"Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n", *ptr);
}
}
dtmf_stream_cleanup:
if (silgen) {
}
return res;
}
{
int res;
return -1;
}
res = -1;
}
return res;
}
{
}
int fd;
int autoclose;
int allowoverride;
};
{
}
}
}
{
};
int res;
len = samples * 2;
}
if (res > 0) {
if (res == len) {
return 0;
}
}
return -1;
}
{
if (!params) {
}
} else {
}
}
return params;
}
{
};
{
char tmpf[256];
if (fd < 0) {
return -1;
}
autoclose = 1;
if (filename[0] == '/') {
} else {
}
fd = open(tmpf, O_RDONLY);
if (fd < 0) {
return -1;
}
}
if (!lin) {
if (autoclose) {
close(fd);
}
return -1;
}
}
const char *fwd,
const char *rev,
const char *restart,
long *offsetms,
const char *lang,
{
int blen = 2;
int res;
long pause_restart_point = 0;
long offset = 0;
if (!file) {
return -1;
}
if (offsetms) {
offset = *offsetms * 8;
}
}
if (stop) {
blen += strlen(stop);
}
if (suspend) {
blen += strlen(suspend);
}
if (restart) {
blen += strlen(restart);
}
if (blen > 2) {
breaks[0] = '\0';
if (stop) {
strcat(breaks, stop);
}
if (suspend) {
strcat(breaks, suspend);
}
if (restart) {
strcat(breaks, restart);
}
}
if ((end = strchr(file, ':'))) {
if (!strcasecmp(end, ":end")) {
*end = '\0';
end++;
} else {
}
}
for (;;) {
if (!res) {
if (pause_restart_point) {
pause_restart_point = 0;
}
else if (end || offset < 0) {
if (offset == -8) {
offset = 0;
}
ast_verb(3,
"ControlPlayback seek to offset %ld from end\n", offset);
offset = 0;
} else if (offset) {
ast_verb(3,
"ControlPlayback seek to offset %ld\n", offset);
offset = 0;
}
if (cb) {
} else {
}
}
if (res < 1) {
break;
}
ast_debug(1,
"we'll restart the stream here at next loop\n");
pause_restart_point = 0;
"Control: %s\r\n",
"Restart");
continue;
}
}
"Control: %s\r\n",
"Pause");
for (;;) {
continue;
} else if (res == -1 || (suspend && strchr(suspend, res)) || (stop && strchr(stop, res))
break;
}
}
if (silgen) {
}
res = 0;
"Control: %s\r\n",
"Unpause");
continue;
}
}
if (res == -1) {
break;
}
"Control: %s\r\n",
"Stop");
break;
}
}
if (pause_restart_point) {
offset = pause_restart_point;
} else {
} else {
offset = -8;
}
}
if (offsetms) {
*offsetms = offset / 8;
}
return res;
}
const char *file,
const char *fwd,
const char *rev,
const char *stop,
const char *suspend,
const char *restart,
int skipms,
long *offsetms,
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms,
NULL, cb);
}
const char *fwd, const char *rev,
const char *stop, const char *suspend,
const char *restart, int skipms, long *offsetms)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms,
NULL,
NULL);
}
const char *fwd, const char *rev, const char *stop, const char *suspend,
const char *restart, int skipms, const char *lang, long *offsetms)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, lang,
NULL);
}
};
{
if (*paused) {
*paused = 0;
}
} else {
*paused = 1;
}
}
ast_log(
LOG_NOTICE,
"Media control operation 'reverse' not supported for media type 'tone'\n");
ast_log(
LOG_NOTICE,
"Media control operation 'forward' not supported for media type 'tone'\n");
}
}
const char **tone_indication,
const char **tone_zone)
{
*tone_indication =
strsep(&tone_parser,
";");
return 0;
}
if (!(strncmp(tone_parser, "tonezone=", 9))) {
*tone_zone = tone_parser + 9;
} else {
return -1;
}
return 0;
}
{
int paused = 0;
int res = 0;
const char *tone_indication =
NULL;
const char *tone_zone =
NULL;
char *tone_uri_parser;
return -1;
}
return -1;
}
if (tone_zone) {
}
res = -1;
}
while (!res) {
res = -1;
break;
}
if (!fr) {
res = -1;
break;
}
continue;
}
res = 0;
break;
res = -1;
break;
}
}
if (ts) {
}
if (zone) {
}
return res;
}
{
}
}
{
size_t size;
size_t samples = 0;
if (!orig) {
}
do {
}
} while (orig);
ast_verb(4,
"Silencing %zu samples\n", samples);
datalen = sizeof(short) * samples;
if (!silence) {
}
silence->
data.
ptr = (
void *)(silence + 1);
return silence;
}
{
if (!chan || !orig_format) {
return -1;
}
}
static int __ast_play_and_record(
struct ast_channel *chan,
const char *playfile,
const char *recordfile,
int maxtime,
const char *fmt,
int *duration,
int *sound_duration,
int beep,
int silencethreshold,
int maxsilence,
const char *path,
int prepend,
const char *acceptdtmf,
const char *canceldtmf,
int skip_confirmation_sound,
enum ast_record_if_exists if_exists)
{
int d = 0;
char *fmts;
int x, fmtcnt = 1, res = -1, outmsg = 0;
int dspsilence = 0;
int olddspsilence = 0;
int ioflags;
ioflags = O_CREAT|O_WRONLY;
switch (if_exists) {
ioflags |= O_EXCL;
break;
ioflags |= O_TRUNC;
break;
ioflags |= O_APPEND;
break;
break;
}
if (silencethreshold < 0) {
}
if (maxsilence < 0) {
}
if (!duration) {
return -1;
}
ast_debug(1,
"play_and_record: %s, %s, '%s'\n", playfile ? playfile :
"<None>", recordfile, fmt);
snprintf(comment,
sizeof(comment),
"Playing %s, Recording to: %s on %s\n", playfile ? playfile :
"<None>", recordfile,
ast_channel_name(chan));
if (playfile || beep) {
if (!beep) {
}
if (d > -1) {
}
if (d < 0) {
return -1;
}
}
if (prepend) {
strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
}
stringp = fmts;
ast_debug(1,
"Recording Formats: sfmts=%s\n", fmts);
while ((fmt =
strsep(&stringp,
"|"))) {
break;
}
sfmt[fmtcnt++] = fmt;
}
end = start = time(
NULL);
for (x = 0; x < fmtcnt; x++) {
ast_verb(3,
"x=%d, open writing: %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
if (!others[x]) {
break;
}
}
if (path) {
}
if (maxsilence > 0) {
if (!sildet) {
return -1;
}
if (res < 0) {
return -1;
}
}
if (!prepend) {
}
}
if (x == fmtcnt) {
int paused = 0;
time_t pause_start = 0;
int paused_secs = 0;
int pausedsilence = 0;
for (;;) {
ast_debug(1,
"One waitfor failed, trying another\n");
res = -1;
}
}
if (res < 0) {
break;
}
break;
}
if (paused) {
res = 0;
} else {
if (muted) {
if (!silence) {
break;
}
f = silence;
}
for (x = 0; x < fmtcnt; x++) {
if (prepend && !others[x]) {
break;
}
}
f = orig;
}
if (maxsilence > 0) {
dspsilence = 0;
if (olddspsilence > dspsilence) {
totalsilence += olddspsilence;
}
olddspsilence = dspsilence;
if (paused) {
pausedsilence = dspsilence;
} else if (dspsilence > pausedsilence) {
dspsilence -= pausedsilence;
} else {
pausedsilence = 0;
}
if (dspsilence > maxsilence) {
ast_verb(3,
"Recording automatically stopped after a silence of %d seconds\n", dspsilence/1000);
res = 'S';
outmsg = 2;
break;
}
}
if (res) {
break;
}
if (prepend) {
res = 't';
outmsg = 2;
break;
}
outmsg = 2;
break;
}
outmsg = 0;
break;
}
ast_verb(3,
"Message canceled by control\n");
outmsg = 0;
res = 0;
break;
ast_verb(3,
"Message ended by control\n");
res = 0;
break;
paused = !paused;
ast_verb(3,
"Message %spaused by control\n",
paused ? "" : "un");
if (paused) {
pause_start = time(
NULL);
} else {
paused_secs += time(
NULL) - pause_start;
}
ast_verb(3,
"Message %smuted by control\n",
muted ? "" : "un");
if (muted && !rfmt) {
ast_verb(3,
"Setting read format to linear mode\n");
if (res < 0) {
break;
}
}
}
}
if (maxtime && !paused) {
if (maxtime < (end - start - paused_secs)) {
ast_verb(3,
"Took too long, cutting it short...\n");
res = 't';
outmsg = 2;
break;
}
}
}
if (!f) {
res = -1;
outmsg = 1;
} else {
}
} else {
ast_log(
LOG_WARNING,
"Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
}
if (!prepend) {
if (silgen) {
}
}
if (sound_duration) {
*sound_duration = *duration;
}
if (!prepend) {
if (olddspsilence <= dspsilence) {
totalsilence += dspsilence;
}
if (sound_duration) {
if (totalsilence > 0) {
*sound_duration -= (totalsilence - 200) / 1000;
}
if (*sound_duration < 0) {
*sound_duration = 0;
}
}
if (dspsilence > 0) {
*duration -= (dspsilence - 200) / 1000;
}
if (*duration < 0) {
*duration = 0;
}
for (x = 0; x < fmtcnt; x++) {
if (!others[x]) {
break;
}
if (res > 0 && dspsilence) {
}
}
} else if (prepend && outmsg) {
for (x = 0; x < fmtcnt; x++) {
snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
realfiles[x] =
ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
if (!others[x]) {
break;
}
if (!realfiles[x]) {
continue;
}
if (dspsilence) {
}
}
ast_verb(4,
"Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
}
} else {
for (x = 0; x < fmtcnt; x++) {
if (!others[x]) {
break;
}
}
}
}
if ((outmsg == 2) && (!skip_confirmation_sound)) {
}
if (sildet) {
}
return res;
}
int ast_play_and_record_full(
struct ast_channel *chan,
const char *playfile,
const char *recordfile,
int maxtime,
const char *fmt,
int *duration,
int *sound_duration,
int beep,
int silencethreshold,
int maxsilence,
const char *path,
const char *acceptdtmf,
const char *canceldtmf,
int skip_confirmation_sound,
enum ast_record_if_exists if_exists)
{
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, path, 0,
S_OR(acceptdtmf,
""),
S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists);
}
int ast_play_and_record(
struct ast_channel *chan,
const char *playfile,
const char *recordfile,
int maxtime,
const char *fmt,
int *duration,
int *sound_duration,
int silencethreshold,
int maxsilence,
const char *path)
{
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf, 0,
AST_RECORD_IF_EXISTS_OVERWRITE);
}
int ast_play_and_prepend(
struct ast_channel *chan,
char *playfile,
char *recordfile,
int maxtime,
char *fmt,
int *duration,
int *sound_duration,
int beep,
int silencethreshold,
int maxsilence)
{
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence,
NULL, 1, default_acceptdtmf, default_canceldtmf, 1,
AST_RECORD_IF_EXISTS_OVERWRITE);
}
{
int res = 0;
if ((cat = strchr(tmp, '@'))) {
*cat++ = '\0';
}
}
} else {
*group = '\0';
}
}
return res;
}
{
int res = 0;
char group[80] = "", category[80] = "";
size_t len = 0;
return -1;
}
len = sizeof(*gi) + strlen(group) + 1;
len += strlen(category) + 1;
}
break;
}
}
gi->
group = (
char *) gi +
sizeof(*gi);
strcpy(gi->
group, group);
gi->
category = (
char *) gi +
sizeof(*gi) + strlen(group) + 1;
}
} else {
res = -1;
}
return res;
}
{
int count = 0;
return 0;
}
count++;
}
}
return count;
}
{
regex_t regexbuf_group;
regex_t regexbuf_category;
int count = 0;
return 0;
}
if (regcomp(®exbuf_group, groupmatch, REG_EXTENDED | REG_NOSUB)) {
return 0;
}
if (!
ast_strlen_zero(category) && regcomp(®exbuf_category, category, REG_EXTENDED | REG_NOSUB)) {
regfree(®exbuf_group);
return 0;
}
count++;
}
}
regfree(®exbuf_group);
regfree(®exbuf_category);
}
return count;
}
{
}
else if (gi->
chan ==
new) {
}
}
return 0;
}
{
}
}
return 0;
}
{
}
{
}
{
}
{
}
{
int argc;
char *scan, *wasdelim =
NULL;
if (!array || !arraylen) {
return 0;
}
memset(array, 0, arraylen * sizeof(*array));
if (!buf) {
return 0;
}
for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
array[argc] = scan;
for (; *scan; scan++) {
if (*scan == '(') {
paren++;
} else if (*scan == ')') {
if (paren) {
paren--;
}
} else if (*scan == '[') {
bracket++;
} else if (*scan == ']') {
if (bracket) {
bracket--;
}
} else if (*scan == '"' && delim != '"') {
if (remove_chars) {
memmove(scan, scan + 1, strlen(scan));
scan--;
}
} else if (*scan == '\\') {
if (remove_chars) {
memmove(scan, scan + 1, strlen(scan));
} else {
scan++;
}
}
else if ((*scan == delim) && !paren && !
quote && !bracket) {
wasdelim = scan;
*scan++ = '\0';
break;
}
}
}
if (*scan || (scan > buf && (scan - 1) == wasdelim)) {
array[argc++] = scan;
}
return argc;
}
{
char *s;
char *fs;
int res;
int fd;
int lp = strlen(path);
time_t start;
snprintf(fs, strlen(path) + 19,
"%s/.lock-%08lx", path, (
unsigned long)
ast_random());
if (fd < 0) {
}
close(fd);
snprintf(s, strlen(path) + 9, "%s/.lock", path);
while (((res = link(fs, s)) < 0) && (
errno == EEXIST) && (time(
NULL) - start < 5)) {
sched_yield();
}
unlink(fs);
if (res) {
} else {
}
}
{
char *s;
int res;
snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
if ((res = unlink(s))) {
} else {
}
return res;
}
int fd;
char *path;
};
{
}
}
}
{
char *fs;
int res;
int fd;
time_t start;
struct stat st, ost;
snprintf(fs, strlen(path) + 19, "%s/lock", path);
if (lstat(fs, &st) == 0) {
if ((st.st_mode & S_IFMT) == S_IFLNK) {
"'%s': it's already a symbolic link\n",
fs);
}
if (st.st_nlink > 1) {
"'%s': %u hard links exist\n",
fs, (unsigned int) st.st_nlink);
}
}
if ((fd = open(fs, O_WRONLY | O_CREAT, 0600)) < 0) {
}
close(fd);
}
time(&start);
while (
#ifdef SOLARIS
((res = fcntl(pl->
fd, F_SETLK, fcntl(pl->
fd, F_GETFL) | O_NONBLOCK)) < 0) &&
#else
((res = flock(pl->
fd, LOCK_EX | LOCK_NB)) < 0) &&
#endif
(
errno == EWOULDBLOCK) &&
(time(
NULL) - start < 5))
usleep(1000);
if (res) {
}
if (lstat(fs, &st) != 0 && fstat(pl->
fd, &ost) != 0 &&
st.st_dev != ost.st_dev &&
st.st_ino != ost.st_ino) {
"file changed underneath us\n", fs);
}
}
{
char *s;
if (!strcmp(p->
path, path)) {
break;
}
}
if (p) {
snprintf(s, strlen(path) + 19, "%s/lock", path);
unlink(s);
} else {
"lock not found\n", path);
}
return 0;
}
{
}
{
switch (ast_lock_type) {
break;
break;
}
return r;
}
{
int r = 0;
switch (ast_lock_type) {
break;
break;
}
return r;
}
int ast_record_review(
struct ast_channel *chan,
const char *playfile,
const char *recordfile,
int maxtime,
const char *fmt,
int *duration,
const char *path)
{
int maxsilence = 0;
int res = 0;
int cmd = 0;
int max_attempts = 3;
int attempts = 0;
int recorded = 0;
int message_exists = 0;
if (!duration) {
return -1;
}
cmd = '3';
while ((cmd >= 0) && (cmd != 't')) {
switch (cmd) {
case '1':
if (!message_exists) {
cmd = '3';
break;
} else {
cmd = 't';
return res;
}
case '2':
ast_verb(3,
"Reviewing the recording\n");
break;
case '3':
message_exists = 0;
ast_verb(3,
"R%secording\n", recorded == 1 ?
"e-r" :
"");
recorded = 1;
if ((cmd =
ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration,
NULL, silencethreshold, maxsilence, path)) == -1) {
return cmd;
}
if (cmd == '0') {
break;
} else if (cmd == '*') {
break;
} else {
message_exists = 1;
cmd = 0;
}
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '*':
case '#':
break;
default:
if (message_exists) {
} else {
}
}
if (!cmd) {
}
if (!cmd) {
attempts++;
}
if (attempts > max_attempts) {
cmd = 't';
}
}
}
if (cmd == 't') {
cmd = 0;
}
return cmd;
}
#define RES_UPONE (1 << 16)
#define RES_EXIT (1 << 17)
#define RES_REPEAT (1 << 18)
#define RES_RESTART ((1 << 19) | RES_REPEAT)
{
int res;
char *n;
return 0;
if (res < 0) {
res = 0;
}
return res;
if (res < 0) {
res = 0;
}
return res;
res = 0;
}
return res;
return 't';
}
return res;
ivr_func = option->
adata;
res = ivr_func(chan, cbdata);
return res;
return 0;
res = 0;
while ((n =
strsep(&c,
";"))) {
break;
}
}
return res;
default:
return 0;
}
return -1;
}
{
}
}
return -1;
}
{
if ((!strncasecmp(menu->
options[x].
option, option, strlen(option))) &&
}
}
return -1;
}
{
int res = 0;
int ms;
if (strlen(exten) >= maxexten - 1) {
break;
}
break;
}
exten[strlen(exten) + 1] = '\0';
exten[strlen(exten)] = res;
}
return res > 0 ? 0 : res;
}
{
int res = 0;
int pos = 0;
int retries = 0;
strcpy(exten, "g");
return -1;
}
}
while (!res) {
ast_debug(1,
"IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
if (res < 0) {
break;
return 0;
return res;
retries = 0;
} else {
retries++;
}
if (!maxretries) {
maxretries = 3;
}
if ((maxretries > 0) && (retries >= maxretries)) {
ast_debug(1,
"Max retries %d exceeded\n", maxretries);
return -2;
} else {
strcpy(exten, "g");
strcpy(exten, "s");
}
}
pos = 0;
continue;
ast_debug(1,
"Got start of extension, %c\n", res);
exten[1] = '\0';
exten[0] = res;
break;
}
ast_debug(1,
"Invalid extension entered, going to 'i'!\n");
strcpy(exten, "i");
pos = 0;
continue;
} else {
ast_debug(1,
"Aborting on invalid entry, with no 'i' option!\n");
res = -2;
break;
}
} else {
ast_debug(1,
"New existing extension: %s\n", exten);
pos = 0;
continue;
}
}
}
pos++;
}
ast_debug(1,
"Stopping option '%s', res is %d\n", exten, res);
pos = 0;
if (!strcasecmp(exten, "s")) {
strcpy(exten, "g");
} else {
break;
}
}
return res;
}
{
return res > 0 ? 0 : res;
}
{
int fd, count = 0, res;
struct stat filesize;
if (stat(filename, &filesize) == -1) {
}
count = filesize.st_size + 1;
if ((fd = open(filename, O_RDONLY)) < 0) {
}
res = read(fd, output, count - 1);
if (res == count - 1) {
output[res] = '\0';
} else {
}
}
close(fd);
return output;
}
{
char *s, *arg;
int curarg, res = 0;
unsigned int argloc;
if (flaglen == 32) {
} else {
}
if (!optstr) {
return 0;
}
s = optstr;
while (*s) {
curarg = *s++ & 0x7f;
if (*s == '(') {
int paren = 1,
quote = 0;
int parsequotes = (s[1] == '"') ? 1 : 0;
arg = ++s;
for (; *s; s++) {
if (*s ==
'(' && !
quote) {
paren++;
}
else if (*s ==
')' && !
quote) {
paren--;
} else if (*s == '"' && parsequotes) {
s--;
} else if (*s == '\\') {
}
else if (
quote && s[1] ==
'"') {
} else {
s++;
}
}
if (paren == 0) {
break;
}
}
if ((s = strchr(s, ')'))) {
if (argloc) {
args[argloc - 1] = arg;
}
*s++ = '\0';
} else {
ast_log(
LOG_WARNING,
"Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
res = -1;
break;
}
} else if (argloc) {
args[argloc - 1] = "";
}
if (flaglen == 32) {
} else {
}
}
return res;
}
{
}
{
}
{
unsigned int i, found = 0;
for (i = 32; i < 128 && found <
len; i++) {
buf[found++] = i;
}
}
buf[found] = '\0';
}
{
int i;
*consumed = 1;
*result = 0;
*consumed = 0;
return -1;
}
if (*stream == '\\') {
*consumed = 2;
switch (*(stream + 1)) {
case 'n':
*result = '\n';
break;
case 'r':
*result = '\r';
break;
case 't':
*result = '\t';
break;
case 'x':
if (strchr("0123456789ABCDEFabcdef", *(stream + 2)) && *(stream + 2) != '\0') {
*consumed = 3;
if (*(stream + 2) <= '9') {
*result = *(stream + 2) - '0';
} else if (*(stream + 2) <= 'F') {
*result = *(stream + 2) - 'A' + 10;
} else {
*result = *(stream + 2) - 'a' + 10;
}
} else {
ast_log(
LOG_ERROR,
"Illegal character '%c' in hexadecimal string\n", *(stream + 2));
return -1;
}
if (strchr("0123456789ABCDEFabcdef", *(stream + 3)) && *(stream + 3) != '\0') {
*consumed = 4;
*result <<= 4;
if (*(stream + 3) <= '9') {
*result += *(stream + 3) - '0';
} else if (*(stream + 3) <= 'F') {
*result += *(stream + 3) - 'A' + 10;
} else {
*result += *(stream + 3) - 'a' + 10;
}
}
break;
case '0':
*consumed = 2;
for (i = 2; ; i++) {
if (strchr("01234567", *(stream + i)) && *(stream + i) != '\0') {
(*consumed)++;
*result <<= 3;
*result += *(stream + i) - '0';
} else {
break;
}
}
break;
default:
*result = *(stream + 1);
}
} else {
*result = *stream;
*consumed = 1;
}
return 0;
}
{
size_t consumed;
cur++;
stream += consumed;
}
*cur = '\0';
}
{
size_t offset = 0;
size_t consumed;
if (strchr(stream, '\\')) {
}
break;
}
stream += consumed;
}
buf[offset++] = '\0';
} else {
}
return 0;
}
{
}
{
sigset_t signal_set, old_set;
if (stop_reaper) {
}
(void) sigfillset(&signal_set);
pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
pid = fork();
if (pid != 0) {
pthread_sigmask(SIG_SETMASK, &old_set,
NULL);
if (!stop_reaper && pid > 0) {
if (cur) {
}
}
}
}
} else {
#ifdef HAVE_CAP
cap_set_proc(child_cap);
#endif
signal(SIGHUP, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGURG, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
signal(SIGXFSZ, SIG_DFL);
if (pthread_sigmask(SIG_UNBLOCK, &signal_set,
NULL)) {
_exit(1);
}
}
}
{
}
{
int res;
char u[10];
#ifdef HAVE_LONG_DOUBLE_WIDER
long double amount;
#define FMT "%30Lf%9s"
#else
double amount;
#define FMT "%30lf%9s"
#endif
if (!timestr) {
return -1;
}
res = sscanf(timestr,
FMT, &amount, u);
if (res == 0 || res == EOF) {
#undef FMT
return -1;
} else if (res == 2) {
switch (u[0]) {
case 'h':
case 'H':
if (u[1] != '\0') {
return -1;
}
break;
case 's':
case 'S':
if (u[1] != '\0') {
return -1;
}
break;
case 'm':
case 'M':
if (toupper(u[1]) == 'S') {
if (u[2] != '\0') {
return -1;
}
} else if (u[1] == '\0') {
} else {
return -1;
}
break;
default:
return -1;
}
}
switch (unit) {
amount *= 60;
amount *= 60;
amount *= 1000;
;
}
*result = amount > INT_MAX ? INT_MAX : (int) amount;
return 0;
}
{
}
{
}
{
#ifdef HAS_CAP
cap_free(child_cap);
#endif
}
{
#ifdef HAVE_CAP
child_cap = cap_from_text("cap_net_admin-eip");
#endif
if (!queue_topic_all) {
return -1;
}
if (!queue_topic_pool) {
return -1;
}
return 0;
}