main/app.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Convenient Application Routines
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  */
00025 
00026 /** \example
00027  * \par This is an example of how to develop an app.
00028  * Application Skeleton is an example of creating an application for Asterisk.
00029  * \verbinclude app_skel.c
00030  */
00031 
00032 /*** MODULEINFO
00033    <support_level>core</support_level>
00034  ***/
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434471 $")
00039 
00040 #ifdef HAVE_SYS_STAT_H
00041 #include <sys/stat.h>
00042 #endif
00043 #include <regex.h>          /* for regcomp(3) */
00044 #include <sys/file.h>       /* for flock(2) */
00045 #include <signal.h>         /* for pthread_sigmask(3) */
00046 #include <stdlib.h>         /* for closefrom(3) */
00047 #include <sys/types.h>
00048 #include <sys/wait.h>       /* for waitpid(2) */
00049 #ifndef HAVE_CLOSEFROM
00050 #include <dirent.h>         /* for opendir(3)   */
00051 #endif
00052 #ifdef HAVE_CAP
00053 #include <sys/capability.h>
00054 #endif /* HAVE_CAP */
00055 
00056 #include "asterisk/paths.h"   /* use ast_config_AST_DATA_DIR */
00057 #include "asterisk/channel.h"
00058 #include "asterisk/pbx.h"
00059 #include "asterisk/file.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/dsp.h"
00062 #include "asterisk/utils.h"
00063 #include "asterisk/lock.h"
00064 #include "asterisk/indications.h"
00065 #include "asterisk/linkedlists.h"
00066 #include "asterisk/threadstorage.h"
00067 #include "asterisk/test.h"
00068 #include "asterisk/module.h"
00069 #include "asterisk/astobj2.h"
00070 #include "asterisk/stasis.h"
00071 #include "asterisk/stasis_channels.h"
00072 #include "asterisk/json.h"
00073 #include "asterisk/format_cache.h"
00074 
00075 #define MWI_TOPIC_BUCKETS 57
00076 
00077 AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf);
00078 
00079 static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
00080 
00081 struct zombie {
00082    pid_t pid;
00083    AST_LIST_ENTRY(zombie) list;
00084 };
00085 
00086 static AST_LIST_HEAD_STATIC(zombies, zombie);
00087 
00088 /*
00089  * @{ \brief Define \ref stasis topic objects
00090  */
00091 static struct stasis_topic *mwi_topic_all;
00092 static struct stasis_cache *mwi_state_cache;
00093 static struct stasis_caching_topic *mwi_topic_cached;
00094 static struct stasis_topic_pool *mwi_topic_pool;
00095 
00096 static struct stasis_topic *queue_topic_all;
00097 static struct stasis_topic_pool *queue_topic_pool;
00098 /* @} */
00099 
00100 /*! \brief Convert a MWI \ref stasis_message to a \ref ast_event */
00101 static struct ast_event *mwi_to_event(struct stasis_message *message)
00102 {
00103    struct ast_event *event;
00104    struct ast_mwi_state *mwi_state;
00105    char *mailbox;
00106    char *context;
00107 
00108    if (!message) {
00109       return NULL;
00110    }
00111 
00112    mwi_state = stasis_message_data(message);
00113 
00114    /* Strip off @context */
00115    context = mailbox = ast_strdupa(mwi_state->uniqueid);
00116    strsep(&context, "@");
00117    if (ast_strlen_zero(context)) {
00118       context = "default";
00119    }
00120 
00121    event = ast_event_new(AST_EVENT_MWI,
00122             AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
00123             AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
00124             AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, mwi_state->new_msgs,
00125             AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, mwi_state->old_msgs,
00126             AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, &mwi_state->eid, sizeof(mwi_state->eid),
00127             AST_EVENT_IE_END);
00128 
00129    return event;
00130 }
00131 
00132 /*
00133  * @{ \brief Define \ref stasis message types for MWI
00134  */
00135 STASIS_MESSAGE_TYPE_DEFN(ast_mwi_state_type,
00136    .to_event = mwi_to_event, );
00137 STASIS_MESSAGE_TYPE_DEFN(ast_mwi_vm_app_type);
00138 /* @} */
00139 
00140 
00141 
00142 static void *shaun_of_the_dead(void *data)
00143 {
00144    struct zombie *cur;
00145    int status;
00146    for (;;) {
00147       if (!AST_LIST_EMPTY(&zombies)) {
00148          /* Don't allow cancellation while we have a lock. */
00149          pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
00150          AST_LIST_LOCK(&zombies);
00151          AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
00152             if (waitpid(cur->pid, &status, WNOHANG) != 0) {
00153                AST_LIST_REMOVE_CURRENT(list);
00154                ast_free(cur);
00155             }
00156          }
00157          AST_LIST_TRAVERSE_SAFE_END
00158          AST_LIST_UNLOCK(&zombies);
00159          pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
00160       }
00161       pthread_testcancel();
00162       /* Wait for 60 seconds, without engaging in a busy loop. */
00163       ast_poll(NULL, 0, AST_LIST_FIRST(&zombies) ? 5000 : 60000);
00164    }
00165    return NULL;
00166 }
00167 
00168 
00169 #define AST_MAX_FORMATS 10
00170 
00171 static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
00172 
00173 /*!
00174  * \brief This function presents a dialtone and reads an extension into 'collect'
00175  * which must be a pointer to a **pre-initialized** array of char having a
00176  * size of 'size' suitable for writing to.  It will collect no more than the smaller
00177  * of 'maxlen' or 'size' minus the original strlen() of collect digits.
00178  * \param chan struct.
00179  * \param context
00180  * \param collect
00181  * \param size
00182  * \param maxlen
00183  * \param timeout timeout in milliseconds
00184  *
00185  * \return 0 if extension does not exist, 1 if extension exists
00186 */
00187 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
00188 {
00189    struct ast_tone_zone_sound *ts;
00190    int res = 0, x = 0;
00191 
00192    if (maxlen > size) {
00193       maxlen = size;
00194    }
00195 
00196    if (!timeout) {
00197       if (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) {
00198          timeout = ast_channel_pbx(chan)->dtimeoutms;
00199       } else {
00200          timeout = 5000;
00201       }
00202    }
00203 
00204    if ((ts = ast_get_indication_tone(ast_channel_zone(chan), "dial"))) {
00205       res = ast_playtones_start(chan, 0, ts->data, 0);
00206       ts = ast_tone_zone_sound_unref(ts);
00207    } else {
00208       ast_log(LOG_NOTICE, "Huh....? no dial for indications?\n");
00209    }
00210 
00211    for (x = strlen(collect); x < maxlen; ) {
00212       res = ast_waitfordigit(chan, timeout);
00213       if (!ast_ignore_pattern(context, collect)) {
00214          ast_playtones_stop(chan);
00215       }
00216       if (res < 1) {
00217          break;
00218       }
00219       if (res == '#') {
00220          break;
00221       }
00222       collect[x++] = res;
00223       if (!ast_matchmore_extension(chan, context, collect, 1,
00224          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00225          break;
00226       }
00227    }
00228 
00229    if (res >= 0) {
00230       res = ast_exists_extension(chan, context, collect, 1,
00231          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)) ? 1 : 0;
00232    }
00233 
00234    return res;
00235 }
00236 
00237 /*!
00238  * \brief ast_app_getdata
00239  * \param c The channel to read from
00240  * \param prompt The file to stream to the channel
00241  * \param s The string to read in to.  Must be at least the size of your length
00242  * \param maxlen How many digits to read (maximum)
00243  * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
00244  *      "ludicrous time" (essentially never times out) */
00245 enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
00246 {
00247    int res = 0, to, fto;
00248    char *front, *filename;
00249 
00250    /* XXX Merge with full version? XXX */
00251 
00252    if (maxlen)
00253       s[0] = '\0';
00254 
00255    if (!prompt)
00256       prompt = "";
00257 
00258    filename = ast_strdupa(prompt);
00259    while ((front = strsep(&filename, "&"))) {
00260       if (!ast_strlen_zero(front)) {
00261          res = ast_streamfile(c, front, ast_channel_language(c));
00262          if (res)
00263             continue;
00264       }
00265       if (ast_strlen_zero(filename)) {
00266          /* set timeouts for the last prompt */
00267          fto = ast_channel_pbx(c) ? ast_channel_pbx(c)->rtimeoutms : 6000;
00268          to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
00269 
00270          if (timeout > 0) {
00271             fto = to = timeout;
00272          }
00273          if (timeout < 0) {
00274             fto = to = 1000000000;
00275          }
00276       } else {
00277          /* there is more than one prompt, so
00278           * get rid of the long timeout between
00279           * prompts, and make it 50ms */
00280          fto = 50;
00281          to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
00282       }
00283       res = ast_readstring(c, s, maxlen, to, fto, "#");
00284       if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
00285          return res;
00286       }
00287       if (!ast_strlen_zero(s)) {
00288          return res;
00289       }
00290    }
00291 
00292    return res;
00293 }
00294 
00295 /* The lock type used by ast_lock_path() / ast_unlock_path() */
00296 static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
00297 
00298 int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
00299 {
00300    int res, to = 2000, fto = 6000;
00301 
00302    if (!ast_strlen_zero(prompt)) {
00303       res = ast_streamfile(c, prompt, ast_channel_language(c));
00304       if (res < 0) {
00305          return res;
00306       }
00307    }
00308 
00309    if (timeout > 0) {
00310       fto = to = timeout;
00311    }
00312    if (timeout < 0) {
00313       fto = to = 1000000000;
00314    }
00315 
00316    res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
00317 
00318    return res;
00319 }
00320 
00321 int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args)
00322 {
00323    struct ast_app *macro_app;
00324    int res;
00325 
00326    macro_app = pbx_findapp("Macro");
00327    if (!macro_app) {
00328       ast_log(LOG_WARNING,
00329          "Cannot run 'Macro(%s)'.  The application is not available.\n", macro_args);
00330       return -1;
00331    }
00332    if (autoservice_chan) {
00333       ast_autoservice_start(autoservice_chan);
00334    }
00335 
00336    ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(macro_chan),
00337       ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
00338       ast_channel_priority(macro_chan));
00339 
00340    res = pbx_exec(macro_chan, macro_app, macro_args);
00341    ast_debug(4, "Macro exited with status %d\n", res);
00342 
00343    /*
00344     * Assume anything negative from Macro is an error.
00345     * Anything else is success.
00346     */
00347    if (res < 0) {
00348       res = -1;
00349    } else {
00350       res = 0;
00351    }
00352 
00353    ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(macro_chan),
00354       ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
00355       ast_channel_priority(macro_chan));
00356 
00357    if (autoservice_chan) {
00358       ast_autoservice_stop(autoservice_chan);
00359    }
00360    return res;
00361 }
00362 
00363 int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args)
00364 {
00365    int res;
00366    char *args_str;
00367    size_t args_len;
00368 
00369    if (ast_strlen_zero(macro_args)) {
00370       return ast_app_exec_macro(autoservice_chan, macro_chan, macro_name);
00371    }
00372 
00373    /* Create the Macro application argument string. */
00374    args_len = strlen(macro_name) + strlen(macro_args) + 2;
00375    args_str = ast_malloc(args_len);
00376    if (!args_str) {
00377       return -1;
00378    }
00379    snprintf(args_str, args_len, "%s,%s", macro_name, macro_args);
00380 
00381    res = ast_app_exec_macro(autoservice_chan, macro_chan, args_str);
00382    ast_free(args_str);
00383    return res;
00384 }
00385 
00386 static const struct ast_app_stack_funcs *app_stack_callbacks;
00387 
00388 void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
00389 {
00390    app_stack_callbacks = funcs;
00391 }
00392 
00393 const char *ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
00394 {
00395    const struct ast_app_stack_funcs *funcs;
00396    const char *new_args;
00397 
00398    funcs = app_stack_callbacks;
00399    if (!funcs || !funcs->expand_sub_args) {
00400       ast_log(LOG_WARNING,
00401          "Cannot expand 'Gosub(%s)' arguments.  The app_stack module is not available.\n",
00402          args);
00403       return NULL;
00404    }
00405    ast_module_ref(funcs->module);
00406 
00407    new_args = funcs->expand_sub_args(chan, args);
00408    ast_module_unref(funcs->module);
00409    return new_args;
00410 }
00411 
00412 int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
00413 {
00414    const struct ast_app_stack_funcs *funcs;
00415    int res;
00416 
00417    funcs = app_stack_callbacks;
00418    if (!funcs || !funcs->run_sub) {
00419       ast_log(LOG_WARNING,
00420          "Cannot run 'Gosub(%s)'.  The app_stack module is not available.\n",
00421          sub_args);
00422       return -1;
00423    }
00424    ast_module_ref(funcs->module);
00425 
00426    if (autoservice_chan) {
00427       ast_autoservice_start(autoservice_chan);
00428    }
00429 
00430    res = funcs->run_sub(sub_chan, sub_args, ignore_hangup);
00431    ast_module_unref(funcs->module);
00432 
00433    if (autoservice_chan) {
00434       ast_autoservice_stop(autoservice_chan);
00435    }
00436    return res;
00437 }
00438 
00439 int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_location, const char *sub_args, int ignore_hangup)
00440 {
00441    int res;
00442    char *args_str;
00443    size_t args_len;
00444 
00445    if (ast_strlen_zero(sub_args)) {
00446       return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location, ignore_hangup);
00447    }
00448 
00449    /* Create the Gosub application argument string. */
00450    args_len = strlen(sub_location) + strlen(sub_args) + 3;
00451    args_str = ast_malloc(args_len);
00452    if (!args_str) {
00453       return -1;
00454    }
00455    snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args);
00456 
00457    res = ast_app_exec_sub(autoservice_chan, sub_chan, args_str, ignore_hangup);
00458    ast_free(args_str);
00459    return res;
00460 }
00461 
00462 /*! \brief The container for the voicemail provider */
00463 static AO2_GLOBAL_OBJ_STATIC(vm_provider);
00464 
00465 /*! Voicemail not registered warning */
00466 static int vm_warnings;
00467 
00468 int ast_vm_is_registered(void)
00469 {
00470    struct ast_vm_functions *table;
00471    int is_registered;
00472 
00473    table = ao2_global_obj_ref(vm_provider);
00474    is_registered = table ? 1 : 0;
00475    ao2_cleanup(table);
00476    return is_registered;
00477 }
00478 
00479 int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module *module)
00480 {
00481    RAII_VAR(struct ast_vm_functions *, table, NULL, ao2_cleanup);
00482 
00483    if (!vm_table->module_name) {
00484       ast_log(LOG_ERROR, "Voicemail provider missing required information.\n");
00485       return -1;
00486    }
00487    if (vm_table->module_version != VM_MODULE_VERSION) {
00488       ast_log(LOG_ERROR, "Voicemail provider '%s' has incorrect version\n",
00489          vm_table->module_name);
00490       return -1;
00491    }
00492 
00493    table = ao2_global_obj_ref(vm_provider);
00494    if (table) {
00495       ast_log(LOG_WARNING, "Voicemail provider already registered by %s.\n",
00496          table->module_name);
00497       return -1;
00498    }
00499 
00500    table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
00501    if (!table) {
00502       return -1;
00503    }
00504    *table = *vm_table;
00505    table->module = module;
00506 
00507    ao2_global_obj_replace_unref(vm_provider, table);
00508    return 0;
00509 }
00510 
00511 void ast_vm_unregister(const char *module_name)
00512 {
00513    struct ast_vm_functions *table;
00514 
00515    table = ao2_global_obj_ref(vm_provider);
00516    if (table && !strcmp(table->module_name, module_name)) {
00517       ao2_global_obj_release(vm_provider);
00518    }
00519    ao2_cleanup(table);
00520 }
00521 
00522 #ifdef TEST_FRAMEWORK
00523 /*! \brief Holding container for the voicemail provider used while testing */
00524 static AO2_GLOBAL_OBJ_STATIC(vm_provider_holder);
00525 static int provider_is_swapped = 0;
00526 
00527 void ast_vm_test_swap_table_in(const struct ast_vm_functions *vm_table)
00528 {
00529    RAII_VAR(struct ast_vm_functions *, holding_table, NULL, ao2_cleanup);
00530    RAII_VAR(struct ast_vm_functions *, new_table, NULL, ao2_cleanup);
00531 
00532    if (provider_is_swapped) {
00533       ast_log(LOG_ERROR, "Attempted to swap in test function table without swapping out old test table.\n");
00534       return;
00535    }
00536 
00537    holding_table = ao2_global_obj_ref(vm_provider);
00538 
00539    if (holding_table) {
00540       ao2_global_obj_replace_unref(vm_provider_holder, holding_table);
00541    }
00542 
00543    new_table = ao2_alloc_options(sizeof(*new_table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
00544    if (!new_table) {
00545       return;
00546    }
00547    *new_table = *vm_table;
00548 
00549    ao2_global_obj_replace_unref(vm_provider, new_table);
00550    provider_is_swapped = 1;
00551 }
00552 
00553 void ast_vm_test_swap_table_out(void)
00554 {
00555    RAII_VAR(struct ast_vm_functions *, held_table, NULL, ao2_cleanup);
00556 
00557    if (!provider_is_swapped) {
00558       ast_log(LOG_ERROR, "Attempted to swap out test function table, but none is currently installed.\n");
00559       return;
00560    }
00561 
00562    held_table = ao2_global_obj_ref(vm_provider_holder);
00563    if (!held_table) {
00564       return;
00565    }
00566 
00567    ao2_global_obj_replace_unref(vm_provider, held_table);
00568    ao2_global_obj_release(vm_provider_holder);
00569    provider_is_swapped = 0;
00570 }
00571 #endif
00572 
00573 /*! \brief The container for the voicemail greeter provider */
00574 static AO2_GLOBAL_OBJ_STATIC(vm_greeter_provider);
00575 
00576 /*! Voicemail greeter not registered warning */
00577 static int vm_greeter_warnings;
00578 
00579 int ast_vm_greeter_is_registered(void)
00580 {
00581    struct ast_vm_greeter_functions *table;
00582    int is_registered;
00583 
00584    table = ao2_global_obj_ref(vm_greeter_provider);
00585    is_registered = table ? 1 : 0;
00586    ao2_cleanup(table);
00587    return is_registered;
00588 }
00589 
00590 int __ast_vm_greeter_register(const struct ast_vm_greeter_functions *vm_table, struct ast_module *module)
00591 {
00592    RAII_VAR(struct ast_vm_greeter_functions *, table, NULL, ao2_cleanup);
00593 
00594    if (!vm_table->module_name) {
00595       ast_log(LOG_ERROR, "Voicemail greeter provider missing required information.\n");
00596       return -1;
00597    }
00598    if (vm_table->module_version != VM_GREETER_MODULE_VERSION) {
00599       ast_log(LOG_ERROR, "Voicemail greeter provider '%s' has incorrect version\n",
00600          vm_table->module_name);
00601       return -1;
00602    }
00603 
00604    table = ao2_global_obj_ref(vm_greeter_provider);
00605    if (table) {
00606       ast_log(LOG_WARNING, "Voicemail greeter provider already registered by %s.\n",
00607          table->module_name);
00608       return -1;
00609    }
00610 
00611    table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
00612    if (!table) {
00613       return -1;
00614    }
00615    *table = *vm_table;
00616    table->module = module;
00617 
00618    ao2_global_obj_replace_unref(vm_greeter_provider, table);
00619    return 0;
00620 }
00621 
00622 void ast_vm_greeter_unregister(const char *module_name)
00623 {
00624    struct ast_vm_greeter_functions *table;
00625 
00626    table = ao2_global_obj_ref(vm_greeter_provider);
00627    if (table && !strcmp(table->module_name, module_name)) {
00628       ao2_global_obj_release(vm_greeter_provider);
00629    }
00630    ao2_cleanup(table);
00631 }
00632 
00633 #ifdef TEST_FRAMEWORK
00634 static ast_vm_test_create_user_fn *ast_vm_test_create_user_func = NULL;
00635 static ast_vm_test_destroy_user_fn *ast_vm_test_destroy_user_func = NULL;
00636 
00637 void ast_install_vm_test_functions(ast_vm_test_create_user_fn *vm_test_create_user_func,
00638    ast_vm_test_destroy_user_fn *vm_test_destroy_user_func)
00639 {
00640    ast_vm_test_create_user_func = vm_test_create_user_func;
00641    ast_vm_test_destroy_user_func = vm_test_destroy_user_func;
00642 }
00643 
00644 void ast_uninstall_vm_test_functions(void)
00645 {
00646    ast_vm_test_create_user_func = NULL;
00647    ast_vm_test_destroy_user_func = NULL;
00648 }
00649 #endif
00650 
00651 static void vm_warn_no_provider(void)
00652 {
00653    if (vm_warnings++ % 10 == 0) {
00654       ast_verb(3, "No voicemail provider registered.\n");
00655    }
00656 }
00657 
00658 #define VM_API_CALL(res, api_call, api_parms)                        \
00659    do {                                                  \
00660       struct ast_vm_functions *table;                             \
00661       table = ao2_global_obj_ref(vm_provider);                    \
00662       if (!table) {                                         \
00663          vm_warn_no_provider();                                \
00664       } else if (table->api_call) {                            \
00665          ast_module_ref(table->module);                           \
00666          (res) = table->api_call api_parms;                       \
00667          ast_module_unref(table->module);                      \
00668       }                                                  \
00669       ao2_cleanup(table);                                      \
00670    } while (0)
00671 
00672 static void vm_greeter_warn_no_provider(void)
00673 {
00674    if (vm_greeter_warnings++ % 10 == 0) {
00675       ast_verb(3, "No voicemail greeter provider registered.\n");
00676    }
00677 }
00678 
00679 #define VM_GREETER_API_CALL(res, api_call, api_parms)                \
00680    do {                                                  \
00681       struct ast_vm_greeter_functions *table;                        \
00682       table = ao2_global_obj_ref(vm_greeter_provider);               \
00683       if (!table) {                                         \
00684          vm_greeter_warn_no_provider();                           \
00685       } else if (table->api_call) {                            \
00686          ast_module_ref(table->module);                           \
00687          (res) = table->api_call api_parms;                       \
00688          ast_module_unref(table->module);                      \
00689       }                                                  \
00690       ao2_cleanup(table);                                      \
00691    } while (0)
00692 
00693 int ast_app_has_voicemail(const char *mailboxes, const char *folder)
00694 {
00695    int res = 0;
00696 
00697    VM_API_CALL(res, has_voicemail, (mailboxes, folder));
00698    return res;
00699 }
00700 
00701 /*!
00702  * \internal
00703  * \brief Function used as a callback for ast_copy_recording_to_vm when a real one isn't installed.
00704  * \param vm_rec_data Stores crucial information about the voicemail that will basically just be used
00705  * to figure out what the name of the recipient was supposed to be
00706  */
00707 int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
00708 {
00709    int res = -1;
00710 
00711    VM_API_CALL(res, copy_recording_to_vm, (vm_rec_data));
00712    return res;
00713 }
00714 
00715 int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
00716 {
00717    int res = 0;
00718 
00719    if (newmsgs) {
00720       *newmsgs = 0;
00721    }
00722    if (oldmsgs) {
00723       *oldmsgs = 0;
00724    }
00725 
00726    VM_API_CALL(res, inboxcount, (mailboxes, newmsgs, oldmsgs));
00727    return res;
00728 }
00729 
00730 int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
00731 {
00732    int res = 0;
00733 
00734    if (newmsgs) {
00735       *newmsgs = 0;
00736    }
00737    if (oldmsgs) {
00738       *oldmsgs = 0;
00739    }
00740    if (urgentmsgs) {
00741       *urgentmsgs = 0;
00742    }
00743 
00744    VM_API_CALL(res, inboxcount2, (mailboxes, urgentmsgs, newmsgs, oldmsgs));
00745    return res;
00746 }
00747 
00748 int ast_app_sayname(struct ast_channel *chan, const char *mailbox_id)
00749 {
00750    int res = -1;
00751 
00752    VM_GREETER_API_CALL(res, sayname, (chan, mailbox_id));
00753    return res;
00754 }
00755 
00756 int ast_app_messagecount(const char *mailbox_id, const char *folder)
00757 {
00758    int res = 0;
00759 
00760    VM_API_CALL(res, messagecount, (mailbox_id, folder));
00761    return res;
00762 }
00763 
00764 const char *ast_vm_index_to_foldername(int id)
00765 {
00766    const char *res = NULL;
00767 
00768    VM_API_CALL(res, index_to_foldername, (id));
00769    return res;
00770 }
00771 
00772 struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_create(const char *mailbox,
00773    const char *context,
00774    const char *folder,
00775    int descending,
00776    enum ast_vm_snapshot_sort_val sort_val,
00777    int combine_INBOX_and_OLD)
00778 {
00779    struct ast_vm_mailbox_snapshot *res = NULL;
00780 
00781    VM_API_CALL(res, mailbox_snapshot_create, (mailbox, context, folder, descending,
00782       sort_val, combine_INBOX_and_OLD));
00783    return res;
00784 }
00785 
00786 struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
00787 {
00788    struct ast_vm_mailbox_snapshot *res = NULL;
00789 
00790    VM_API_CALL(res, mailbox_snapshot_destroy, (mailbox_snapshot));
00791    return res;
00792 }
00793 
00794 int ast_vm_msg_move(const char *mailbox,
00795    const char *context,
00796    size_t num_msgs,
00797    const char *oldfolder,
00798    const char *old_msg_ids[],
00799    const char *newfolder)
00800 {
00801    int res = 0;
00802 
00803    VM_API_CALL(res, msg_move, (mailbox, context, num_msgs, oldfolder, old_msg_ids,
00804       newfolder));
00805    return res;
00806 }
00807 
00808 int ast_vm_msg_remove(const char *mailbox,
00809    const char *context,
00810    size_t num_msgs,
00811    const char *folder,
00812    const char *msgs[])
00813 {
00814    int res = 0;
00815 
00816    VM_API_CALL(res, msg_remove, (mailbox, context, num_msgs, folder, msgs));
00817    return res;
00818 }
00819 
00820 int ast_vm_msg_forward(const char *from_mailbox,
00821    const char *from_context,
00822    const char *from_folder,
00823    const char *to_mailbox,
00824    const char *to_context,
00825    const char *to_folder,
00826    size_t num_msgs,
00827    const char *msg_ids[],
00828    int delete_old)
00829 {
00830    int res = 0;
00831 
00832    VM_API_CALL(res, msg_forward, (from_mailbox, from_context, from_folder, to_mailbox,
00833       to_context, to_folder, num_msgs, msg_ids, delete_old));
00834    return res;
00835 }
00836 
00837 int ast_vm_msg_play(struct ast_channel *chan,
00838    const char *mailbox,
00839    const char *context,
00840    const char *folder,
00841    const char *msg_num,
00842    ast_vm_msg_play_cb *cb)
00843 {
00844    int res = 0;
00845 
00846    VM_API_CALL(res, msg_play, (chan, mailbox, context, folder, msg_num, cb));
00847    return res;
00848 }
00849 
00850 #ifdef TEST_FRAMEWORK
00851 int ast_vm_test_create_user(const char *context, const char *mailbox)
00852 {
00853    if (ast_vm_test_create_user_func) {
00854       return ast_vm_test_create_user_func(context, mailbox);
00855    }
00856    return 0;
00857 }
00858 
00859 int ast_vm_test_destroy_user(const char *context, const char *mailbox)
00860 {
00861    if (ast_vm_test_destroy_user_func) {
00862       return ast_vm_test_destroy_user_func(context, mailbox);
00863    }
00864    return 0;
00865 }
00866 #endif
00867 
00868 int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
00869 {
00870    const char *ptr;
00871    int res;
00872    struct ast_silence_generator *silgen = NULL;
00873 
00874    if (!between) {
00875       between = 100;
00876    }
00877 
00878    if (peer && ast_autoservice_start(peer)) {
00879       return -1;
00880    }
00881 
00882    /* Need a quiet time before sending digits. */
00883    if (ast_opt_transmit_silence) {
00884       silgen = ast_channel_start_silence_generator(chan);
00885    }
00886    res = ast_safe_sleep(chan, 100);
00887    if (res) {
00888       goto dtmf_stream_cleanup;
00889    }
00890 
00891    for (ptr = digits; *ptr; ptr++) {
00892       if (*ptr == 'w') {
00893          /* 'w' -- wait half a second */
00894          if ((res = ast_safe_sleep(chan, 500))) {
00895             break;
00896          }
00897       } else if (*ptr == 'W') {
00898          /* 'W' -- wait a second */
00899          if ((res = ast_safe_sleep(chan, 1000))) {
00900             break;
00901          }
00902       } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
00903          if (*ptr == 'f' || *ptr == 'F') {
00904             /* ignore return values if not supported by channel */
00905             ast_indicate(chan, AST_CONTROL_FLASH);
00906          } else {
00907             /* Character represents valid DTMF */
00908             ast_senddigit(chan, *ptr, duration);
00909          }
00910          /* pause between digits */
00911          if ((res = ast_safe_sleep(chan, between))) {
00912             break;
00913          }
00914       } else {
00915          ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n", *ptr);
00916       }
00917    }
00918 
00919 dtmf_stream_cleanup:
00920    if (silgen) {
00921       ast_channel_stop_silence_generator(chan, silgen);
00922    }
00923    if (peer && ast_autoservice_stop(peer)) {
00924       res = -1;
00925    }
00926 
00927    return res;
00928 }
00929 
00930 struct linear_state {
00931    int fd;
00932    int autoclose;
00933    int allowoverride;
00934    struct ast_format *origwfmt;
00935 };
00936 
00937 static void linear_release(struct ast_channel *chan, void *params)
00938 {
00939    struct linear_state *ls = params;
00940 
00941    if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
00942       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n",
00943          ast_channel_name(chan), ast_format_get_name(ls->origwfmt));
00944    }
00945    ao2_cleanup(ls->origwfmt);
00946 
00947    if (ls->autoclose) {
00948       close(ls->fd);
00949    }
00950 
00951    ast_free(params);
00952 }
00953 
00954 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
00955 {
00956    short buf[2048 + AST_FRIENDLY_OFFSET / 2];
00957    struct linear_state *ls = data;
00958    struct ast_frame f = {
00959       .frametype = AST_FRAME_VOICE,
00960       .data.ptr = buf + AST_FRIENDLY_OFFSET / 2,
00961       .offset = AST_FRIENDLY_OFFSET,
00962    };
00963    int res;
00964 
00965    f.subclass.format = ast_format_slin;
00966 
00967    len = samples * 2;
00968    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00969       ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len);
00970       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00971    }
00972    res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
00973    if (res > 0) {
00974       f.datalen = res;
00975       f.samples = res / 2;
00976       ast_write(chan, &f);
00977       if (res == len) {
00978          return 0;
00979       }
00980    }
00981    return -1;
00982 }
00983 
00984 static void *linear_alloc(struct ast_channel *chan, void *params)
00985 {
00986    struct linear_state *ls = params;
00987 
00988    if (!params) {
00989       return NULL;
00990    }
00991 
00992    /* In this case, params is already malloc'd */
00993    if (ls->allowoverride) {
00994       ast_set_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
00995    } else {
00996       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
00997    }
00998 
00999    ls->origwfmt = ao2_bump(ast_channel_writeformat(chan));
01000 
01001    if (ast_set_write_format(chan, ast_format_slin)) {
01002       ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", ast_channel_name(chan));
01003       ao2_cleanup(ls->origwfmt);
01004       ast_free(ls);
01005       ls = params = NULL;
01006    }
01007 
01008    return params;
01009 }
01010 
01011 static struct ast_generator linearstream =
01012 {
01013    .alloc = linear_alloc,
01014    .release = linear_release,
01015    .generate = linear_generator,
01016 };
01017 
01018 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
01019 {
01020    struct linear_state *lin;
01021    char tmpf[256];
01022    int res = -1;
01023    int autoclose = 0;
01024    if (fd < 0) {
01025       if (ast_strlen_zero(filename)) {
01026          return -1;
01027       }
01028       autoclose = 1;
01029       if (filename[0] == '/') {
01030          ast_copy_string(tmpf, filename, sizeof(tmpf));
01031       } else {
01032          snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", ast_config_AST_DATA_DIR, "sounds", filename);
01033       }
01034       if ((fd = open(tmpf, O_RDONLY)) < 0) {
01035          ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
01036          return -1;
01037       }
01038    }
01039    if ((lin = ast_calloc(1, sizeof(*lin)))) {
01040       lin->fd = fd;
01041       lin->allowoverride = allowoverride;
01042       lin->autoclose = autoclose;
01043       res = ast_activate_generator(chan, &linearstream, lin);
01044    }
01045    return res;
01046 }
01047 
01048 static int control_streamfile(struct ast_channel *chan,
01049    const char *file,
01050    const char *fwd,
01051    const char *rev,
01052    const char *stop,
01053    const char *suspend,
01054    const char *restart,
01055    int skipms,
01056    long *offsetms,
01057    const char *lang,
01058    ast_waitstream_fr_cb cb)
01059 {
01060    char *breaks = NULL;
01061    char *end = NULL;
01062    int blen = 2;
01063    int res;
01064    long pause_restart_point = 0;
01065    long offset = 0;
01066 
01067    if (!file) {
01068       return -1;
01069    }
01070    if (offsetms) {
01071       offset = *offsetms * 8; /* XXX Assumes 8kHz */
01072    }
01073    if (lang == NULL) {
01074       lang = ast_channel_language(chan);
01075    }
01076 
01077    if (stop) {
01078       blen += strlen(stop);
01079    }
01080    if (suspend) {
01081       blen += strlen(suspend);
01082    }
01083    if (restart) {
01084       blen += strlen(restart);
01085    }
01086 
01087    if (blen > 2) {
01088       breaks = ast_alloca(blen + 1);
01089       breaks[0] = '\0';
01090       if (stop) {
01091          strcat(breaks, stop);
01092       }
01093       if (suspend) {
01094          strcat(breaks, suspend);
01095       }
01096       if (restart) {
01097          strcat(breaks, restart);
01098       }
01099    }
01100 
01101    if ((end = strchr(file, ':'))) {
01102       if (!strcasecmp(end, ":end")) {
01103          *end = '\0';
01104          end++;
01105       }
01106    }
01107 
01108    for (;;) {
01109       ast_stopstream(chan);
01110       res = ast_streamfile(chan, file, lang);
01111       if (!res) {
01112          if (pause_restart_point) {
01113             ast_seekstream(ast_channel_stream(chan), pause_restart_point, SEEK_SET);
01114             pause_restart_point = 0;
01115          }
01116          else if (end || offset < 0) {
01117             if (offset == -8) {
01118                offset = 0;
01119             }
01120             ast_verb(3, "ControlPlayback seek to offset %ld from end\n", offset);
01121 
01122             ast_seekstream(ast_channel_stream(chan), offset, SEEK_END);
01123             end = NULL;
01124             offset = 0;
01125          } else if (offset) {
01126             ast_verb(3, "ControlPlayback seek to offset %ld\n", offset);
01127             ast_seekstream(ast_channel_stream(chan), offset, SEEK_SET);
01128             offset = 0;
01129          }
01130          if (cb) {
01131             res = ast_waitstream_fr_w_cb(chan, breaks, fwd, rev, skipms, cb);
01132          } else {
01133             res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
01134          }
01135       }
01136 
01137       if (res < 1) {
01138          break;
01139       }
01140 
01141       /* We go at next loop if we got the restart char */
01142       if ((restart && strchr(restart, res)) || res == AST_CONTROL_STREAM_RESTART) {
01143          ast_debug(1, "we'll restart the stream here at next loop\n");
01144          pause_restart_point = 0;
01145          ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
01146             "Control: %s\r\n",
01147             ast_channel_name(chan),
01148             "Restart");
01149          continue;
01150       }
01151 
01152       if ((suspend && strchr(suspend, res)) || res == AST_CONTROL_STREAM_SUSPEND) {
01153          pause_restart_point = ast_tellstream(ast_channel_stream(chan));
01154          ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
01155             "Control: %s\r\n",
01156             ast_channel_name(chan),
01157             "Pause");
01158          for (;;) {
01159             ast_stopstream(chan);
01160             if (!(res = ast_waitfordigit(chan, 1000))) {
01161                continue;
01162             } else if (res == -1 || (suspend && strchr(suspend, res)) || (stop && strchr(stop, res))
01163                   || res == AST_CONTROL_STREAM_SUSPEND || res == AST_CONTROL_STREAM_STOP) {
01164                break;
01165             }
01166          }
01167          if ((suspend && (res == *suspend)) || res == AST_CONTROL_STREAM_SUSPEND) {
01168             res = 0;
01169             ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
01170                "Control: %s\r\n",
01171                ast_channel_name(chan),
01172                "Unpause");
01173             continue;
01174          }
01175       }
01176 
01177       if (res == -1) {
01178          break;
01179       }
01180 
01181       /* if we get one of our stop chars, return it to the calling function */
01182       if ((stop && strchr(stop, res)) || res == AST_CONTROL_STREAM_STOP) {
01183          ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
01184             "Control: %s\r\n",
01185             ast_channel_name(chan),
01186             "Stop");
01187          break;
01188       }
01189    }
01190 
01191    if (pause_restart_point) {
01192       offset = pause_restart_point;
01193    } else {
01194       if (ast_channel_stream(chan)) {
01195          offset = ast_tellstream(ast_channel_stream(chan));
01196       } else {
01197          offset = -8;  /* indicate end of file */
01198       }
01199    }
01200 
01201    if (offsetms) {
01202       *offsetms = offset / 8; /* samples --> ms ... XXX Assumes 8 kHz */
01203    }
01204 
01205    ast_stopstream(chan);
01206 
01207    return res;
01208 }
01209 
01210 int ast_control_streamfile_w_cb(struct ast_channel *chan,
01211    const char *file,
01212    const char *fwd,
01213    const char *rev,
01214    const char *stop,
01215    const char *suspend,
01216    const char *restart,
01217    int skipms,
01218    long *offsetms,
01219    ast_waitstream_fr_cb cb)
01220 {
01221    return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL, cb);
01222 }
01223 
01224 int ast_control_streamfile(struct ast_channel *chan, const char *file,
01225             const char *fwd, const char *rev,
01226             const char *stop, const char *suspend,
01227             const char *restart, int skipms, long *offsetms)
01228 {
01229    return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL, NULL);
01230 }
01231 
01232 int ast_control_streamfile_lang(struct ast_channel *chan, const char *file,
01233    const char *fwd, const char *rev, const char *stop, const char *suspend,
01234    const char *restart, int skipms, const char *lang, long *offsetms)
01235 {
01236    return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, lang, NULL);
01237 }
01238 
01239 enum control_tone_frame_response_result {
01240    CONTROL_TONE_RESPONSE_FAILED = -1,
01241    CONTROL_TONE_RESPONSE_NORMAL = 0,
01242    CONTROL_TONE_RESPONSE_FINISHED = 1,
01243 };
01244 
01245 static enum control_tone_frame_response_result control_tone_frame_response(struct ast_channel *chan, struct ast_frame *fr, struct ast_tone_zone_sound *ts, const char *tone, int *paused)
01246 {
01247    switch (fr->subclass.integer) {
01248    case AST_CONTROL_STREAM_STOP:
01249       ast_playtones_stop(chan);
01250       return CONTROL_TONE_RESPONSE_FINISHED;
01251    case AST_CONTROL_STREAM_SUSPEND:
01252       if (*paused) {
01253          *paused = 0;
01254          if (ast_playtones_start(chan, 0, ts ? ts->data : tone, 0)) {
01255             return CONTROL_TONE_RESPONSE_FAILED;
01256          }
01257       } else {
01258          *paused = 1;
01259          ast_playtones_stop(chan);
01260       }
01261       return CONTROL_TONE_RESPONSE_NORMAL;
01262    case AST_CONTROL_STREAM_RESTART:
01263       ast_playtones_stop(chan);
01264       if (ast_playtones_start(chan, 0, ts ? ts->data : tone, 0)) {
01265          return CONTROL_TONE_RESPONSE_FAILED;
01266       }
01267       return CONTROL_TONE_RESPONSE_NORMAL;
01268    case AST_CONTROL_STREAM_REVERSE:
01269       ast_log(LOG_NOTICE, "Media control operation 'reverse' not supported for media type 'tone'\n");
01270       return CONTROL_TONE_RESPONSE_NORMAL;
01271    case AST_CONTROL_STREAM_FORWARD:
01272       ast_log(LOG_NOTICE, "Media control operation 'forward' not supported for media type 'tone'\n");
01273       return CONTROL_TONE_RESPONSE_NORMAL;
01274    case AST_CONTROL_HANGUP:
01275    case AST_CONTROL_BUSY:
01276    case AST_CONTROL_CONGESTION:
01277       return CONTROL_TONE_RESPONSE_FINISHED;
01278    }
01279 
01280    return CONTROL_TONE_RESPONSE_NORMAL;
01281 }
01282 
01283 static int parse_tone_uri(char *tone_parser,
01284    const char **tone_indication,
01285    const char **tone_zone)
01286 {
01287    *tone_indication = strsep(&tone_parser, ";");
01288 
01289    if (ast_strlen_zero(tone_parser)) {
01290       /* Only the indication is included */
01291       return 0;
01292    }
01293 
01294    if (!(strncmp(tone_parser, "tonezone=", 9))) {
01295       *tone_zone = tone_parser + 9;
01296    } else {
01297       ast_log(LOG_ERROR, "Unexpected Tone URI component: %s\n", tone_parser);
01298       return -1;
01299    }
01300 
01301    return 0;
01302 }
01303 
01304 int ast_control_tone(struct ast_channel *chan, const char *tone)
01305 {
01306    struct ast_tone_zone *zone = NULL;
01307    struct ast_tone_zone_sound *ts;
01308    int paused = 0;
01309    int res = 0;
01310 
01311    const char *tone_indication = NULL;
01312    const char *tone_zone = NULL;
01313    char *tone_uri_parser;
01314 
01315    if (ast_strlen_zero(tone)) {
01316       return -1;
01317    }
01318 
01319    tone_uri_parser = ast_strdupa(tone);
01320 
01321    if (parse_tone_uri(tone_uri_parser, &tone_indication, &tone_zone)) {
01322       return -1;
01323    }
01324 
01325    if (tone_zone) {
01326       zone = ast_get_indication_zone(tone_zone);
01327    }
01328 
01329    ts = ast_get_indication_tone(zone ? zone : ast_channel_zone(chan), tone_indication);
01330 
01331    if (ast_playtones_start(chan, 0, ts ? ts->data : tone_indication, 0)) {
01332       return -1;
01333    }
01334 
01335    for (;;) {
01336       struct ast_frame *fr;
01337 
01338       if (ast_waitfor(chan, -1) < 0) {
01339          res = -1;
01340          break;
01341       }
01342 
01343       fr = ast_read_noaudio(chan);
01344 
01345       if (!fr) {
01346          res = -1;
01347          break;
01348       }
01349 
01350       if (fr->frametype != AST_FRAME_CONTROL) {
01351          continue;
01352       }
01353 
01354       res = control_tone_frame_response(chan, fr, ts, tone_indication, &paused);
01355       if (res == CONTROL_TONE_RESPONSE_FINISHED) {
01356          res = 0;
01357          break;
01358       } else if (res == CONTROL_TONE_RESPONSE_FAILED) {
01359          res = -1;
01360          break;
01361       }
01362    }
01363 
01364    if (ts) {
01365       ast_tone_zone_sound_unref(ts);
01366    }
01367 
01368    if (zone) {
01369       ast_tone_zone_unref(zone);
01370    }
01371 
01372    return res;
01373 }
01374 
01375 int ast_play_and_wait(struct ast_channel *chan, const char *fn)
01376 {
01377    int d = 0;
01378 
01379    if ((d = ast_streamfile(chan, fn, ast_channel_language(chan)))) {
01380       return d;
01381    }
01382 
01383    d = ast_waitstream(chan, AST_DIGIT_ANY);
01384 
01385    ast_stopstream(chan);
01386 
01387    return d;
01388 }
01389 
01390 /*!
01391  * \brief Construct a silence frame of the same duration as \a orig.
01392  *
01393  * The \a orig frame must be \ref AST_FORMAT_SLINEAR.
01394  *
01395  * \param orig Frame as basis for silence to generate.
01396  * \return New frame of silence; free with ast_frfree().
01397  * \return \c NULL on error.
01398  */
01399 static struct ast_frame *make_silence(const struct ast_frame *orig)
01400 {
01401    struct ast_frame *silence;
01402    size_t size;
01403    size_t datalen;
01404    size_t samples = 0;
01405    struct ast_frame *next;
01406 
01407    if (!orig) {
01408       return NULL;
01409    }
01410 
01411    if (ast_format_cmp(orig->subclass.format, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {
01412       ast_log(LOG_WARNING, "Attempting to silence non-slin frame\n");
01413       return NULL;
01414    }
01415 
01416    for (next = AST_LIST_NEXT(orig, frame_list);
01417        orig;
01418        orig = next, next = orig ? AST_LIST_NEXT(orig, frame_list) : NULL) {
01419       samples += orig->samples;
01420    }
01421 
01422    ast_verb(4, "Silencing %zu samples\n", samples);
01423 
01424 
01425    datalen = sizeof(short) * samples;
01426    size = sizeof(*silence) + datalen;
01427    silence = ast_calloc(1, size);
01428    if (!silence) {
01429       return NULL;
01430    }
01431 
01432    silence->mallocd = AST_MALLOCD_HDR;
01433    silence->frametype = AST_FRAME_VOICE;
01434    silence->data.ptr = (void *)(silence + 1);
01435    silence->samples = samples;
01436    silence->datalen = datalen;
01437 
01438    silence->subclass.format = ast_format_slin;
01439 
01440    return silence;
01441 }
01442 
01443 /*!
01444  * \brief Sets a channel's read format to \ref AST_FORMAT_SLINEAR, recording
01445  * its original format.
01446  *
01447  * \param chan Channel to modify.
01448  * \param[out] orig_format Output variable to store channel's original read
01449  *                         format.
01450  * \return 0 on success.
01451  * \return -1 on error.
01452  */
01453 static int set_read_to_slin(struct ast_channel *chan, struct ast_format **orig_format)
01454 {
01455    if (!chan || !orig_format) {
01456       return -1;
01457    }
01458    *orig_format = ao2_bump(ast_channel_readformat(chan));
01459    return ast_set_read_format(chan, ast_format_slin);
01460 }
01461 
01462 static int global_silence_threshold = 128;
01463 static int global_maxsilence = 0;
01464 
01465 /*! Optionally play a sound file or a beep, then record audio and video from the channel.
01466  * \param chan Channel to playback to/record from.
01467  * \param playfile Filename of sound to play before recording begins.
01468  * \param recordfile Filename to record to.
01469  * \param maxtime Maximum length of recording (in seconds).
01470  * \param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
01471  * \param duration Where to store actual length of the recorded message (in milliseconds).
01472  * \param sound_duration Where to store the length of the recorded message (in milliseconds), minus any silence
01473  * \param beep Whether to play a beep before starting to record.
01474  * \param silencethreshold
01475  * \param maxsilence Length of silence that will end a recording (in milliseconds).
01476  * \param path Optional filesystem path to unlock.
01477  * \param prepend If true, prepend the recorded audio to an existing file and follow prepend mode recording rules
01478  * \param acceptdtmf DTMF digits that will end the recording.
01479  * \param canceldtmf DTMF digits that will cancel the recording.
01480  * \param skip_confirmation_sound If true, don't play auth-thankyou at end. Nice for custom recording prompts in apps.
01481  *
01482  * \retval -1 failure or hangup
01483  * \retval 'S' Recording ended from silence timeout
01484  * \retval 't' Recording ended from the message exceeding the maximum duration, or via DTMF in prepend mode
01485  * \retval dtmfchar Recording ended via the return value's DTMF character for either cancel or accept.
01486  */
01487 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)
01488 {
01489    int d = 0;
01490    char *fmts;
01491    char comment[256];
01492    int x, fmtcnt = 1, res = -1, outmsg = 0;
01493    struct ast_filestream *others[AST_MAX_FORMATS];
01494    char *sfmt[AST_MAX_FORMATS];
01495    char *stringp = NULL;
01496    time_t start, end;
01497    struct ast_dsp *sildet = NULL;   /* silence detector dsp */
01498    int totalsilence = 0;
01499    int dspsilence = 0;
01500    int olddspsilence = 0;
01501    struct ast_format *rfmt = NULL;
01502    struct ast_silence_generator *silgen = NULL;
01503    char prependfile[PATH_MAX];
01504    int ioflags;   /* IO flags for writing output file */
01505 
01506    ioflags = O_CREAT|O_WRONLY;
01507 
01508    switch (if_exists) {
01509    case AST_RECORD_IF_EXISTS_FAIL:
01510       ioflags |= O_EXCL;
01511       break;
01512    case AST_RECORD_IF_EXISTS_OVERWRITE:
01513       ioflags |= O_TRUNC;
01514       break;
01515    case AST_RECORD_IF_EXISTS_APPEND:
01516       ioflags |= O_APPEND;
01517       break;
01518    case AST_RECORD_IF_EXISTS_ERROR:
01519       ast_assert(0);
01520       break;
01521    }
01522 
01523    if (silencethreshold < 0) {
01524       silencethreshold = global_silence_threshold;
01525    }
01526 
01527    if (maxsilence < 0) {
01528       maxsilence = global_maxsilence;
01529    }
01530 
01531    /* barf if no pointer passed to store duration in */
01532    if (!duration) {
01533       ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
01534       return -1;
01535    }
01536 
01537    ast_debug(1, "play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
01538    snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, ast_channel_name(chan));
01539 
01540    if (playfile || beep) {
01541       if (!beep) {
01542          d = ast_play_and_wait(chan, playfile);
01543       }
01544       if (d > -1) {
01545          d = ast_stream_and_wait(chan, "beep", "");
01546       }
01547       if (d < 0) {
01548          return -1;
01549       }
01550    }
01551 
01552    if (prepend) {
01553       ast_copy_string(prependfile, recordfile, sizeof(prependfile));
01554       strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
01555    }
01556 
01557    fmts = ast_strdupa(fmt);
01558 
01559    stringp = fmts;
01560    strsep(&stringp, "|");
01561    ast_debug(1, "Recording Formats: sfmts=%s\n", fmts);
01562    sfmt[0] = ast_strdupa(fmts);
01563 
01564    while ((fmt = strsep(&stringp, "|"))) {
01565       if (fmtcnt > AST_MAX_FORMATS - 1) {
01566          ast_log(LOG_WARNING, "Please increase AST_MAX_FORMATS in file.h\n");
01567          break;
01568       }
01569       sfmt[fmtcnt++] = ast_strdupa(fmt);
01570    }
01571 
01572    end = start = time(NULL);  /* pre-initialize end to be same as start in case we never get into loop */
01573    for (x = 0; x < fmtcnt; x++) {
01574       others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, ioflags, 0, AST_FILE_MODE);
01575       ast_verb(3, "x=%d, open writing:  %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
01576 
01577       if (!others[x]) {
01578          break;
01579       }
01580    }
01581 
01582    if (path) {
01583       ast_unlock_path(path);
01584    }
01585 
01586    if (maxsilence > 0) {
01587       sildet = ast_dsp_new(); /* Create the silence detector */
01588       if (!sildet) {
01589          ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
01590          return -1;
01591       }
01592       ast_dsp_set_threshold(sildet, silencethreshold);
01593       res = set_read_to_slin(chan, &rfmt);
01594       if (res < 0) {
01595          ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
01596          ast_dsp_free(sildet);
01597          ao2_cleanup(rfmt);
01598          return -1;
01599       }
01600    }
01601 
01602    if (!prepend) {
01603       /* Request a video update */
01604       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
01605 
01606       if (ast_opt_transmit_silence) {
01607          silgen = ast_channel_start_silence_generator(chan);
01608       }
01609    }
01610 
01611    if (x == fmtcnt) {
01612       /* Loop, writing the packets we read to the writer(s), until
01613        * we have reason to stop. */
01614       struct ast_frame *f;
01615       int paused = 0;
01616       int muted = 0;
01617       time_t pause_start = 0;
01618       int paused_secs = 0;
01619       int pausedsilence = 0;
01620 
01621       for (;;) {
01622          if (!(res = ast_waitfor(chan, 2000))) {
01623             ast_debug(1, "One waitfor failed, trying another\n");
01624             /* Try one more time in case of masq */
01625             if (!(res = ast_waitfor(chan, 2000))) {
01626                ast_log(LOG_WARNING, "No audio available on %s??\n", ast_channel_name(chan));
01627                res = -1;
01628             }
01629          }
01630 
01631          if (res < 0) {
01632             f = NULL;
01633             break;
01634          }
01635          if (!(f = ast_read(chan))) {
01636             break;
01637          }
01638          if (f->frametype == AST_FRAME_VOICE) {
01639             /* write each format */
01640             if (paused) {
01641                /* It's all good */
01642                res = 0;
01643             } else {
01644                RAII_VAR(struct ast_frame *, silence, NULL, ast_frame_dtor);
01645                struct ast_frame *orig = f;
01646 
01647                if (muted) {
01648                   silence = make_silence(orig);
01649                   if (!silence) {
01650                      ast_log(LOG_WARNING,
01651                         "Error creating silence\n");
01652                      break;
01653                   }
01654                   f = silence;
01655                }
01656                for (x = 0; x < fmtcnt; x++) {
01657                   if (prepend && !others[x]) {
01658                      break;
01659                   }
01660                   res = ast_writestream(others[x], f);
01661                }
01662                f = orig;
01663             }
01664 
01665             /* Silence Detection */
01666             if (maxsilence > 0) {
01667                dspsilence = 0;
01668                ast_dsp_silence(sildet, f, &dspsilence);
01669                if (olddspsilence > dspsilence) {
01670                   totalsilence += olddspsilence;
01671                }
01672                olddspsilence = dspsilence;
01673 
01674                if (paused) {
01675                   /* record how much silence there was while we are paused */
01676                   pausedsilence = dspsilence;
01677                } else if (dspsilence > pausedsilence) {
01678                   /* ignore the paused silence */
01679                   dspsilence -= pausedsilence;
01680                } else {
01681                   /* dspsilence has reset, reset pausedsilence */
01682                   pausedsilence = 0;
01683                }
01684 
01685                if (dspsilence > maxsilence) {
01686                   /* Ended happily with silence */
01687                   ast_verb(3, "Recording automatically stopped after a silence of %d seconds\n", dspsilence/1000);
01688                   res = 'S';
01689                   outmsg = 2;
01690                   break;
01691                }
01692             }
01693             /* Exit on any error */
01694             if (res) {
01695                ast_log(LOG_WARNING, "Error writing frame\n");
01696                break;
01697             }
01698          } else if (f->frametype == AST_FRAME_VIDEO) {
01699             /* Write only once */
01700             ast_writestream(others[0], f);
01701          } else if (f->frametype == AST_FRAME_DTMF) {
01702             if (prepend) {
01703             /* stop recording with any digit */
01704                ast_verb(3, "User ended message by pressing %c\n", f->subclass.integer);
01705                res = 't';
01706                outmsg = 2;
01707                break;
01708             }
01709             if (strchr(acceptdtmf, f->subclass.integer)) {
01710                ast_verb(3, "User ended message by pressing %c\n", f->subclass.integer);
01711                res = f->subclass.integer;
01712                outmsg = 2;
01713                break;
01714             }
01715             if (strchr(canceldtmf, f->subclass.integer)) {
01716                ast_verb(3, "User canceled message by pressing %c\n", f->subclass.integer);
01717                res = f->subclass.integer;
01718                outmsg = 0;
01719                break;
01720             }
01721          } else if (f->frametype == AST_FRAME_CONTROL) {
01722             if (f->subclass.integer == AST_CONTROL_RECORD_CANCEL) {
01723                ast_verb(3, "Message canceled by control\n");
01724                outmsg = 0; /* cancels the recording */
01725                res = 0;
01726                break;
01727             } else if (f->subclass.integer == AST_CONTROL_RECORD_STOP) {
01728                ast_verb(3, "Message ended by control\n");
01729                res = 0;
01730                break;
01731             } else if (f->subclass.integer == AST_CONTROL_RECORD_SUSPEND) {
01732                paused = !paused;
01733                ast_verb(3, "Message %spaused by control\n",
01734                   paused ? "" : "un");
01735                if (paused) {
01736                   pause_start = time(NULL);
01737                } else {
01738                   paused_secs += time(NULL) - pause_start;
01739                }
01740             } else if (f->subclass.integer == AST_CONTROL_RECORD_MUTE) {
01741                muted = !muted;
01742                ast_verb(3, "Message %smuted by control\n",
01743                   muted ? "" : "un");
01744                /* We can only silence slin frames, so
01745                 * set the mode, if we haven't already
01746                 * for sildet
01747                 */
01748                if (muted && !rfmt) {
01749                   ast_verb(3, "Setting read format to linear mode\n");
01750                   res = set_read_to_slin(chan, &rfmt);
01751                   if (res < 0) {
01752                      ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
01753                      break;
01754                   }
01755                }
01756             }
01757          }
01758          if (maxtime && !paused) {
01759             end = time(NULL);
01760             if (maxtime < (end - start - paused_secs)) {
01761                ast_verb(3, "Took too long, cutting it short...\n");
01762                res = 't';
01763                outmsg = 2;
01764                break;
01765             }
01766          }
01767          ast_frfree(f);
01768       }
01769       if (!f) {
01770          ast_verb(3, "User hung up\n");
01771          res = -1;
01772          outmsg = 1;
01773       } else {
01774          ast_frfree(f);
01775       }
01776    } else {
01777       ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
01778    }
01779 
01780    if (!prepend) {
01781       if (silgen) {
01782          ast_channel_stop_silence_generator(chan, silgen);
01783       }
01784    }
01785 
01786    /*!\note
01787     * Instead of asking how much time passed (end - start), calculate the number
01788     * of seconds of audio which actually went into the file.  This fixes a
01789     * problem where audio is stopped up on the network and never gets to us.
01790     *
01791     * Note that we still want to use the number of seconds passed for the max
01792     * message, otherwise we could get a situation where this stream is never
01793     * closed (which would create a resource leak).
01794     */
01795    *duration = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
01796    if (sound_duration) {
01797       *sound_duration = *duration;
01798    }
01799 
01800    if (!prepend) {
01801       /* Reduce duration by a total silence amount */
01802       if (olddspsilence <= dspsilence) {
01803          totalsilence += dspsilence;
01804       }
01805 
01806       if (sound_duration) {
01807          if (totalsilence > 0) {
01808             *sound_duration -= (totalsilence - 200) / 1000;
01809          }
01810          if (*sound_duration < 0) {
01811             *sound_duration = 0;
01812          }
01813       }
01814 
01815       if (dspsilence > 0) {
01816          *duration -= (dspsilence - 200) / 1000;
01817       }
01818 
01819       if (*duration < 0) {
01820          *duration = 0;
01821       }
01822 
01823       for (x = 0; x < fmtcnt; x++) {
01824          if (!others[x]) {
01825             break;
01826          }
01827          /*!\note
01828           * If we ended with silence, trim all but the first 200ms of silence
01829           * off the recording.  However, if we ended with '#', we don't want
01830           * to trim ANY part of the recording.
01831           */
01832          if (res > 0 && dspsilence) {
01833             /* rewind only the trailing silence */
01834             ast_stream_rewind(others[x], dspsilence - 200);
01835          }
01836          ast_truncstream(others[x]);
01837          ast_closestream(others[x]);
01838       }
01839    } else if (prepend && outmsg) {
01840       struct ast_filestream *realfiles[AST_MAX_FORMATS];
01841       struct ast_frame *fr;
01842 
01843       for (x = 0; x < fmtcnt; x++) {
01844          snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
01845          realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
01846          if (!others[x]) {
01847             break;
01848          }
01849          if (!realfiles[x]) {
01850             ast_closestream(others[x]);
01851             continue;
01852          }
01853          /*!\note Same logic as above. */
01854          if (dspsilence) {
01855             ast_stream_rewind(others[x], dspsilence - 200);
01856          }
01857          ast_truncstream(others[x]);
01858          /* add the original file too */
01859          while ((fr = ast_readframe(realfiles[x]))) {
01860             ast_writestream(others[x], fr);
01861             ast_frfree(fr);
01862          }
01863          ast_closestream(others[x]);
01864          ast_closestream(realfiles[x]);
01865          ast_filerename(prependfile, recordfile, sfmt[x]);
01866          ast_verb(4, "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
01867          ast_filedelete(prependfile, sfmt[x]);
01868       }
01869    } else {
01870       for (x = 0; x < fmtcnt; x++) {
01871          if (!others[x]) {
01872             break;
01873          }
01874          ast_closestream(others[x]);
01875       }
01876    }
01877 
01878    if (rfmt && ast_set_read_format(chan, rfmt)) {
01879       ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_format_get_name(rfmt), ast_channel_name(chan));
01880    }
01881    ao2_cleanup(rfmt);
01882    if ((outmsg == 2) && (!skip_confirmation_sound)) {
01883       ast_stream_and_wait(chan, "auth-thankyou", "");
01884    }
01885    if (sildet) {
01886       ast_dsp_free(sildet);
01887    }
01888    return res;
01889 }
01890 
01891 static const char default_acceptdtmf[] = "#";
01892 static const char default_canceldtmf[] = "";
01893 
01894 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)
01895 {
01896    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);
01897 }
01898 
01899 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)
01900 {
01901    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);
01902 }
01903 
01904 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)
01905 {
01906    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);
01907 }
01908 
01909 /* Channel group core functions */
01910 
01911 int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
01912 {
01913    int res = 0;
01914    char tmp[256];
01915    char *grp = NULL, *cat = NULL;
01916 
01917    if (!ast_strlen_zero(data)) {
01918       ast_copy_string(tmp, data, sizeof(tmp));
01919       grp = tmp;
01920       if ((cat = strchr(tmp, '@'))) {
01921          *cat++ = '\0';
01922       }
01923    }
01924 
01925    if (!ast_strlen_zero(grp)) {
01926       ast_copy_string(group, grp, group_max);
01927    } else {
01928       *group = '\0';
01929    }
01930 
01931    if (!ast_strlen_zero(cat)) {
01932       ast_copy_string(category, cat, category_max);
01933    }
01934 
01935    return res;
01936 }
01937 
01938 int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
01939 {
01940    int res = 0;
01941    char group[80] = "", category[80] = "";
01942    struct ast_group_info *gi = NULL;
01943    size_t len = 0;
01944 
01945    if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
01946       return -1;
01947    }
01948 
01949    /* Calculate memory we will need if this is new */
01950    len = sizeof(*gi) + strlen(group) + 1;
01951    if (!ast_strlen_zero(category)) {
01952       len += strlen(category) + 1;
01953    }
01954 
01955    AST_RWLIST_WRLOCK(&groups);
01956    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
01957       if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
01958          AST_RWLIST_REMOVE_CURRENT(group_list);
01959          ast_free(gi);
01960          break;
01961       }
01962    }
01963    AST_RWLIST_TRAVERSE_SAFE_END;
01964 
01965    if (ast_strlen_zero(group)) {
01966       /* Enable unsetting the group */
01967    } else if ((gi = ast_calloc(1, len))) {
01968       gi->chan = chan;
01969       gi->group = (char *) gi + sizeof(*gi);
01970       strcpy(gi->group, group);
01971       if (!ast_strlen_zero(category)) {
01972          gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
01973          strcpy(gi->category, category);
01974       }
01975       AST_RWLIST_INSERT_TAIL(&groups, gi, group_list);
01976    } else {
01977       res = -1;
01978    }
01979 
01980    AST_RWLIST_UNLOCK(&groups);
01981 
01982    return res;
01983 }
01984 
01985 int ast_app_group_get_count(const char *group, const char *category)
01986 {
01987    struct ast_group_info *gi = NULL;
01988    int count = 0;
01989 
01990    if (ast_strlen_zero(group)) {
01991       return 0;
01992    }
01993 
01994    AST_RWLIST_RDLOCK(&groups);
01995    AST_RWLIST_TRAVERSE(&groups, gi, group_list) {
01996       if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
01997          count++;
01998       }
01999    }
02000    AST_RWLIST_UNLOCK(&groups);
02001 
02002    return count;
02003 }
02004 
02005 int ast_app_group_match_get_count(const char *groupmatch, const char *category)
02006 {
02007    struct ast_group_info *gi = NULL;
02008    regex_t regexbuf_group;
02009    regex_t regexbuf_category;
02010    int count = 0;
02011 
02012    if (ast_strlen_zero(groupmatch)) {
02013       ast_log(LOG_NOTICE, "groupmatch empty\n");
02014       return 0;
02015    }
02016 
02017    /* if regex compilation fails, return zero matches */
02018    if (regcomp(&regexbuf_group, groupmatch, REG_EXTENDED | REG_NOSUB)) {
02019       ast_log(LOG_ERROR, "Regex compile failed on: %s\n", groupmatch);
02020       return 0;
02021    }
02022 
02023    if (!ast_strlen_zero(category) && regcomp(&regexbuf_category, category, REG_EXTENDED | REG_NOSUB)) {
02024       ast_log(LOG_ERROR, "Regex compile failed on: %s\n", category);
02025       regfree(&regexbuf_group);
02026       return 0;
02027    }
02028 
02029    AST_RWLIST_RDLOCK(&groups);
02030    AST_RWLIST_TRAVERSE(&groups, gi, group_list) {
02031       if (!regexec(&regexbuf_group, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !regexec(&regexbuf_category, gi->category, 0, NULL, 0)))) {
02032          count++;
02033       }
02034    }
02035    AST_RWLIST_UNLOCK(&groups);
02036 
02037    regfree(&regexbuf_group);
02038    if (!ast_strlen_zero(category)) {
02039       regfree(&regexbuf_category);
02040    }
02041 
02042    return count;
02043 }
02044 
02045 int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
02046 {
02047    struct ast_group_info *gi = NULL;
02048 
02049    AST_RWLIST_WRLOCK(&groups);
02050    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
02051       if (gi->chan == old) {
02052          gi->chan = new;
02053       } else if (gi->chan == new) {
02054          AST_RWLIST_REMOVE_CURRENT(group_list);
02055          ast_free(gi);
02056       }
02057    }
02058    AST_RWLIST_TRAVERSE_SAFE_END;
02059    AST_RWLIST_UNLOCK(&groups);
02060 
02061    return 0;
02062 }
02063 
02064 int ast_app_group_discard(struct ast_channel *chan)
02065 {
02066    struct ast_group_info *gi = NULL;
02067 
02068    AST_RWLIST_WRLOCK(&groups);
02069    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
02070       if (gi->chan == chan) {
02071          AST_RWLIST_REMOVE_CURRENT(group_list);
02072          ast_free(gi);
02073       }
02074    }
02075    AST_RWLIST_TRAVERSE_SAFE_END;
02076    AST_RWLIST_UNLOCK(&groups);
02077 
02078    return 0;
02079 }
02080 
02081 int ast_app_group_list_wrlock(void)
02082 {
02083    return AST_RWLIST_WRLOCK(&groups);
02084 }
02085 
02086 int ast_app_group_list_rdlock(void)
02087 {
02088    return AST_RWLIST_RDLOCK(&groups);
02089 }
02090 
02091 struct ast_group_info *ast_app_group_list_head(void)
02092 {
02093    return AST_RWLIST_FIRST(&groups);
02094 }
02095 
02096 int ast_app_group_list_unlock(void)
02097 {
02098    return AST_RWLIST_UNLOCK(&groups);
02099 }
02100 
02101 #undef ast_app_separate_args
02102 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen);
02103 
02104 unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, char **array, int arraylen)
02105 {
02106    int argc;
02107    char *scan, *wasdelim = NULL;
02108    int paren = 0, quote = 0, bracket = 0;
02109 
02110    if (!array || !arraylen) {
02111       return 0;
02112    }
02113 
02114    memset(array, 0, arraylen * sizeof(*array));
02115 
02116    if (!buf) {
02117       return 0;
02118    }
02119 
02120    scan = buf;
02121 
02122    for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
02123       array[argc] = scan;
02124       for (; *scan; scan++) {
02125          if (*scan == '(') {
02126             paren++;
02127          } else if (*scan == ')') {
02128             if (paren) {
02129                paren--;
02130             }
02131          } else if (*scan == '[') {
02132             bracket++;
02133          } else if (*scan == ']') {
02134             if (bracket) {
02135                bracket--;
02136             }
02137          } else if (*scan == '"' && delim != '"') {
02138             quote = quote ? 0 : 1;
02139             if (remove_chars) {
02140                /* Remove quote character from argument */
02141                memmove(scan, scan + 1, strlen(scan));
02142                scan--;
02143             }
02144          } else if (*scan == '\\') {
02145             if (remove_chars) {
02146                /* Literal character, don't parse */
02147                memmove(scan, scan + 1, strlen(scan));
02148             } else {
02149                scan++;
02150             }
02151          } else if ((*scan == delim) && !paren && !quote && !bracket) {
02152             wasdelim = scan;
02153             *scan++ = '\0';
02154             break;
02155          }
02156       }
02157    }
02158 
02159    /* If the last character in the original string was the delimiter, then
02160     * there is one additional argument. */
02161    if (*scan || (scan > buf && (scan - 1) == wasdelim)) {
02162       array[argc++] = scan;
02163    }
02164 
02165    return argc;
02166 }
02167 
02168 /* ABI compatible function */
02169 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
02170 {
02171    return __ast_app_separate_args(buf, delim, 1, array, arraylen);
02172 }
02173 
02174 static enum AST_LOCK_RESULT ast_lock_path_lockfile(const char *path)
02175 {
02176    char *s;
02177    char *fs;
02178    int res;
02179    int fd;
02180    int lp = strlen(path);
02181    time_t start;
02182 
02183    s = ast_alloca(lp + 10);
02184    fs = ast_alloca(lp + 20);
02185 
02186    snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, (unsigned long)ast_random());
02187    fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
02188    if (fd < 0) {
02189       ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
02190       return AST_LOCK_PATH_NOT_FOUND;
02191    }
02192    close(fd);
02193 
02194    snprintf(s, strlen(path) + 9, "%s/.lock", path);
02195    start = time(NULL);
02196    while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5)) {
02197       sched_yield();
02198    }
02199 
02200    unlink(fs);
02201 
02202    if (res) {
02203       ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
02204       return AST_LOCK_TIMEOUT;
02205    } else {
02206       ast_debug(1, "Locked path '%s'\n", path);
02207       return AST_LOCK_SUCCESS;
02208    }
02209 }
02210 
02211 static int ast_unlock_path_lockfile(const char *path)
02212 {
02213    char *s;
02214    int res;
02215 
02216    s = ast_alloca(strlen(path) + 10);
02217 
02218    snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
02219 
02220    if ((res = unlink(s))) {
02221       ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
02222    } else {
02223       ast_debug(1, "Unlocked path '%s'\n", path);
02224    }
02225 
02226    return res;
02227 }
02228 
02229 struct path_lock {
02230    AST_LIST_ENTRY(path_lock) le;
02231    int fd;
02232    char *path;
02233 };
02234 
02235 static AST_LIST_HEAD_STATIC(path_lock_list, path_lock);
02236 
02237 static void path_lock_destroy(struct path_lock *obj)
02238 {
02239    if (obj->fd >= 0) {
02240       close(obj->fd);
02241    }
02242    if (obj->path) {
02243       ast_free(obj->path);
02244    }
02245    ast_free(obj);
02246 }
02247 
02248 static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path)
02249 {
02250    char *fs;
02251    int res;
02252    int fd;
02253    time_t start;
02254    struct path_lock *pl;
02255    struct stat st, ost;
02256 
02257    fs = ast_alloca(strlen(path) + 20);
02258 
02259    snprintf(fs, strlen(path) + 19, "%s/lock", path);
02260    if (lstat(fs, &st) == 0) {
02261       if ((st.st_mode & S_IFMT) == S_IFLNK) {
02262          ast_log(LOG_WARNING, "Unable to create lock file "
02263                "'%s': it's already a symbolic link\n",
02264                fs);
02265          return AST_LOCK_FAILURE;
02266       }
02267       if (st.st_nlink > 1) {
02268          ast_log(LOG_WARNING, "Unable to create lock file "
02269                "'%s': %u hard links exist\n",
02270                fs, (unsigned int) st.st_nlink);
02271          return AST_LOCK_FAILURE;
02272       }
02273    }
02274    if ((fd = open(fs, O_WRONLY | O_CREAT, 0600)) < 0) {
02275       ast_log(LOG_WARNING, "Unable to create lock file '%s': %s\n",
02276             fs, strerror(errno));
02277       return AST_LOCK_PATH_NOT_FOUND;
02278    }
02279    if (!(pl = ast_calloc(1, sizeof(*pl)))) {
02280       /* We don't unlink the lock file here, on the possibility that
02281        * someone else created it - better to leave a little mess
02282        * than create a big one by destroying someone else's lock
02283        * and causing something to be corrupted.
02284        */
02285       close(fd);
02286       return AST_LOCK_FAILURE;
02287    }
02288    pl->fd = fd;
02289    pl->path = ast_strdup(path);
02290 
02291    time(&start);
02292    while (
02293       #ifdef SOLARIS
02294       ((res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd, F_GETFL) | O_NONBLOCK)) < 0) &&
02295       #else
02296       ((res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
02297       #endif
02298          (errno == EWOULDBLOCK) &&
02299          (time(NULL) - start < 5))
02300       usleep(1000);
02301    if (res) {
02302       ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n",
02303             path, strerror(errno));
02304       /* No unlinking of lock done, since we tried and failed to
02305        * flock() it.
02306        */
02307       path_lock_destroy(pl);
02308       return AST_LOCK_TIMEOUT;
02309    }
02310 
02311    /* Check for the race where the file is recreated or deleted out from
02312     * underneath us.
02313     */
02314    if (lstat(fs, &st) != 0 && fstat(pl->fd, &ost) != 0 &&
02315          st.st_dev != ost.st_dev &&
02316          st.st_ino != ost.st_ino) {
02317       ast_log(LOG_WARNING, "Unable to create lock file '%s': "
02318             "file changed underneath us\n", fs);
02319       path_lock_destroy(pl);
02320       return AST_LOCK_FAILURE;
02321    }
02322 
02323    /* Success: file created, flocked, and is the one we started with */
02324    AST_LIST_LOCK(&path_lock_list);
02325    AST_LIST_INSERT_TAIL(&path_lock_list, pl, le);
02326    AST_LIST_UNLOCK(&path_lock_list);
02327 
02328    ast_debug(1, "Locked path '%s'\n", path);
02329 
02330    return AST_LOCK_SUCCESS;
02331 }
02332 
02333 static int ast_unlock_path_flock(const char *path)
02334 {
02335    char *s;
02336    struct path_lock *p;
02337 
02338    s = ast_alloca(strlen(path) + 20);
02339 
02340    AST_LIST_LOCK(&path_lock_list);
02341    AST_LIST_TRAVERSE_SAFE_BEGIN(&path_lock_list, p, le) {
02342       if (!strcmp(p->path, path)) {
02343          AST_LIST_REMOVE_CURRENT(le);
02344          break;
02345       }
02346    }
02347    AST_LIST_TRAVERSE_SAFE_END;
02348    AST_LIST_UNLOCK(&path_lock_list);
02349 
02350    if (p) {
02351       snprintf(s, strlen(path) + 19, "%s/lock", path);
02352       unlink(s);
02353       path_lock_destroy(p);
02354       ast_debug(1, "Unlocked path '%s'\n", path);
02355    } else {
02356       ast_debug(1, "Failed to unlock path '%s': "
02357             "lock not found\n", path);
02358    }
02359 
02360    return 0;
02361 }
02362 
02363 void ast_set_lock_type(enum AST_LOCK_TYPE type)
02364 {
02365    ast_lock_type = type;
02366 }
02367 
02368 enum AST_LOCK_RESULT ast_lock_path(const char *path)
02369 {
02370    enum AST_LOCK_RESULT r = AST_LOCK_FAILURE;
02371 
02372    switch (ast_lock_type) {
02373    case AST_LOCK_TYPE_LOCKFILE:
02374       r = ast_lock_path_lockfile(path);
02375       break;
02376    case AST_LOCK_TYPE_FLOCK:
02377       r = ast_lock_path_flock(path);
02378       break;
02379    }
02380 
02381    return r;
02382 }
02383 
02384 int ast_unlock_path(const char *path)
02385 {
02386    int r = 0;
02387 
02388    switch (ast_lock_type) {
02389    case AST_LOCK_TYPE_LOCKFILE:
02390       r = ast_unlock_path_lockfile(path);
02391       break;
02392    case AST_LOCK_TYPE_FLOCK:
02393       r = ast_unlock_path_flock(path);
02394       break;
02395    }
02396 
02397    return r;
02398 }
02399 
02400 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
02401 {
02402    int silencethreshold;
02403    int maxsilence = 0;
02404    int res = 0;
02405    int cmd = 0;
02406    int max_attempts = 3;
02407    int attempts = 0;
02408    int recorded = 0;
02409    int message_exists = 0;
02410    /* Note that urgent and private are for flagging messages as such in the future */
02411 
02412    /* barf if no pointer passed to store duration in */
02413    if (!duration) {
02414       ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
02415       return -1;
02416    }
02417 
02418    cmd = '3';   /* Want to start by recording */
02419 
02420    silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
02421 
02422    while ((cmd >= 0) && (cmd != 't')) {
02423       switch (cmd) {
02424       case '1':
02425          if (!message_exists) {
02426             /* In this case, 1 is to record a message */
02427             cmd = '3';
02428             break;
02429          } else {
02430             ast_stream_and_wait(chan, "vm-msgsaved", "");
02431             cmd = 't';
02432             return res;
02433          }
02434       case '2':
02435          /* Review */
02436          ast_verb(3, "Reviewing the recording\n");
02437          cmd = ast_stream_and_wait(chan, recordfile, AST_DIGIT_ANY);
02438          break;
02439       case '3':
02440          message_exists = 0;
02441          /* Record */
02442          ast_verb(3, "R%secording\n", recorded == 1 ? "e-r" : "");
02443          recorded = 1;
02444          if ((cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, NULL, silencethreshold, maxsilence, path)) == -1) {
02445             /* User has hung up, no options to give */
02446             return cmd;
02447          }
02448          if (cmd == '0') {
02449             break;
02450          } else if (cmd == '*') {
02451             break;
02452          } else {
02453             /* If all is well, a message exists */
02454             message_exists = 1;
02455             cmd = 0;
02456          }
02457          break;
02458       case '4':
02459       case '5':
02460       case '6':
02461       case '7':
02462       case '8':
02463       case '9':
02464       case '*':
02465       case '#':
02466          cmd = ast_play_and_wait(chan, "vm-sorry");
02467          break;
02468       default:
02469          if (message_exists) {
02470             cmd = ast_play_and_wait(chan, "vm-review");
02471          } else {
02472             if (!(cmd = ast_play_and_wait(chan, "vm-torerecord"))) {
02473                cmd = ast_waitfordigit(chan, 600);
02474             }
02475          }
02476 
02477          if (!cmd) {
02478             cmd = ast_waitfordigit(chan, 6000);
02479          }
02480          if (!cmd) {
02481             attempts++;
02482          }
02483          if (attempts > max_attempts) {
02484             cmd = 't';
02485          }
02486       }
02487    }
02488    if (cmd == 't') {
02489       cmd = 0;
02490    }
02491    return cmd;
02492 }
02493 
02494 #define RES_UPONE (1 << 16)
02495 #define RES_EXIT  (1 << 17)
02496 #define RES_REPEAT (1 << 18)
02497 #define RES_RESTART ((1 << 19) | RES_REPEAT)
02498 
02499 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
02500 
02501 static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
02502 {
02503    int res;
02504    int (*ivr_func)(struct ast_channel *, void *);
02505    char *c;
02506    char *n;
02507 
02508    switch (option->action) {
02509    case AST_ACTION_UPONE:
02510       return RES_UPONE;
02511    case AST_ACTION_EXIT:
02512       return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
02513    case AST_ACTION_REPEAT:
02514       return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
02515    case AST_ACTION_RESTART:
02516       return RES_RESTART ;
02517    case AST_ACTION_NOOP:
02518       return 0;
02519    case AST_ACTION_BACKGROUND:
02520       res = ast_stream_and_wait(chan, (char *)option->adata, AST_DIGIT_ANY);
02521       if (res < 0) {
02522          ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
02523          res = 0;
02524       }
02525       return res;
02526    case AST_ACTION_PLAYBACK:
02527       res = ast_stream_and_wait(chan, (char *)option->adata, "");
02528       if (res < 0) {
02529          ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
02530          res = 0;
02531       }
02532       return res;
02533    case AST_ACTION_MENU:
02534       if ((res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata)) == -2) {
02535          /* Do not pass entry errors back up, treat as though it was an "UPONE" */
02536          res = 0;
02537       }
02538       return res;
02539    case AST_ACTION_WAITOPTION:
02540       if (!(res = ast_waitfordigit(chan, ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 10000))) {
02541          return 't';
02542       }
02543       return res;
02544    case AST_ACTION_CALLBACK:
02545       ivr_func = option->adata;
02546       res = ivr_func(chan, cbdata);
02547       return res;
02548    case AST_ACTION_TRANSFER:
02549       res = ast_parseable_goto(chan, option->adata);
02550       return 0;
02551    case AST_ACTION_PLAYLIST:
02552    case AST_ACTION_BACKLIST:
02553       res = 0;
02554       c = ast_strdupa(option->adata);
02555       while ((n = strsep(&c, ";"))) {
02556          if ((res = ast_stream_and_wait(chan, n,
02557                (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : ""))) {
02558             break;
02559          }
02560       }
02561       ast_stopstream(chan);
02562       return res;
02563    default:
02564       ast_log(LOG_NOTICE, "Unknown dispatch function %u, ignoring!\n", option->action);
02565       return 0;
02566    }
02567    return -1;
02568 }
02569 
02570 static int option_exists(struct ast_ivr_menu *menu, char *option)
02571 {
02572    int x;
02573    for (x = 0; menu->options[x].option; x++) {
02574       if (!strcasecmp(menu->options[x].option, option)) {
02575          return x;
02576       }
02577    }
02578    return -1;
02579 }
02580 
02581 static int option_matchmore(struct ast_ivr_menu *menu, char *option)
02582 {
02583    int x;
02584    for (x = 0; menu->options[x].option; x++) {
02585       if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
02586             (menu->options[x].option[strlen(option)])) {
02587          return x;
02588       }
02589    }
02590    return -1;
02591 }
02592 
02593 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
02594 {
02595    int res = 0;
02596    int ms;
02597    while (option_matchmore(menu, exten)) {
02598       ms = ast_channel_pbx(chan) ? ast_channel_pbx(chan)->dtimeoutms : 5000;
02599       if (strlen(exten) >= maxexten - 1) {
02600          break;
02601       }
02602       if ((res = ast_waitfordigit(chan, ms)) < 1) {
02603          break;
02604       }
02605       exten[strlen(exten) + 1] = '\0';
02606       exten[strlen(exten)] = res;
02607    }
02608    return res > 0 ? 0 : res;
02609 }
02610 
02611 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
02612 {
02613    /* Execute an IVR menu structure */
02614    int res = 0;
02615    int pos = 0;
02616    int retries = 0;
02617    char exten[AST_MAX_EXTENSION] = "s";
02618    if (option_exists(menu, "s") < 0) {
02619       strcpy(exten, "g");
02620       if (option_exists(menu, "g") < 0) {
02621          ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
02622          return -1;
02623       }
02624    }
02625    while (!res) {
02626       while (menu->options[pos].option) {
02627          if (!strcasecmp(menu->options[pos].option, exten)) {
02628             res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
02629             ast_debug(1, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
02630             if (res < 0) {
02631                break;
02632             } else if (res & RES_UPONE) {
02633                return 0;
02634             } else if (res & RES_EXIT) {
02635                return res;
02636             } else if (res & RES_REPEAT) {
02637                int maxretries = res & 0xffff;
02638                if ((res & RES_RESTART) == RES_RESTART) {
02639                   retries = 0;
02640                } else {
02641                   retries++;
02642                }
02643                if (!maxretries) {
02644                   maxretries = 3;
02645                }
02646                if ((maxretries > 0) && (retries >= maxretries)) {
02647                   ast_debug(1, "Max retries %d exceeded\n", maxretries);
02648                   return -2;
02649                } else {
02650                   if (option_exists(menu, "g") > -1) {
02651                      strcpy(exten, "g");
02652                   } else if (option_exists(menu, "s") > -1) {
02653                      strcpy(exten, "s");
02654                   }
02655                }
02656                pos = 0;
02657                continue;
02658             } else if (res && strchr(AST_DIGIT_ANY, res)) {
02659                ast_debug(1, "Got start of extension, %c\n", res);
02660                exten[1] = '\0';
02661                exten[0] = res;
02662                if ((res = read_newoption(chan, menu, exten, sizeof(exten)))) {
02663                   break;
02664                }
02665                if (option_exists(menu, exten) < 0) {
02666                   if (option_exists(menu, "i")) {
02667                      ast_debug(1, "Invalid extension entered, going to 'i'!\n");
02668                      strcpy(exten, "i");
02669                      pos = 0;
02670                      continue;
02671                   } else {
02672                      ast_debug(1, "Aborting on invalid entry, with no 'i' option!\n");
02673                      res = -2;
02674                      break;
02675                   }
02676                } else {
02677                   ast_debug(1, "New existing extension: %s\n", exten);
02678                   pos = 0;
02679                   continue;
02680                }
02681             }
02682          }
02683          pos++;
02684       }
02685       ast_debug(1, "Stopping option '%s', res is %d\n", exten, res);
02686       pos = 0;
02687       if (!strcasecmp(exten, "s")) {
02688          strcpy(exten, "g");
02689       } else {
02690          break;
02691       }
02692    }
02693    return res;
02694 }
02695 
02696 int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
02697 {
02698    int res = ast_ivr_menu_run_internal(chan, menu, cbdata);
02699    /* Hide internal coding */
02700    return res > 0 ? 0 : res;
02701 }
02702 
02703 char *ast_read_textfile(const char *filename)
02704 {
02705    int fd, count = 0, res;
02706    char *output = NULL;
02707    struct stat filesize;
02708 
02709    if (stat(filename, &filesize) == -1) {
02710       ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
02711       return NULL;
02712    }
02713 
02714    count = filesize.st_size + 1;
02715 
02716    if ((fd = open(filename, O_RDONLY)) < 0) {
02717       ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
02718       return NULL;
02719    }
02720 
02721    if ((output = ast_malloc(count))) {
02722       res = read(fd, output, count - 1);
02723       if (res == count - 1) {
02724          output[res] = '\0';
02725       } else {
02726          ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
02727          ast_free(output);
02728          output = NULL;
02729       }
02730    }
02731 
02732    close(fd);
02733 
02734    return output;
02735 }
02736 
02737 static int parse_options(const struct ast_app_option *options, void *_flags, char **args, char *optstr, int flaglen)
02738 {
02739    char *s, *arg;
02740    int curarg, res = 0;
02741    unsigned int argloc;
02742    struct ast_flags *flags = _flags;
02743    struct ast_flags64 *flags64 = _flags;
02744 
02745    if (flaglen == 32) {
02746       ast_clear_flag(flags, AST_FLAGS_ALL);
02747    } else {
02748       flags64->flags = 0;
02749    }
02750 
02751    if (!optstr) {
02752       return 0;
02753    }
02754 
02755    s = optstr;
02756    while (*s) {
02757       curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
02758       argloc = options[curarg].arg_index;
02759       if (*s == '(') {
02760          int paren = 1, quote = 0;
02761          int parsequotes = (s[1] == '"') ? 1 : 0;
02762 
02763          /* Has argument */
02764          arg = ++s;
02765          for (; *s; s++) {
02766             if (*s == '(' && !quote) {
02767                paren++;
02768             } else if (*s == ')' && !quote) {
02769                /* Count parentheses, unless they're within quotes (or backslashed, below) */
02770                paren--;
02771             } else if (*s == '"' && parsequotes) {
02772                /* Leave embedded quotes alone, unless they are the first character */
02773                quote = quote ? 0 : 1;
02774                ast_copy_string(s, s + 1, INT_MAX);
02775                s--;
02776             } else if (*s == '\\') {
02777                if (!quote) {
02778                   /* If a backslash is found outside of quotes, remove it */
02779                   ast_copy_string(s, s + 1, INT_MAX);
02780                } else if (quote && s[1] == '"') {
02781                   /* Backslash for a quote character within quotes, remove the backslash */
02782                   ast_copy_string(s, s + 1, INT_MAX);
02783                } else {
02784                   /* Backslash within quotes, keep both characters */
02785                   s++;
02786                }
02787             }
02788 
02789             if (paren == 0) {
02790                break;
02791             }
02792          }
02793          /* This will find the closing paren we found above, or none, if the string ended before we found one. */
02794          if ((s = strchr(s, ')'))) {
02795             if (argloc) {
02796                args[argloc - 1] = arg;
02797             }
02798             *s++ = '\0';
02799          } else {
02800             ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
02801             res = -1;
02802             break;
02803          }
02804       } else if (argloc) {
02805          args[argloc - 1] = "";
02806       }
02807       if (flaglen == 32) {
02808          ast_set_flag(flags, options[curarg].flag);
02809       } else {
02810          ast_set_flag64(flags64, options[curarg].flag);
02811       }
02812    }
02813 
02814    return res;
02815 }
02816 
02817 int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
02818 {
02819    return parse_options(options, flags, args, optstr, 32);
02820 }
02821 
02822 int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
02823 {
02824    return parse_options(options, flags, args, optstr, 64);
02825 }
02826 
02827 void ast_app_options2str64(const struct ast_app_option *options, struct ast_flags64 *flags, char *buf, size_t len)
02828 {
02829    unsigned int i, found = 0;
02830    for (i = 32; i < 128 && found < len; i++) {
02831       if (ast_test_flag64(flags, options[i].flag)) {
02832          buf[found++] = i;
02833       }
02834    }
02835    buf[found] = '\0';
02836 }
02837 
02838 int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
02839 {
02840    int i;
02841    *consumed = 1;
02842    *result = 0;
02843    if (ast_strlen_zero(stream)) {
02844       *consumed = 0;
02845       return -1;
02846    }
02847 
02848    if (*stream == '\\') {
02849       *consumed = 2;
02850       switch (*(stream + 1)) {
02851       case 'n':
02852          *result = '\n';
02853          break;
02854       case 'r':
02855          *result = '\r';
02856          break;
02857       case 't':
02858          *result = '\t';
02859          break;
02860       case 'x':
02861          /* Hexadecimal */
02862          if (strchr("0123456789ABCDEFabcdef", *(stream + 2)) && *(stream + 2) != '\0') {
02863             *consumed = 3;
02864             if (*(stream + 2) <= '9') {
02865                *result = *(stream + 2) - '0';
02866             } else if (*(stream + 2) <= 'F') {
02867                *result = *(stream + 2) - 'A' + 10;
02868             } else {
02869                *result = *(stream + 2) - 'a' + 10;
02870             }
02871          } else {
02872             ast_log(LOG_ERROR, "Illegal character '%c' in hexadecimal string\n", *(stream + 2));
02873             return -1;
02874          }
02875 
02876          if (strchr("0123456789ABCDEFabcdef", *(stream + 3)) && *(stream + 3) != '\0') {
02877             *consumed = 4;
02878             *result <<= 4;
02879             if (*(stream + 3) <= '9') {
02880                *result += *(stream + 3) - '0';
02881             } else if (*(stream + 3) <= 'F') {
02882                *result += *(stream + 3) - 'A' + 10;
02883             } else {
02884                *result += *(stream + 3) - 'a' + 10;
02885             }
02886          }
02887          break;
02888       case '0':
02889          /* Octal */
02890          *consumed = 2;
02891          for (i = 2; ; i++) {
02892             if (strchr("01234567", *(stream + i)) && *(stream + i) != '\0') {
02893                (*consumed)++;
02894                ast_debug(5, "result was %d, ", *result);
02895                *result <<= 3;
02896                *result += *(stream + i) - '0';
02897                ast_debug(5, "is now %d\n", *result);
02898             } else {
02899                break;
02900             }
02901          }
02902          break;
02903       default:
02904          *result = *(stream + 1);
02905       }
02906    } else {
02907       *result = *stream;
02908       *consumed = 1;
02909    }
02910    return 0;
02911 }
02912 
02913 char *ast_get_encoded_str(const char *stream, char *result, size_t result_size)
02914 {
02915    char *cur = result;
02916    size_t consumed;
02917 
02918    while (cur < result + result_size - 1 && !ast_get_encoded_char(stream, cur, &consumed)) {
02919       cur++;
02920       stream += consumed;
02921    }
02922    *cur = '\0';
02923    return result;
02924 }
02925 
02926 int ast_str_get_encoded_str(struct ast_str **str, int maxlen, const char *stream)
02927 {
02928    char next, *buf;
02929    size_t offset = 0;
02930    size_t consumed;
02931 
02932    if (strchr(stream, '\\')) {
02933       while (!ast_get_encoded_char(stream, &next, &consumed)) {
02934          if (offset + 2 > ast_str_size(*str) && maxlen > -1) {
02935             ast_str_make_space(str, maxlen > 0 ? maxlen : (ast_str_size(*str) + 48) * 2 - 48);
02936          }
02937          if (offset + 2 > ast_str_size(*str)) {
02938             break;
02939          }
02940          buf = ast_str_buffer(*str);
02941          buf[offset++] = next;
02942          stream += consumed;
02943       }
02944       buf = ast_str_buffer(*str);
02945       buf[offset++] = '\0';
02946       ast_str_update(*str);
02947    } else {
02948       ast_str_set(str, maxlen, "%s", stream);
02949    }
02950    return 0;
02951 }
02952 
02953 void ast_close_fds_above_n(int n)
02954 {
02955    closefrom(n + 1);
02956 }
02957 
02958 int ast_safe_fork(int stop_reaper)
02959 {
02960    sigset_t signal_set, old_set;
02961    int pid;
02962 
02963    /* Don't let the default signal handler for children reap our status */
02964    if (stop_reaper) {
02965       ast_replace_sigchld();
02966    }
02967 
02968    /* GCC 4.9 gives a bogus "right-hand operand of comma expression has
02969     * no effect" warning */
02970    (void) sigfillset(&signal_set);
02971    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
02972 
02973    pid = fork();
02974 
02975    if (pid != 0) {
02976       /* Fork failed or parent */
02977       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
02978       if (!stop_reaper && pid > 0) {
02979          struct zombie *cur = ast_calloc(1, sizeof(*cur));
02980          if (cur) {
02981             cur->pid = pid;
02982             AST_LIST_LOCK(&zombies);
02983             AST_LIST_INSERT_TAIL(&zombies, cur, list);
02984             AST_LIST_UNLOCK(&zombies);
02985             if (shaun_of_the_dead_thread == AST_PTHREADT_NULL) {
02986                if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
02987                   ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
02988                   shaun_of_the_dead_thread = AST_PTHREADT_NULL;
02989                }
02990             }
02991          }
02992       }
02993       return pid;
02994    } else {
02995       /* Child */
02996 #ifdef HAVE_CAP
02997       cap_t cap = cap_from_text("cap_net_admin-eip");
02998 
02999       if (cap_set_proc(cap)) {
03000          ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
03001       }
03002       cap_free(cap);
03003 #endif
03004 
03005       /* Before we unblock our signals, return our trapped signals back to the defaults */
03006       signal(SIGHUP, SIG_DFL);
03007       signal(SIGCHLD, SIG_DFL);
03008       signal(SIGINT, SIG_DFL);
03009       signal(SIGURG, SIG_DFL);
03010       signal(SIGTERM, SIG_DFL);
03011       signal(SIGPIPE, SIG_DFL);
03012       signal(SIGXFSZ, SIG_DFL);
03013 
03014       /* unblock important signal handlers */
03015       if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
03016          ast_log(LOG_WARNING, "unable to unblock signals: %s\n", strerror(errno));
03017          _exit(1);
03018       }
03019 
03020       return pid;
03021    }
03022 }
03023 
03024 void ast_safe_fork_cleanup(void)
03025 {
03026    ast_unreplace_sigchld();
03027 }
03028 
03029 int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen unit)
03030 {
03031    int res;
03032    char u[10];
03033 #ifdef HAVE_LONG_DOUBLE_WIDER
03034    long double amount;
03035    #define FMT "%30Lf%9s"
03036 #else
03037    double amount;
03038    #define FMT "%30lf%9s"
03039 #endif
03040    if (!timestr) {
03041       return -1;
03042    }
03043 
03044    res = sscanf(timestr, FMT, &amount, u);
03045 
03046    if (res == 0 || res == EOF) {
03047 #undef FMT
03048       return -1;
03049    } else if (res == 2) {
03050       switch (u[0]) {
03051       case 'h':
03052       case 'H':
03053          unit = TIMELEN_HOURS;
03054          break;
03055       case 's':
03056       case 'S':
03057          unit = TIMELEN_SECONDS;
03058          break;
03059       case 'm':
03060       case 'M':
03061          if (toupper(u[1]) == 'S') {
03062             unit = TIMELEN_MILLISECONDS;
03063          } else if (u[1] == '\0') {
03064             unit = TIMELEN_MINUTES;
03065          }
03066          break;
03067       }
03068    }
03069 
03070    switch (unit) {
03071    case TIMELEN_HOURS:
03072       amount *= 60;
03073       /* fall-through */
03074    case TIMELEN_MINUTES:
03075       amount *= 60;
03076       /* fall-through */
03077    case TIMELEN_SECONDS:
03078       amount *= 1000;
03079       /* fall-through */
03080    case TIMELEN_MILLISECONDS:
03081       ;
03082    }
03083    *result = amount > INT_MAX ? INT_MAX : (int) amount;
03084    return 0;
03085 }
03086 
03087 
03088 
03089 static void mwi_state_dtor(void *obj)
03090 {
03091    struct ast_mwi_state *mwi_state = obj;
03092    ast_string_field_free_memory(mwi_state);
03093    ao2_cleanup(mwi_state->snapshot);
03094    mwi_state->snapshot = NULL;
03095 }
03096 
03097 struct stasis_topic *ast_mwi_topic_all(void)
03098 {
03099    return mwi_topic_all;
03100 }
03101 
03102 struct stasis_cache *ast_mwi_state_cache(void)
03103 {
03104    return mwi_state_cache;
03105 }
03106 
03107 struct stasis_topic *ast_mwi_topic_cached(void)
03108 {
03109    return stasis_caching_get_topic(mwi_topic_cached);
03110 }
03111 
03112 struct stasis_topic *ast_mwi_topic(const char *uniqueid)
03113 {
03114    return stasis_topic_pool_get_topic(mwi_topic_pool, uniqueid);
03115 }
03116 
03117 struct ast_mwi_state *ast_mwi_create(const char *mailbox, const char *context)
03118 {
03119    RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup);
03120 
03121    ast_assert(!ast_strlen_zero(mailbox));
03122 
03123    mwi_state = ao2_alloc(sizeof(*mwi_state), mwi_state_dtor);
03124    if (!mwi_state) {
03125       return NULL;
03126    }
03127 
03128    if (ast_string_field_init(mwi_state, 256)) {
03129       return NULL;
03130    }
03131    if (!ast_strlen_zero(context)) {
03132       ast_string_field_build(mwi_state, uniqueid, "%s@%s", mailbox, context);
03133    } else {
03134       ast_string_field_set(mwi_state, uniqueid, mailbox);
03135    }
03136 
03137    ao2_ref(mwi_state, +1);
03138    return mwi_state;
03139 }
03140 
03141 /*!
03142  * \internal
03143  * \brief Create a MWI state snapshot message.
03144  * \since 12.2.0
03145  *
03146  * \param[in] mailbox The mailbox identifier string.
03147  * \param[in] context The context this mailbox resides in (NULL or "" if only using mailbox)
03148  * \param[in] new_msgs The number of new messages in this mailbox
03149  * \param[in] old_msgs The number of old messages in this mailbox
03150  * \param[in] channel_id A unique identifier for a channel associated with this
03151  * change in mailbox state
03152  * \param[in] eid The EID of the server that originally published the message
03153  *
03154  * \retval message on success.  Use ao2_cleanup() when done with it.
03155  * \retval NULL on error.
03156  */
03157 static struct stasis_message *mwi_state_create_message(
03158    const char *mailbox,
03159    const char *context,
03160    int new_msgs,
03161    int old_msgs,
03162    const char *channel_id,
03163    struct ast_eid *eid)
03164 {
03165    struct ast_mwi_state *mwi_state;
03166    struct stasis_message *message;
03167 
03168    if (!ast_mwi_state_type()) {
03169       return NULL;
03170    }
03171 
03172    mwi_state = ast_mwi_create(mailbox, context);
03173    if (!mwi_state) {
03174       return NULL;
03175    }
03176 
03177    mwi_state->new_msgs = new_msgs;
03178    mwi_state->old_msgs = old_msgs;
03179 
03180    if (!ast_strlen_zero(channel_id)) {
03181       struct stasis_message *chan_message;
03182 
03183       chan_message = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),
03184          channel_id);
03185       if (chan_message) {
03186          mwi_state->snapshot = stasis_message_data(chan_message);
03187          ao2_ref(mwi_state->snapshot, +1);
03188       }
03189       ao2_cleanup(chan_message);
03190    }
03191 
03192    if (eid) {
03193       mwi_state->eid = *eid;
03194    } else {
03195       mwi_state->eid = ast_eid_default;
03196    }
03197 
03198    /*
03199     * XXX As far as stasis is concerned, all MWI events are local.
03200     *
03201     * We may in the future want to make MWI aggregate local/remote
03202     * message counts similar to how device state aggregates state.
03203     */
03204    message = stasis_message_create_full(ast_mwi_state_type(), mwi_state, &ast_eid_default);
03205    ao2_cleanup(mwi_state);
03206    return message;
03207 }
03208 
03209 int ast_publish_mwi_state_full(
03210    const char *mailbox,
03211    const char *context,
03212    int new_msgs,
03213    int old_msgs,
03214    const char *channel_id,
03215    struct ast_eid *eid)
03216 {
03217    struct ast_mwi_state *mwi_state;
03218    RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
03219    struct stasis_topic *mailbox_specific_topic;
03220 
03221    message = mwi_state_create_message(mailbox, context, new_msgs, old_msgs, channel_id, eid);
03222    if (!message) {
03223       return -1;
03224    }
03225 
03226    mwi_state = stasis_message_data(message);
03227    mailbox_specific_topic = ast_mwi_topic(mwi_state->uniqueid);
03228    if (!mailbox_specific_topic) {
03229       return -1;
03230    }
03231 
03232    stasis_publish(mailbox_specific_topic, message);
03233 
03234    return 0;
03235 }
03236 
03237 int ast_delete_mwi_state_full(const char *mailbox, const char *context, struct ast_eid *eid)
03238 {
03239    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
03240    struct stasis_message *cached_msg;
03241    struct stasis_message *clear_msg;
03242    struct ast_mwi_state *mwi_state;
03243    struct stasis_topic *mailbox_specific_topic;
03244 
03245    msg = mwi_state_create_message(mailbox, context, 0, 0, NULL, eid);
03246    if (!msg) {
03247       return -1;
03248    }
03249 
03250    mwi_state = stasis_message_data(msg);
03251 
03252    /*
03253     * XXX As far as stasis is concerned, all MWI events are local.
03254     *
03255     * For now, it is assumed that there is only one entity
03256     * maintaining the state of a particular mailbox.
03257     *
03258     * If we ever have multiple MWI event entities maintaining
03259     * the same mailbox that wish to delete their cached entry
03260     * we will need to do something about the race condition
03261     * potential between checking the cache and removing the
03262     * cache entry.
03263     */
03264    cached_msg = stasis_cache_get_by_eid(ast_mwi_state_cache(),
03265       ast_mwi_state_type(), mwi_state->uniqueid, &ast_eid_default);
03266    if (!cached_msg) {
03267       /* Nothing to clear */
03268       return -1;
03269    }
03270    ao2_cleanup(cached_msg);
03271 
03272    mailbox_specific_topic = ast_mwi_topic(mwi_state->uniqueid);
03273    if (!mailbox_specific_topic) {
03274       return -1;
03275    }
03276 
03277    clear_msg = stasis_cache_clear_create(msg);
03278    if (clear_msg) {
03279       stasis_publish(mailbox_specific_topic, clear_msg);
03280    }
03281    ao2_cleanup(clear_msg);
03282    return 0;
03283 }
03284 
03285 static const char *mwi_state_get_id(struct stasis_message *message)
03286 {
03287    if (ast_mwi_state_type() == stasis_message_type(message)) {
03288       struct ast_mwi_state *mwi_state = stasis_message_data(message);
03289       return mwi_state->uniqueid;
03290    } else if (stasis_subscription_change_type() == stasis_message_type(message)) {
03291       struct stasis_subscription_change *change = stasis_message_data(message);
03292       return change->uniqueid;
03293    }
03294 
03295    return NULL;
03296 }
03297 
03298 static void mwi_blob_dtor(void *obj)
03299 {
03300    struct ast_mwi_blob *mwi_blob = obj;
03301 
03302    ao2_cleanup(mwi_blob->mwi_state);
03303    ast_json_unref(mwi_blob->blob);
03304 }
03305 
03306 struct stasis_message *ast_mwi_blob_create(struct ast_mwi_state *mwi_state,
03307                       struct stasis_message_type *message_type,
03308                       struct ast_json *blob)
03309 {
03310    RAII_VAR(struct ast_mwi_blob *, obj, NULL, ao2_cleanup);
03311    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
03312 
03313    ast_assert(blob != NULL);
03314 
03315    if (!message_type) {
03316       return NULL;
03317    }
03318 
03319    obj = ao2_alloc(sizeof(*obj), mwi_blob_dtor);
03320    if (!obj) {
03321       return NULL;
03322    }
03323 
03324    obj->mwi_state = mwi_state;
03325    ao2_ref(obj->mwi_state, +1);
03326    obj->blob = ast_json_ref(blob);
03327 
03328    /* This is not a normal MWI event.  Only used by the MinivmNotify app. */
03329    msg = stasis_message_create(message_type, obj);
03330    if (!msg) {
03331       return NULL;
03332    }
03333 
03334    ao2_ref(msg, +1);
03335    return msg;
03336 }
03337 
03338 struct stasis_topic *ast_queue_topic_all(void)
03339 {
03340    return queue_topic_all;
03341 }
03342 
03343 struct stasis_topic *ast_queue_topic(const char *queuename)
03344 {
03345    return stasis_topic_pool_get_topic(queue_topic_pool, queuename);
03346 }
03347 
03348 static void app_cleanup(void)
03349 {
03350    ao2_cleanup(queue_topic_pool);
03351    queue_topic_pool = NULL;
03352    ao2_cleanup(queue_topic_all);
03353    queue_topic_all = NULL;
03354    ao2_cleanup(mwi_topic_pool);
03355    mwi_topic_pool = NULL;
03356    ao2_cleanup(mwi_topic_all);
03357    mwi_topic_all = NULL;
03358    ao2_cleanup(mwi_state_cache);
03359    mwi_state_cache = NULL;
03360    mwi_topic_cached = stasis_caching_unsubscribe_and_join(mwi_topic_cached);
03361    STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_state_type);
03362    STASIS_MESSAGE_TYPE_CLEANUP(ast_mwi_vm_app_type);
03363 }
03364 
03365 int app_init(void)
03366 {
03367    ast_register_cleanup(app_cleanup);
03368 
03369    if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_state_type) != 0) {
03370       return -1;
03371    }
03372    if (STASIS_MESSAGE_TYPE_INIT(ast_mwi_vm_app_type) != 0) {
03373       return -1;
03374    }
03375    mwi_topic_all = stasis_topic_create("stasis_mwi_topic");
03376    if (!mwi_topic_all) {
03377       return -1;
03378    }
03379    mwi_state_cache = stasis_cache_create(mwi_state_get_id);
03380    if (!mwi_state_cache) {
03381       return -1;
03382    }
03383    mwi_topic_cached = stasis_caching_topic_create(mwi_topic_all, mwi_state_cache);
03384    if (!mwi_topic_cached) {
03385       return -1;
03386    }
03387    mwi_topic_pool = stasis_topic_pool_create(mwi_topic_all);
03388    if (!mwi_topic_pool) {
03389       return -1;
03390    }
03391    queue_topic_all = stasis_topic_create("stasis_queue_topic");
03392    if (!queue_topic_all) {
03393       return -1;
03394    }
03395    queue_topic_pool = stasis_topic_pool_create(queue_topic_all);
03396    if (!queue_topic_pool) {
03397       return -1;
03398    }
03399    return 0;
03400 }
03401 

Generated on Thu Apr 16 06:27:06 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6