Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2005, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief Convenient Application Routines
/** \example
* \par This is an example of how to develop an app.
* Application Skeleton is an example of creating an application for Asterisk.
* \verbinclude app_skel.c
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
Kevin P. Fleming
committed
#include <regex.h> /* for regcomp(3) */
#include <sys/file.h> /* for flock(2) */
#include <signal.h> /* for pthread_sigmask(3) */
Tilghman Lesher
committed
#include <stdlib.h> /* for closefrom(3) */
#include <sys/types.h>
#include <sys/wait.h> /* for waitpid(2) */
#ifndef HAVE_CLOSEFROM
#include <dirent.h> /* for opendir(3) */
#endif
#ifdef HAVE_CAP
#include <sys/capability.h>
#endif /* HAVE_CAP */
#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
Kevin P. Fleming
committed
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/file.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/indications.h"
#include "asterisk/threadstorage.h"
#include "asterisk/module.h"
#include "asterisk/astobj2.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/json.h"
#include "asterisk/format_cache.h"
AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf);
static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
struct zombie {
pid_t pid;
AST_LIST_ENTRY(zombie) list;
};
static AST_LIST_HEAD_STATIC(zombies, zombie);
* @{ \brief Define \ref stasis topic objects
static struct stasis_cache *mwi_state_cache;
static struct stasis_caching_topic *mwi_topic_cached;
static struct stasis_topic_pool *mwi_topic_pool;
static struct stasis_topic *queue_topic_all;
static struct stasis_topic_pool *queue_topic_pool;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*! \brief Convert a MWI \ref stasis_message to a \ref ast_event */
static struct ast_event *mwi_to_event(struct stasis_message *message)
{
struct ast_event *event;
struct ast_mwi_state *mwi_state;
char *mailbox;
char *context;
if (!message) {
return NULL;
}
mwi_state = stasis_message_data(message);
/* Strip off @context */
context = mailbox = ast_strdupa(mwi_state->uniqueid);
strsep(&context, "@");
if (ast_strlen_zero(context)) {
context = "default";
}
event = ast_event_new(AST_EVENT_MWI,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, mwi_state->new_msgs,
AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, mwi_state->old_msgs,
AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, &mwi_state->eid, sizeof(mwi_state->eid),
AST_EVENT_IE_END);
return event;
}
/*
* @{ \brief Define \ref stasis message types for MWI
*/
STASIS_MESSAGE_TYPE_DEFN(ast_mwi_state_type,
.to_event = mwi_to_event, );
STASIS_MESSAGE_TYPE_DEFN(ast_mwi_vm_app_type);
/* @} */
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
static void *shaun_of_the_dead(void *data)
{
struct zombie *cur;
int status;
for (;;) {
if (!AST_LIST_EMPTY(&zombies)) {
/* Don't allow cancellation while we have a lock. */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
AST_LIST_LOCK(&zombies);
AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
if (waitpid(cur->pid, &status, WNOHANG) != 0) {
AST_LIST_REMOVE_CURRENT(list);
ast_free(cur);
}
}
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK(&zombies);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
}
pthread_testcancel();
/* Wait for 60 seconds, without engaging in a busy loop. */
ast_poll(NULL, 0, AST_LIST_FIRST(&zombies) ? 5000 : 60000);
}
return NULL;
}
#define AST_MAX_FORMATS 10
static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
Russell Bryant
committed
/*!
* \brief This function presents a dialtone and reads an extension into 'collect'
* which must be a pointer to a **pre-initialized** array of char having a
* size of 'size' suitable for writing to. It will collect no more than the smaller
Russell Bryant
committed
* of 'maxlen' or 'size' minus the original strlen() of collect digits.
* \param chan struct.
* \param context
* \param collect
* \param size
Russell Bryant
committed
* \param maxlen
* \param timeout timeout in milliseconds
Russell Bryant
committed
*
* \return 0 if extension does not exist, 1 if extension exists
int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
struct ast_tone_zone_sound *ts;
if (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) {
timeout = ast_channel_pbx(chan)->dtimeoutms;
} else {
timeout = 5000;
}
if ((ts = ast_get_indication_tone(ast_channel_zone(chan), "dial"))) {
res = ast_playtones_start(chan, 0, ts->data, 0);
ts = ast_tone_zone_sound_unref(ts);
} else {
ast_log(LOG_NOTICE, "Huh....? no dial for indications?\n");
for (x = strlen(collect); x < maxlen; ) {
res = ast_waitfordigit(chan, timeout);
ast_playtones_stop(chan);
collect[x++] = res;
if (!ast_matchmore_extension(chan, context, collect, 1,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
res = ast_exists_extension(chan, context, collect, 1,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)) ? 1 : 0;
return res;
}
Russell Bryant
committed
/*!
* \brief ast_app_getdata
* \param c The channel to read from
* \param prompt The file to stream to the channel
* \param s The string to read in to. Must be at least the size of your length
* \param maxlen How many digits to read (maximum)
* \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
* "ludicrous time" (essentially never times out) */
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Russell Bryant
committed
int res = 0, to, fto;
char *front, *filename;
/* XXX Merge with full version? XXX */
if (maxlen)
s[0] = '\0';
Russell Bryant
committed
Steve Murphy
committed
if (!prompt)
Russell Bryant
committed
filename = ast_strdupa(prompt);
while ((front = strsep(&filename, "&"))) {
Steve Murphy
committed
if (!ast_strlen_zero(front)) {
res = ast_streamfile(c, front, ast_channel_language(c));
Steve Murphy
committed
if (res)
continue;
}
Russell Bryant
committed
if (ast_strlen_zero(filename)) {
/* set timeouts for the last prompt */
fto = ast_channel_pbx(c) ? ast_channel_pbx(c)->rtimeoutms : 6000;
to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
Russell Bryant
committed
Russell Bryant
committed
fto = to = timeout;
Russell Bryant
committed
fto = to = 1000000000;
Russell Bryant
committed
} else {
/* there is more than one prompt, so
* get rid of the long timeout between
* prompts, and make it 50ms */
Russell Bryant
committed
fto = 50;
to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
Russell Bryant
committed
}
res = ast_readstring(c, s, maxlen, to, fto, "#");
if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
}
if (!ast_strlen_zero(s)) {
return res;
}
/* The lock type used by ast_lock_path() / ast_unlock_path() */
static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
int res, to = 2000, fto = 6000;
if (!ast_strlen_zero(prompt)) {
res = ast_streamfile(c, prompt, ast_channel_language(c));
res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
Richard Mudgett
committed
int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args)
Richard Mudgett
committed
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
struct ast_app *macro_app;
int res;
macro_app = pbx_findapp("Macro");
if (!macro_app) {
ast_log(LOG_WARNING,
"Cannot run 'Macro(%s)'. The application is not available.\n", macro_args);
return -1;
}
if (autoservice_chan) {
ast_autoservice_start(autoservice_chan);
}
ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(macro_chan),
ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
ast_channel_priority(macro_chan));
res = pbx_exec(macro_chan, macro_app, macro_args);
ast_debug(4, "Macro exited with status %d\n", res);
/*
* Assume anything negative from Macro is an error.
* Anything else is success.
*/
if (res < 0) {
res = -1;
} else {
res = 0;
}
ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(macro_chan),
ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
ast_channel_priority(macro_chan));
if (autoservice_chan) {
ast_autoservice_stop(autoservice_chan);
}
return res;
}
int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args)
{
int res;
char *args_str;
size_t args_len;
if (ast_strlen_zero(macro_args)) {
return ast_app_exec_macro(autoservice_chan, macro_chan, macro_name);
}
/* Create the Macro application argument string. */
args_len = strlen(macro_name) + strlen(macro_args) + 2;
args_str = ast_malloc(args_len);
if (!args_str) {
return -1;
}
snprintf(args_str, args_len, "%s,%s", macro_name, macro_args);
res = ast_app_exec_macro(autoservice_chan, macro_chan, args_str);
ast_free(args_str);
return res;
}
static const struct ast_app_stack_funcs *app_stack_callbacks;
void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
Richard Mudgett
committed
{
app_stack_callbacks = funcs;
}
const char *ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
{
const struct ast_app_stack_funcs *funcs;
const char *new_args;
funcs = app_stack_callbacks;
if (!funcs || !funcs->expand_sub_args) {
Richard Mudgett
committed
ast_log(LOG_WARNING,
"Cannot expand 'Gosub(%s)' arguments. The app_stack module is not available.\n",
args);
return NULL;
ast_module_ref(funcs->module);
Richard Mudgett
committed
new_args = funcs->expand_sub_args(chan, args);
ast_module_unref(funcs->module);
return new_args;
}
Richard Mudgett
committed
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
{
const struct ast_app_stack_funcs *funcs;
int res;
Richard Mudgett
committed
funcs = app_stack_callbacks;
if (!funcs || !funcs->run_sub) {
ast_log(LOG_WARNING,
"Cannot run 'Gosub(%s)'. The app_stack module is not available.\n",
sub_args);
return -1;
ast_module_ref(funcs->module);
Richard Mudgett
committed
if (autoservice_chan) {
ast_autoservice_start(autoservice_chan);
}
Richard Mudgett
committed
res = funcs->run_sub(sub_chan, sub_args, ignore_hangup);
ast_module_unref(funcs->module);
Richard Mudgett
committed
if (autoservice_chan) {
ast_autoservice_stop(autoservice_chan);
}
return res;
}
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)
Richard Mudgett
committed
int res;
char *args_str;
size_t args_len;
Richard Mudgett
committed
if (ast_strlen_zero(sub_args)) {
return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location, ignore_hangup);
Richard Mudgett
committed
/* Create the Gosub application argument string. */
args_len = strlen(sub_location) + strlen(sub_args) + 3;
args_str = ast_malloc(args_len);
if (!args_str) {
return -1;
Richard Mudgett
committed
snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args);
res = ast_app_exec_sub(autoservice_chan, sub_chan, args_str, ignore_hangup);
Richard Mudgett
committed
ast_free(args_str);
return res;
Richard Mudgett
committed
/*! \brief The container for the voicemail provider */
static AO2_GLOBAL_OBJ_STATIC(vm_provider);
/*! Voicemail not registered warning */
static int vm_warnings;
Richard Mudgett
committed
int ast_vm_is_registered(void)
{
struct ast_vm_functions *table;
int is_registered;
table = ao2_global_obj_ref(vm_provider);
is_registered = table ? 1 : 0;
ao2_cleanup(table);
return is_registered;
}
Richard Mudgett
committed
int __ast_vm_register(const struct ast_vm_functions *vm_table, struct ast_module *module)
Richard Mudgett
committed
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
RAII_VAR(struct ast_vm_functions *, table, NULL, ao2_cleanup);
if (!vm_table->module_name) {
ast_log(LOG_ERROR, "Voicemail provider missing required information.\n");
return -1;
}
if (vm_table->module_version != VM_MODULE_VERSION) {
ast_log(LOG_ERROR, "Voicemail provider '%s' has incorrect version\n",
vm_table->module_name);
return -1;
}
table = ao2_global_obj_ref(vm_provider);
if (table) {
ast_log(LOG_WARNING, "Voicemail provider already registered by %s.\n",
table->module_name);
return -1;
}
table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!table) {
return -1;
}
*table = *vm_table;
table->module = module;
ao2_global_obj_replace_unref(vm_provider, table);
return 0;
Richard Mudgett
committed
void ast_vm_unregister(const char *module_name)
Richard Mudgett
committed
struct ast_vm_functions *table;
table = ao2_global_obj_ref(vm_provider);
if (table && !strcmp(table->module_name, module_name)) {
ao2_global_obj_release(vm_provider);
}
ao2_cleanup(table);
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
#ifdef TEST_FRAMEWORK
/*! \brief Holding container for the voicemail provider used while testing */
static AO2_GLOBAL_OBJ_STATIC(vm_provider_holder);
static int provider_is_swapped = 0;
void ast_vm_test_swap_table_in(const struct ast_vm_functions *vm_table)
{
RAII_VAR(struct ast_vm_functions *, holding_table, NULL, ao2_cleanup);
RAII_VAR(struct ast_vm_functions *, new_table, NULL, ao2_cleanup);
if (provider_is_swapped) {
ast_log(LOG_ERROR, "Attempted to swap in test function table without swapping out old test table.\n");
return;
}
holding_table = ao2_global_obj_ref(vm_provider);
if (holding_table) {
ao2_global_obj_replace_unref(vm_provider_holder, holding_table);
}
new_table = ao2_alloc_options(sizeof(*new_table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!new_table) {
return;
}
*new_table = *vm_table;
ao2_global_obj_replace_unref(vm_provider, new_table);
provider_is_swapped = 1;
}
void ast_vm_test_swap_table_out(void)
{
RAII_VAR(struct ast_vm_functions *, held_table, NULL, ao2_cleanup);
if (!provider_is_swapped) {
ast_log(LOG_ERROR, "Attempted to swap out test function table, but none is currently installed.\n");
return;
}
held_table = ao2_global_obj_ref(vm_provider_holder);
if (!held_table) {
return;
}
ao2_global_obj_replace_unref(vm_provider, held_table);
ao2_global_obj_release(vm_provider_holder);
provider_is_swapped = 0;
}
#endif
Richard Mudgett
committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
/*! \brief The container for the voicemail greeter provider */
static AO2_GLOBAL_OBJ_STATIC(vm_greeter_provider);
/*! Voicemail greeter not registered warning */
static int vm_greeter_warnings;
int ast_vm_greeter_is_registered(void)
{
struct ast_vm_greeter_functions *table;
int is_registered;
table = ao2_global_obj_ref(vm_greeter_provider);
is_registered = table ? 1 : 0;
ao2_cleanup(table);
return is_registered;
}
int __ast_vm_greeter_register(const struct ast_vm_greeter_functions *vm_table, struct ast_module *module)
{
RAII_VAR(struct ast_vm_greeter_functions *, table, NULL, ao2_cleanup);
if (!vm_table->module_name) {
ast_log(LOG_ERROR, "Voicemail greeter provider missing required information.\n");
return -1;
}
if (vm_table->module_version != VM_GREETER_MODULE_VERSION) {
ast_log(LOG_ERROR, "Voicemail greeter provider '%s' has incorrect version\n",
vm_table->module_name);
return -1;
}
table = ao2_global_obj_ref(vm_greeter_provider);
if (table) {
ast_log(LOG_WARNING, "Voicemail greeter provider already registered by %s.\n",
table->module_name);
return -1;
}
table = ao2_alloc_options(sizeof(*table), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!table) {
return -1;
}
*table = *vm_table;
table->module = module;
ao2_global_obj_replace_unref(vm_greeter_provider, table);
return 0;
}
void ast_vm_greeter_unregister(const char *module_name)
{
struct ast_vm_greeter_functions *table;
table = ao2_global_obj_ref(vm_greeter_provider);
if (table && !strcmp(table->module_name, module_name)) {
ao2_global_obj_release(vm_greeter_provider);
}
ao2_cleanup(table);
}
static ast_vm_test_create_user_fn *ast_vm_test_create_user_func = NULL;
static ast_vm_test_destroy_user_fn *ast_vm_test_destroy_user_func = NULL;
void ast_install_vm_test_functions(ast_vm_test_create_user_fn *vm_test_create_user_func,
ast_vm_test_destroy_user_fn *vm_test_destroy_user_func)
{
ast_vm_test_create_user_func = vm_test_create_user_func;
ast_vm_test_destroy_user_func = vm_test_destroy_user_func;
}
void ast_uninstall_vm_test_functions(void)
{
ast_vm_test_create_user_func = NULL;
ast_vm_test_destroy_user_func = NULL;
}
#endif
Richard Mudgett
committed
static void vm_warn_no_provider(void)
Richard Mudgett
committed
if (vm_warnings++ % 10 == 0) {
ast_verb(3, "No voicemail provider registered.\n");
Richard Mudgett
committed
}
Richard Mudgett
committed
#define VM_API_CALL(res, api_call, api_parms) \
do { \
Richard Mudgett
committed
struct ast_vm_functions *table; \
table = ao2_global_obj_ref(vm_provider); \
Richard Mudgett
committed
if (!table) { \
vm_warn_no_provider(); \
} else if (table->api_call) { \
ast_module_ref(table->module); \
(res) = table->api_call api_parms; \
ast_module_unref(table->module); \
} \
ao2_cleanup(table); \
} while (0)
Richard Mudgett
committed
static void vm_greeter_warn_no_provider(void)
{
if (vm_greeter_warnings++ % 10 == 0) {
ast_verb(3, "No voicemail greeter provider registered.\n");
}
}
#define VM_GREETER_API_CALL(res, api_call, api_parms) \
do { \
struct ast_vm_greeter_functions *table; \
table = ao2_global_obj_ref(vm_greeter_provider); \
if (!table) { \
vm_greeter_warn_no_provider(); \
} else if (table->api_call) { \
ast_module_ref(table->module); \
(res) = table->api_call api_parms; \
ast_module_unref(table->module); \
} \
ao2_cleanup(table); \
} while (0)
Richard Mudgett
committed
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
{
int res = 0;
VM_API_CALL(res, has_voicemail, (mailboxes, folder));
return res;
/*!
* \internal
* \brief Function used as a callback for ast_copy_recording_to_vm when a real one isn't installed.
* \param vm_rec_data Stores crucial information about the voicemail that will basically just be used
* to figure out what the name of the recipient was supposed to be
*/
int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
{
Richard Mudgett
committed
int res = -1;
Richard Mudgett
committed
VM_API_CALL(res, copy_recording_to_vm, (vm_rec_data));
return res;
Richard Mudgett
committed
int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
Richard Mudgett
committed
int res = 0;
}
if (oldmsgs) {
*oldmsgs = 0;
}
Richard Mudgett
committed
VM_API_CALL(res, inboxcount, (mailboxes, newmsgs, oldmsgs));
return res;
Richard Mudgett
committed
int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Richard Mudgett
committed
int res = 0;
if (newmsgs) {
*newmsgs = 0;
}
if (oldmsgs) {
}
if (urgentmsgs) {
*urgentmsgs = 0;
Richard Mudgett
committed
VM_API_CALL(res, inboxcount2, (mailboxes, urgentmsgs, newmsgs, oldmsgs));
return res;
Richard Mudgett
committed
int ast_app_sayname(struct ast_channel *chan, const char *mailbox_id)
Mark Michelson
committed
{
Richard Mudgett
committed
int res = -1;
Richard Mudgett
committed
VM_GREETER_API_CALL(res, sayname, (chan, mailbox_id));
Richard Mudgett
committed
return res;
Mark Michelson
committed
}
Richard Mudgett
committed
int ast_app_messagecount(const char *mailbox_id, const char *folder)
Richard Mudgett
committed
int res = 0;
Richard Mudgett
committed
VM_API_CALL(res, messagecount, (mailbox_id, folder));
Richard Mudgett
committed
return res;
const char *ast_vm_index_to_foldername(int id)
{
Richard Mudgett
committed
const char *res = NULL;
VM_API_CALL(res, index_to_foldername, (id));
return res;
}
struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_create(const char *mailbox,
const char *context,
const char *folder,
int descending,
enum ast_vm_snapshot_sort_val sort_val,
int combine_INBOX_and_OLD)
{
Richard Mudgett
committed
struct ast_vm_mailbox_snapshot *res = NULL;
VM_API_CALL(res, mailbox_snapshot_create, (mailbox, context, folder, descending,
sort_val, combine_INBOX_and_OLD));
return res;
}
struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
{
Richard Mudgett
committed
struct ast_vm_mailbox_snapshot *res = NULL;
VM_API_CALL(res, mailbox_snapshot_destroy, (mailbox_snapshot));
return res;
}
int ast_vm_msg_move(const char *mailbox,
const char *context,
size_t num_msgs,
const char *oldfolder,
const char *old_msg_ids[],
const char *newfolder)
{
Richard Mudgett
committed
int res = 0;
VM_API_CALL(res, msg_move, (mailbox, context, num_msgs, oldfolder, old_msg_ids,
newfolder));
return res;
}
int ast_vm_msg_remove(const char *mailbox,
const char *context,
size_t num_msgs,
const char *folder,
const char *msgs[])
{
Richard Mudgett
committed
int res = 0;
VM_API_CALL(res, msg_remove, (mailbox, context, num_msgs, folder, msgs));
return res;
}
int ast_vm_msg_forward(const char *from_mailbox,
const char *from_context,
const char *from_folder,
const char *to_mailbox,
const char *to_context,
const char *to_folder,
size_t num_msgs,
const char *msg_ids[],
int delete_old)
{
Richard Mudgett
committed
int res = 0;
VM_API_CALL(res, msg_forward, (from_mailbox, from_context, from_folder, to_mailbox,
to_context, to_folder, num_msgs, msg_ids, delete_old));
return res;
}
int ast_vm_msg_play(struct ast_channel *chan,
const char *mailbox,
const char *context,
const char *folder,
const char *msg_num,
ast_vm_msg_play_cb *cb)
Richard Mudgett
committed
int res = 0;
VM_API_CALL(res, msg_play, (chan, mailbox, context, folder, msg_num, cb));
return res;
}
#ifdef TEST_FRAMEWORK
int ast_vm_test_create_user(const char *context, const char *mailbox)
{
if (ast_vm_test_create_user_func) {
return ast_vm_test_create_user_func(context, mailbox);
}
return 0;
}
int ast_vm_test_destroy_user(const char *context, const char *mailbox)
{
if (ast_vm_test_destroy_user_func) {
return ast_vm_test_destroy_user_func(context, mailbox);
}
return 0;
}
#endif
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
const char *ptr;
struct ast_silence_generator *silgen = NULL;
if (peer && ast_autoservice_start(peer)) {
return -1;
/* Need a quiet time before sending digits. */
if (ast_opt_transmit_silence) {
silgen = ast_channel_start_silence_generator(chan);
}
res = ast_safe_sleep(chan, 100);
if (res) {
goto dtmf_stream_cleanup;
}
for (ptr = digits; *ptr; ptr++) {
if (*ptr == 'w') {
/* 'w' -- wait half a second */
break;
} else if (*ptr == 'W') {
/* 'W' -- wait a second */
if ((res = ast_safe_sleep(chan, 1000))) {
break;
}
} else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
if (*ptr == 'f' || *ptr == 'F') {
/* ignore return values if not supported by channel */
ast_indicate(chan, AST_CONTROL_FLASH);
Joshua Colp
committed
ast_senddigit(chan, *ptr, duration);
/* pause between digits */
break;
ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n", *ptr);
if (silgen) {
ast_channel_stop_silence_generator(chan, silgen);
}
if (peer && ast_autoservice_stop(peer)) {
res = -1;
}
struct linear_state {
int fd;
int autoclose;
int allowoverride;
struct ast_format *origwfmt;
};
static void linear_release(struct ast_channel *chan, void *params)
{
struct linear_state *ls = params;
if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n",
ast_channel_name(chan), ast_format_get_name(ls->origwfmt));
ao2_cleanup(ls->origwfmt);
close(ls->fd);
Tilghman Lesher
committed
ast_free(params);
}
static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
{
short buf[2048 + AST_FRIENDLY_OFFSET / 2];
struct linear_state *ls = data;
Michiel van Baak
committed
.data.ptr = buf + AST_FRIENDLY_OFFSET / 2,
f.subclass.format = ast_format_slin;
len = samples * 2;
if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len);
len = sizeof(buf) - AST_FRIENDLY_OFFSET;
}
res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
if (res > 0) {
f.datalen = res;
f.samples = res / 2;
ast_write(chan, &f);
}
return -1;
}
static void *linear_alloc(struct ast_channel *chan, void *params)
{
/* In this case, params is already malloc'd */
ast_set_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
ls->origwfmt = ao2_bump(ast_channel_writeformat(chan));