00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "asterisk.h"
00038
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 368673 $")
00040
00041 #include <math.h>
00042 #include "asterisk/file.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/lock.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/config_options.h"
00050 #include "asterisk/say.h"
00051 #include "asterisk/astobj2.h"
00052 #include "asterisk/acl.h"
00053 #include "asterisk/netsock2.h"
00054 #include "asterisk/strings.h"
00055 #include "asterisk/cli.h"
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 static char *app = "SkelGuessNumber";
00083
00084 enum option_flags {
00085 OPTION_CHEAT = (1 << 0),
00086 OPTION_NUMGAMES = (1 << 1),
00087 };
00088
00089 enum option_args {
00090 OPTION_ARG_NUMGAMES,
00091
00092 OPTION_ARG_ARRAY_SIZE,
00093 };
00094
00095 AST_APP_OPTIONS(app_opts,{
00096 AST_APP_OPTION('c', OPTION_CHEAT),
00097 AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
00098 });
00099
00100
00101 struct skel_global_config {
00102 AST_DECLARE_STRING_FIELDS(
00103 AST_STRING_FIELD(prompt);
00104 AST_STRING_FIELD(wrong);
00105 AST_STRING_FIELD(right);
00106 AST_STRING_FIELD(high);
00107 AST_STRING_FIELD(low);
00108 AST_STRING_FIELD(lose);
00109 );
00110 uint32_t num_games;
00111 unsigned char cheat:1;
00112 };
00113
00114
00115 struct skel_level_state {
00116 uint32_t wins;
00117 uint32_t losses;
00118 double avg_guesses;
00119 };
00120
00121
00122
00123
00124
00125 struct skel_level {
00126 AST_DECLARE_STRING_FIELDS(
00127 AST_STRING_FIELD(name);
00128 );
00129 uint32_t max_num;
00130 uint32_t max_guesses;
00131 struct skel_level_state *state;
00132 };
00133
00134
00135
00136
00137
00138
00139
00140
00141 struct skel_current_game {
00142 uint32_t total_games;
00143 uint32_t games_left;
00144 uint32_t cheat;
00145 struct skel_level *level_info;
00146 };
00147
00148
00149 #define LEVEL_BUCKETS 1
00150
00151
00152
00153
00154
00155
00156
00157 struct skel_config {
00158 struct skel_global_config *global;
00159 struct ao2_container *levels;
00160 };
00161
00162
00163
00164
00165
00166
00167
00168 static void *skel_config_alloc(void);
00169
00170
00171
00172
00173
00174 static void *skel_level_alloc(const char *cat);
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186 static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
00187
00188
00189 static struct aco_type global_option = {
00190 .type = ACO_GLOBAL,
00191 .item_offset = offsetof(struct skel_config, global),
00192 .category_match = ACO_WHITELIST,
00193 .category = "^general$",
00194 };
00195
00196 struct aco_type *global_options[] = ACO_TYPES(&global_option);
00197
00198
00199 static struct aco_type sound_option = {
00200 .type = ACO_GLOBAL,
00201 .item_offset = offsetof(struct skel_config, global),
00202 .category_match = ACO_WHITELIST,
00203 .category = "^sounds$",
00204 };
00205
00206 struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
00207
00208
00209 static struct aco_type level_option = {
00210 .type = ACO_ITEM,
00211 .category_match = ACO_BLACKLIST,
00212 .category = "^(general|sounds)$",
00213 .item_alloc = skel_level_alloc,
00214 .item_find = skel_level_find,
00215 .item_offset = offsetof(struct skel_config, levels),
00216 };
00217
00218 struct aco_type *level_options[] = ACO_TYPES(&level_option);
00219
00220 struct aco_file app_skel_conf = {
00221 .filename = "app_skel.conf",
00222 .types = ACO_TYPES(&global_option, &sound_option, &level_option),
00223 };
00224
00225
00226 static AO2_GLOBAL_OBJ_STATIC(globals);
00227
00228
00229 static struct ao2_container *games;
00230
00231
00232 CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
00233 .files = ACO_FILES(&app_skel_conf),
00234 );
00235
00236 static void skel_global_config_destructor(void *obj)
00237 {
00238 struct skel_global_config *global = obj;
00239 ast_string_field_free_memory(global);
00240 }
00241
00242 static void skel_game_destructor(void *obj)
00243 {
00244 struct skel_current_game *game = obj;
00245 ao2_cleanup(game->level_info);
00246 }
00247
00248 static void skel_state_destructor(void *obj)
00249 {
00250 return;
00251 }
00252
00253 static struct skel_current_game *skel_game_alloc(struct skel_level *level)
00254 {
00255 struct skel_current_game *game;
00256 if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
00257 return NULL;
00258 }
00259 ao2_ref(level, +1);
00260 game->level_info = level;
00261 return game;
00262 }
00263
00264 static void skel_level_destructor(void *obj)
00265 {
00266 struct skel_level *level = obj;
00267 ast_string_field_free_memory(level);
00268 ao2_cleanup(level->state);
00269 }
00270
00271 static int skel_level_hash(const void *obj, const int flags)
00272 {
00273 const struct skel_level *level = obj;
00274 const char *name = (flags & OBJ_KEY) ? obj : level->name;
00275 return ast_str_case_hash(name);
00276 }
00277
00278 static int skel_level_cmp(void *obj, void *arg, int flags)
00279 {
00280 struct skel_level *one = obj, *two = arg;
00281 const char *match = (flags & OBJ_KEY) ? arg : two->name;
00282 return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295 static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
00296 {
00297 struct skel_global_config *global = obj;
00298
00299 if (!strcasecmp(var->name, "cheat")) {
00300 global->cheat = ast_true(var->value);
00301 } else {
00302 return -1;
00303 }
00304
00305 return 0;
00306 }
00307
00308 static void play_files_helper(struct ast_channel *chan, const char *prompts)
00309 {
00310 char *prompt, *rest = ast_strdupa(prompts);
00311
00312 ast_stopstream(chan);
00313 while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
00314 ast_stopstream(chan);
00315 }
00316 }
00317
00318 static int app_exec(struct ast_channel *chan, const char *data)
00319 {
00320 int win = 0;
00321 uint32_t guesses;
00322 RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00323 RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
00324 RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
00325 char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
00326 struct ast_flags flags;
00327 AST_DECLARE_APP_ARGS(args,
00328 AST_APP_ARG(level);
00329 AST_APP_ARG(options);
00330 );
00331
00332 if (!cfg) {
00333 ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
00334 return -1;
00335 }
00336
00337 if (ast_strlen_zero(data)) {
00338 ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
00339 return -1;
00340 }
00341
00342
00343 parse = ast_strdupa(data);
00344
00345 AST_STANDARD_APP_ARGS(args, parse);
00346
00347 if (args.argc == 2) {
00348 ast_app_parse_options(app_opts, &flags, opts, args.options);
00349 }
00350
00351 if (ast_strlen_zero(args.level)) {
00352 ast_log(LOG_ERROR, "%s requires a level argument\n", app);
00353 return -1;
00354 }
00355
00356 if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
00357 ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
00358 return -1;
00359 }
00360
00361 if (!(game = skel_game_alloc(level))) {
00362 return -1;
00363 }
00364
00365 ao2_link(games, game);
00366
00367
00368 if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
00369 ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
00370 ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
00371 game->total_games = cfg->global->num_games;
00372 }
00373 game->games_left = game->total_games;
00374 game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
00375
00376 for (game->games_left = game->total_games; game->games_left; game->games_left--) {
00377 uint32_t num = ast_random() % level->max_num;
00378
00379 ast_debug(1, "They should totally should guess %u\n", num);
00380
00381
00382 play_files_helper(chan, cfg->global->prompt);
00383 ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
00384
00385 for (guesses = 0; guesses < level->max_guesses; guesses++) {
00386 size_t buflen = log10(level->max_num) + 1;
00387 char buf[buflen];
00388 int guess;
00389 buf[buflen] = '\0';
00390
00391
00392 ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
00393 if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
00394 if (guesses < level->max_guesses - 1) {
00395 play_files_helper(chan, cfg->global->wrong);
00396 }
00397 continue;
00398 }
00399
00400
00401 if (guess == num && !game->cheat) {
00402
00403 win = 1;
00404 play_files_helper(chan, cfg->global->right);
00405 guesses++;
00406 break;
00407 } else if (guess < num) {
00408 play_files_helper(chan, cfg->global->low);
00409 } else {
00410 play_files_helper(chan, cfg->global->high);
00411 }
00412
00413 if (guesses < level->max_guesses - 1) {
00414 play_files_helper(chan, cfg->global->wrong);
00415 }
00416 }
00417
00418
00419 ao2_lock(level->state);
00420 if (win) {
00421 ++level->state->wins;
00422 level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
00423 } else {
00424
00425 level->state->losses++;
00426 play_files_helper(chan, cfg->global->lose);
00427 }
00428 ao2_unlock(level->state);
00429 }
00430
00431 ao2_unlink(games, game);
00432
00433 return 0;
00434 }
00435
00436 static struct skel_level *skel_state_alloc(const char *name)
00437 {
00438 struct skel_level *level;
00439
00440 if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
00441 return NULL;
00442 }
00443
00444 return level;
00445 }
00446
00447 static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
00448 {
00449 return ao2_find(tmp_container, category, OBJ_KEY);
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460 static void *skel_find_or_create_state(const char *category)
00461 {
00462 RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00463 RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
00464 if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
00465 return skel_state_alloc(category);
00466 }
00467 ao2_ref(level->state, +1);
00468 return level->state;
00469 }
00470
00471 static void *skel_level_alloc(const char *cat)
00472 {
00473 struct skel_level *level;
00474
00475 if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
00476 return NULL;
00477 }
00478
00479 if (ast_string_field_init(level, 128)) {
00480 ao2_ref(level, -1);
00481 return NULL;
00482 }
00483
00484
00485
00486
00487
00488 if (!(level->state = skel_find_or_create_state(cat))) {
00489 ao2_ref(level, -1);
00490 return NULL;
00491 }
00492
00493 ast_string_field_set(level, name, cat);
00494
00495 return level;
00496 }
00497
00498 static void skel_config_destructor(void *obj)
00499 {
00500 struct skel_config *cfg = obj;
00501 ao2_cleanup(cfg->global);
00502 ao2_cleanup(cfg->levels);
00503 }
00504
00505 static void *skel_config_alloc(void)
00506 {
00507 struct skel_config *cfg;
00508
00509 if (!(cfg = ao2_alloc(sizeof(*cfg), skel_config_destructor))) {
00510 return NULL;
00511 }
00512
00513
00514 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
00515 goto error;
00516 }
00517
00518 if (ast_string_field_init(cfg->global, 128)) {
00519 goto error;
00520 }
00521
00522 if (!(cfg->levels = ao2_container_alloc(LEVEL_BUCKETS, skel_level_hash, skel_level_cmp))) {
00523 goto error;
00524 }
00525
00526 return cfg;
00527 error:
00528 ao2_ref(cfg, -1);
00529 return NULL;
00530 }
00531
00532 static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00533 {
00534 RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
00535
00536 switch(cmd) {
00537 case CLI_INIT:
00538 e->command = "skel show config";
00539 e->usage =
00540 "Usage: skel show config\n"
00541 " List app_skel global config\n";
00542 return NULL;
00543 case CLI_GENERATE:
00544 return NULL;
00545 }
00546
00547 if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
00548 return NULL;
00549 }
00550
00551 ast_cli(a->fd, "games per call: %u\n", cfg->global->num_games);
00552 ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
00553 ast_cli(a->fd, "\n");
00554 ast_cli(a->fd, "Sounds\n");
00555 ast_cli(a->fd, " prompt: %s\n", cfg->global->prompt);
00556 ast_cli(a->fd, " wrong guess: %s\n", cfg->global->wrong);
00557 ast_cli(a->fd, " right guess: %s\n", cfg->global->right);
00558
00559 return CLI_SUCCESS;
00560 }
00561
00562 static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00563 {
00564 struct ao2_iterator iter;
00565 struct skel_current_game *game;
00566
00567 switch(cmd) {
00568 case CLI_INIT:
00569 e->command = "skel show games";
00570 e->usage =
00571 "Usage: skel show games\n"
00572 " List app_skel active games\n";
00573 return NULL;
00574 case CLI_GENERATE:
00575 return NULL;
00576 }
00577
00578 #define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
00579 #define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
00580 ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
00581 iter = ao2_iterator_init(games, 0);
00582 while ((game = ao2_iterator_next(&iter))) {
00583 ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
00584 ao2_ref(game, -1);
00585 }
00586 ao2_iterator_destroy(&iter);
00587 #undef SKEL_FORMAT
00588 #undef SKEL_FORMAT1
00589 return CLI_SUCCESS;
00590 }
00591
00592 static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00593 {
00594 RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
00595 struct ao2_iterator iter;
00596 struct skel_level *level;
00597
00598 switch(cmd) {
00599 case CLI_INIT:
00600 e->command = "skel show levels";
00601 e->usage =
00602 "Usage: skel show levels\n"
00603 " List the app_skel levels\n";
00604 return NULL;
00605 case CLI_GENERATE:
00606 return NULL;
00607 }
00608
00609 if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
00610 return NULL;
00611 }
00612
00613 #define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
00614 #define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
00615 ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
00616 iter = ao2_iterator_init(cfg->levels, 0);
00617 while ((level = ao2_iterator_next(&iter))) {
00618 ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
00619 ao2_ref(level, -1);
00620 }
00621 ao2_iterator_destroy(&iter);
00622 #undef SKEL_FORMAT
00623 #undef SKEL_FORMAT1
00624
00625 return CLI_SUCCESS;
00626 }
00627
00628 static struct ast_cli_entry skel_cli[] = {
00629 AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
00630 AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
00631 AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
00632 };
00633
00634 static int reload_module(void)
00635 {
00636 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
00637 return AST_MODULE_LOAD_DECLINE;
00638 }
00639
00640 return 0;
00641 }
00642
00643 static int unload_module(void)
00644 {
00645 ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
00646 aco_info_destroy(&cfg_info);
00647 ao2_global_obj_release(globals);
00648 return ast_unregister_application(app);
00649 }
00650
00651 static int load_module(void)
00652 {
00653 if (aco_info_init(&cfg_info)) {
00654 goto error;
00655 }
00656 if (!(games = ao2_container_alloc(1, NULL, NULL))) {
00657 goto error;
00658 }
00659
00660
00661 aco_option_register(&cfg_info, "games", ACO_EXACT, global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
00662 aco_option_register_custom(&cfg_info, "cheat", ACO_EXACT, global_options, "no", custom_bitfield_handler, 0);
00663
00664
00665 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));
00666 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));
00667 aco_option_register(&cfg_info, "right_guess", ACO_EXACT, sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
00668 aco_option_register(&cfg_info, "too_high", ACO_EXACT, sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
00669 aco_option_register(&cfg_info, "too_low", ACO_EXACT, sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
00670 aco_option_register(&cfg_info, "lose", ACO_EXACT, sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
00671
00672
00673 aco_option_register(&cfg_info, "max_number", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
00674 aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
00675
00676 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
00677 goto error;
00678 }
00679
00680 ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
00681 if (ast_register_application_xml(app, app_exec)) {
00682 goto error;
00683 }
00684 return AST_MODULE_LOAD_SUCCESS;
00685
00686 error:
00687 aco_info_destroy(&cfg_info);
00688 ao2_cleanup(games);
00689 return AST_MODULE_LOAD_DECLINE;
00690 }
00691
00692 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skeleton (sample) Application",
00693 .load = load_module,
00694 .unload = unload_module,
00695 .reload = reload_module,
00696 .load_pri = AST_MODPRI_DEFAULT,
00697 );