| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  |  * Copyright (C) <Year>, <Your Name Here> | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-11-29 04:41:19 +00:00
										 |  |  |  * <Your Name Here> <<Your Email Here>> | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  |  * This program is free software, distributed under the terms of | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  |  * Please follow coding guidelines | 
					
						
							| 
									
										
										
										
											2023-11-09 16:26:46 -05:00
										 |  |  |  * https://docs.asterisk.org/Development/Policies-and-Procedures/Coding-Guidelines/
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  | /*! \file
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  |  * \brief Skeleton application | 
					
						
							| 
									
										
										
										
											2005-12-30 21:18:06 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-06-07 23:07:25 +00:00
										 |  |  |  * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This is a skeleton for development of an Asterisk application | 
					
						
							| 
									
										
										
										
											2005-11-06 15:09:47 +00:00
										 |  |  |  * \ingroup applications | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-14 21:45:16 +00:00
										 |  |  | /*! \li \ref app_skel.c uses configuration file \ref app_skel.conf
 | 
					
						
							| 
									
										
										
										
											2012-10-01 23:22:50 +00:00
										 |  |  |  * \addtogroup configuration_file Configuration Files | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-22 09:23:22 -05:00
										 |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2012-10-01 23:22:50 +00:00
										 |  |  |  * \page app_skel.conf app_skel.conf | 
					
						
							|  |  |  |  * \verbinclude app_skel.conf.sample | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-04-24 17:11:45 +00:00
										 |  |  | /*** MODULEINFO
 | 
					
						
							|  |  |  | 	<defaultenabled>no</defaultenabled> | 
					
						
							| 
									
										
										
										
											2011-07-14 20:28:54 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							| 
									
										
										
										
											2006-04-24 17:11:45 +00:00
										 |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | #include <math.h> /* log10 */
 | 
					
						
							| 
									
										
										
										
											2005-04-21 06:02:45 +00:00
										 |  |  | #include "asterisk/file.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/app.h"
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | #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"
 | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | /*** DOCUMENTATION
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	<application name="SkelGuessNumber" language="en_US"> | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | 		<synopsis> | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 			An example number guessing game | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 			<parameter name="level" required="true"/> | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | 			<parameter name="options"> | 
					
						
							|  |  |  | 				<optionlist> | 
					
						
							|  |  |  | 					<option name="c"> | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 						<para>The computer should cheat</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 					<option name="n"> | 
					
						
							|  |  |  | 						<para>How many games to play before hanging up</para> | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | 					</option> | 
					
						
							|  |  |  | 				</optionlist> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		<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> | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  | 		</description> | 
					
						
							|  |  |  | 	</application> | 
					
						
							| 
									
										
										
										
											2013-02-15 13:38:12 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	<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> | 
					
						
							| 
									
										
										
										
											2008-11-01 21:10:07 +00:00
										 |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | static char *app = "SkelGuessNumber"; | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | enum option_flags { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	OPTION_CHEAT    = (1 << 0), | 
					
						
							|  |  |  | 	OPTION_NUMGAMES = (1 << 1), | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | enum option_args { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	OPTION_ARG_NUMGAMES, | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 	/* This *must* be the last value in this enum! */ | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	OPTION_ARG_ARRAY_SIZE, | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | AST_APP_OPTIONS(app_opts,{ | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	AST_APP_OPTION('c', OPTION_CHEAT), | 
					
						
							|  |  |  | 	AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES), | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | /*! \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.
 | 
					
						
							| 
									
										
										
										
											2017-12-20 12:14:54 -05:00
										 |  |  |  * \note This object should hold a reference to an object that holds state across reloads. | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  |  * 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 { | 
					
						
							| 
									
										
										
										
											2017-12-20 12:14:54 -05:00
										 |  |  | 	uint32_t total_games;          /*! The total number of games for this call to the app */ | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-09-21 17:14:59 +00:00
										 |  |  |  * \param tmp_container A non-active container to search for a level | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  |  * \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, | 
					
						
							| 
									
										
										
										
											2013-02-15 13:38:12 +00:00
										 |  |  | 	.name = "globals", | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	.item_offset = offsetof(struct skel_config, global), | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 	.category_match = ACO_WHITELIST_EXACT, | 
					
						
							|  |  |  | 	.category = "general", | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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, | 
					
						
							| 
									
										
										
										
											2013-02-15 13:38:12 +00:00
										 |  |  | 	.name = "sounds", | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	.item_offset = offsetof(struct skel_config, global), | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 	.category_match = ACO_WHITELIST_EXACT, | 
					
						
							|  |  |  | 	.category = "sounds", | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct aco_type *sound_options[] = ACO_TYPES(&sound_option); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | static const char *level_categories[] = { | 
					
						
							|  |  |  | 	"general", | 
					
						
							|  |  |  | 	"sounds", | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | /*! \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, | 
					
						
							| 
									
										
										
										
											2013-02-15 13:38:12 +00:00
										 |  |  | 	.name = "level", | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 	.category_match = ACO_BLACKLIST_ARRAY, | 
					
						
							|  |  |  | 	.category = (const char *)level_categories, | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	.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); | 
					
						
							| 
									
										
										
											
												app.c: Allow ampersands in playback lists to be escaped.
Any function or application that accepts a `&`-separated list of
filenames can now include a literal `&` in a filename by wrapping the
entire filename in single quotes, e.g.:
```
exten = _X.,n,Playback('https://example.com/sound.cgi?a=b&c=d'&hello-world)
```
Fixes #172
UpgradeNote: Ampersands in URLs passed to the `Playback()`,
`Background()`, `SpeechBackground()`, `Read()`, `Authenticate()`, or
`Queue()` applications as filename arguments can now be escaped by
single quoting the filename. Additionally, this is also possible when
using the `CONFBRIDGE` dialplan function, or configuring various
features in `confbridge.conf` and `queues.conf`.
											
										 
											2023-11-07 15:03:53 -05:00
										 |  |  | 	while ((prompt = ast_strsep(&rest, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM)) | 
					
						
							|  |  |  | 		&& !ast_stream_and_wait(chan, prompt, "")) { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		ast_stopstream(chan); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | static int app_exec(struct ast_channel *chan, const char *data) | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 	char *parse, *opts[OPTION_ARG_ARRAY_SIZE]; | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	struct ast_flags flags; | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		AST_APP_ARG(level); | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 		AST_APP_ARG(options); | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	if (!cfg) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Couldn't access configuratino data!\n"); | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* We need to make a copy of the input string if we are going to modify it! */ | 
					
						
							| 
									
										
										
										
											2006-05-10 13:22:15 +00:00
										 |  |  | 	parse = ast_strdupa(data); | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 	AST_STANDARD_APP_ARGS(args, parse); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	if (args.argc == 2) { | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 		ast_app_parse_options(app_opts, &flags, opts, args.options); | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-22 03:04:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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'; | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 			/* 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); | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2008-10-16 22:36:08 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	ao2_ref(level->state, +1); | 
					
						
							|  |  |  | 	return level->state; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-03-27 22:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-19 15:10:02 -05:00
										 |  |  | 	cfg->levels = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, LEVEL_BUCKETS, | 
					
						
							|  |  |  | 		skel_level_hash, NULL, skel_level_cmp); | 
					
						
							|  |  |  | 	if (!cfg->levels) { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-07 20:32:07 +00:00
										 |  |  | 	if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int unload_module(void) | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli)); | 
					
						
							|  |  |  | 	aco_info_destroy(&cfg_info); | 
					
						
							|  |  |  | 	ao2_global_obj_release(globals); | 
					
						
							| 
									
										
										
										
											2014-07-13 16:48:48 +00:00
										 |  |  | 	ao2_cleanup(games); | 
					
						
							| 
									
										
										
										
											2008-05-10 03:28:50 +00:00
										 |  |  | 	return ast_unregister_application(app); | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-21 17:14:59 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Load the module | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-10-01 23:22:50 +00:00
										 |  |  |  * 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 | 
					
						
							| 
									
										
										
										
											2017-12-22 09:23:22 -05:00
										 |  |  |  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the | 
					
						
							|  |  |  |  * configuration file or other non-critical problem return | 
					
						
							| 
									
										
										
										
											2012-10-01 23:22:50 +00:00
										 |  |  |  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. | 
					
						
							| 
									
										
										
										
											2012-09-21 17:14:59 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int load_module(void) | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	if (aco_info_init(&cfg_info)) { | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-19 15:10:02 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	games = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL); | 
					
						
							|  |  |  | 	if (!games) { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		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)); | 
					
						
							| 
									
										
										
										
											2016-03-19 13:34:26 +01:00
										 |  |  | 	aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_guesses)); | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-07 20:32:07 +00:00
										 |  |  | 	if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) { | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 		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; | 
					
						
							| 
									
										
										
										
											1999-10-22 01:49:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 13:54:38 -05:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skeleton (sample) Application", | 
					
						
							| 
									
										
										
										
											2014-07-25 16:47:17 +00:00
										 |  |  | 	.support_level = AST_MODULE_SUPPORT_CORE, | 
					
						
							| 
									
										
										
										
											2012-06-01 16:33:25 +00:00
										 |  |  | 	.load = load_module, | 
					
						
							|  |  |  | 	.unload = unload_module, | 
					
						
							|  |  |  | 	.reload = reload_module, | 
					
						
							|  |  |  | ); |