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
*
* \author Mark Spencer <markster@digium.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
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/logger.h"
#include "asterisk/options.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/indications.h"
#define MAX_OTHER_FORMATS 10
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
of 'maxlen' or 'size' minus the original strlen() of collect digits.
\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 tone_zone_sound *ts;
int res=0, x=0;
if(!timeout && chan->pbx)
timeout = chan->pbx->dtimeout;
else if(!timeout)
timeout = 5;
ts = ast_get_indication_tone(chan->zone,"dial");
if (ts && ts->data[0])
res = ast_playtones_start(chan, 0, ts->data, 0);
else
ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
for (x = strlen(collect); strlen(collect) < maxlen; ) {
res = ast_waitfordigit(chan, timeout);
if (!ast_ignore_pattern(context, collect))
ast_playtones_stop(chan);
if (res < 1)
break;
collect[x++] = res;
if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
if (collect[x-1] == '#') {
/* Not a valid extension, ending in #, assume the # was to finish dialing */
collect[x-1] = '\0';
}
break;
}
}
if (res >= 0) {
if (ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num))
res = 1;
else
res = 0;
}
return res;
}
/*! \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
"ludicrous time" (essentially never times out) */
int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
{
int res,to,fto;
/* XXX Merge with full version? XXX */
if (maxlen)
s[0] = '\0';
if (prompt) {
res = ast_streamfile(c, prompt, c->language);
if (res < 0)
return res;
}
fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
if (timeout > 0)
fto = to = timeout;
if (timeout < 0)
fto = to = 1000000000;
res = ast_readstring(c, s, maxlen, to, fto, "#");
return res;
}
int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
{
int res,to,fto;
if (prompt) {
res = ast_streamfile(c, prompt, c->language);
if (res < 0)
return res;
}
fto = 6000;
to = 2000;
if (timeout > 0)
fto = to = timeout;
if (timeout < 0)
fto = to = 1000000000;
res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
return res;
}
int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
{
int res;
struct ast_filestream *writer;
int rfmt;
struct ast_frame *f;
struct ast_dsp *sildet;
/* Play prompt if requested */
if (prompt) {
res = ast_streamfile(c, prompt, c->language);
if (res < 0)
return res;
res = ast_waitstream(c,"");
if (res < 0)
return res;
}
rfmt = c->readformat;
Mark Spencer
committed
res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
return -1;
}
sildet = ast_dsp_new();
if (!sildet) {
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
return -1;
}
writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
if (!writer) {
ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
ast_dsp_free(sildet);
return -1;
}
for(;;) {
if ((res = ast_waitfor(c, 2000)) < 0) {
ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
break;
}
if (res) {
f = ast_read(c);
if (!f) {
ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
break;
}
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
/* Ended happily with DTMF */
ast_frfree(f);
break;
} else if (f->frametype == AST_FRAME_VOICE) {
ast_dsp_silence(sildet, f, &total);
if (total > silence) {
/* Ended happily with silence */
ast_frfree(f);
break;
}
totalms += f->samples / 8;
if (totalms > maxsec * 1000) {
/* Ended happily with too much stuff */
ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
ast_frfree(f);
break;
}
res = ast_writestream(writer, f);
if (res < 0) {
ast_log(LOG_WARNING, "Failed to write to stream at %s!\n", dest);
ast_frfree(f);
break;
}
Mark Spencer
committed
res = ast_set_read_format(c, rfmt);
if (res)
ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
ast_dsp_free(sildet);
ast_closestream(writer);
return 0;
}
static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
static int (*ast_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
int (*messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs))
{
ast_has_voicemail_func = has_voicemail_func;
ast_messagecount_func = messagecount_func;
}
void ast_uninstall_vm_functions(void)
{
ast_has_voicemail_func = NULL;
ast_messagecount_func = NULL;
}
int ast_app_has_voicemail(const char *mailbox, const char *folder)
if (ast_has_voicemail_func)
return ast_has_voicemail_func(mailbox, folder);
if ((option_verbose > 2) && !warned) {
ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
{
if (newmsgs)
*newmsgs = 0;
if (oldmsgs)
*oldmsgs = 0;
if (ast_messagecount_func)
return ast_messagecount_func(mailbox, newmsgs, oldmsgs);
if (!warned && (option_verbose > 2)) {
warned++;
ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between)
const char *ptr;
struct ast_frame f = {
.frametype = AST_FRAME_DTMF,
.src = "ast_dtmf_stream"
};
if (!res)
res = ast_waitfor(chan, 100);
/* ast_waitfor will return the number of remaining ms on success */
if (res < 0)
return res;
for (ptr = digits; *ptr; ptr++) {
if (*ptr == 'w') {
/* 'w' -- wait half a second */
if ((res = ast_safe_sleep(chan, 500)))
break;
} else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
/* Character represents valid DTMF */
if (*ptr == 'f' || *ptr == 'F') {
/* ignore return values if not supported by channel */
ast_indicate(chan, AST_CONTROL_FLASH);
} else {
if ((res = ast_write(chan, &f)))
break;
/* pause between digits */
if ((res = ast_safe_sleep(chan, between)))
break;
} else
ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
if (peer) {
/* Stop autoservice on the peer channel, but don't overwrite any error condition
that has occurred previously while acting on the primary channel */
if (ast_autoservice_stop(peer) && !res)
res = -1;
}
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
385
386
387
struct linear_state {
int fd;
int autoclose;
int allowoverride;
int 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 '%d'\n", chan->name, ls->origwfmt);
}
if (ls->autoclose)
close(ls->fd);
free(params);
}
static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
{
struct ast_frame f;
short buf[2048 + AST_FRIENDLY_OFFSET / 2];
struct linear_state *ls = data;
int res;
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;
}
memset(&f, 0, sizeof(f));
res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
if (res > 0) {
f.frametype = AST_FRAME_VOICE;
f.subclass = AST_FORMAT_SLINEAR;
f.data = buf + AST_FRIENDLY_OFFSET/2;
f.datalen = res;
f.samples = res / 2;
f.offset = AST_FRIENDLY_OFFSET;
ast_write(chan, &f);
if (res == len)
return 0;
}
return -1;
}
static void *linear_alloc(struct ast_channel *chan, void *params)
{
struct linear_state *ls;
/* In this case, params is already malloc'd */
if (params) {
ls = params;
if (ls->allowoverride)
ast_clear_flag(chan, AST_FLAG_WRITE_INT);
ls->origwfmt = chan->writeformat;
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
free(ls);
ls = params = NULL;
}
}
return params;
}
static struct ast_generator linearstream =
{
alloc: linear_alloc,
release: linear_release,
generate: linear_generator,
};
int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
{
struct linear_state *lin;
int res = -1;
int autoclose = 0;
if (fd < 0) {
if (ast_strlen_zero(filename))
return -1;
autoclose = 1;
if (filename[0] == '/')
ast_copy_string(tmpf, filename, sizeof(tmpf));
else
snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
fd = open(tmpf, O_RDONLY);
if (fd < 0){
ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
return -1;
}
}
if ((lin = ast_calloc(1, sizeof(*lin)))) {
lin->fd = fd;
lin->allowoverride = allowoverride;
lin->autoclose = autoclose;
res = ast_activate_generator(chan, &linearstream, lin);
}
return res;
}
Kevin P. Fleming
committed
int ast_control_streamfile(struct ast_channel *chan, const char *file,
const char *fwd, const char *rev,
const char *stop, const char *pause,
const char *restart, int skipms)
char *breaks = NULL;
char *end = NULL;
int blen = 2;
int res;
long pause_restart_point = 0;
Mark Spencer
committed
if (stop)
blen += strlen(stop);
if (pause)
blen += strlen(pause);
Kevin P. Fleming
committed
if (restart)
blen += strlen(restart);
Mark Spencer
committed
breaks = alloca(blen + 1);
breaks[0] = '\0';
Kevin P. Fleming
committed
if (stop)
strcat(breaks, stop);
if (pause)
strcat(breaks, pause);
if (restart)
strcat(breaks, restart);
}
Anthony Minessale II
committed
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
if (file) {
if ((end = strchr(file,':'))) {
if (!strcasecmp(end, ":end")) {
ast_stopstream(chan);
Anthony Minessale II
committed
res = ast_streamfile(chan, file, chan->language);
if (pause_restart_point) {
ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
pause_restart_point = 0;
Anthony Minessale II
committed
}
else if (end) {
ast_seekstream(chan->stream, 0, SEEK_END);
end = NULL;
};
res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
Anthony Minessale II
committed
}
if (res < 1)
break;
Kevin P. Fleming
committed
/* We go at next loop if we got the restart char */
if (restart && strchr(restart, res)) {
ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
pause_restart_point = 0;
Kevin P. Fleming
committed
continue;
}
if (pause && strchr(pause, res)) {
pause_restart_point = ast_tellstream(chan->stream);
for (;;) {
ast_stopstream(chan);
Anthony Minessale II
committed
res = ast_waitfordigit(chan, 1000);
else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
Anthony Minessale II
committed
break;
}
Anthony Minessale II
committed
res = 0;
continue;
}
}
Anthony Minessale II
committed
break;
/* if we get one of our stop chars, return it to the calling function */
if (stop && strchr(stop, res))
Anthony Minessale II
committed
break;
}
ast_stopstream(chan);
Anthony Minessale II
committed
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
{
int d;
d = ast_streamfile(chan, fn, chan->language);
if (d)
return d;
d = ast_waitstream(chan, AST_DIGIT_ANY);
ast_stopstream(chan);
return d;
}
static int global_silence_threshold = 128;
static int global_maxsilence = 0;
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
Kevin P. Fleming
committed
int d;
char *fmts;
char comment[256];
int x, fmtcnt=1, res=-1,outmsg=0;
struct ast_frame *f;
struct ast_filestream *others[MAX_OTHER_FORMATS];
char *sfmt[MAX_OTHER_FORMATS];
char *stringp=NULL;
time_t start, end;
struct ast_dsp *sildet=NULL; /* silence detector dsp */
int totalsilence = 0;
int dspsilence = 0;
int gotsilence = 0; /* did we timeout for silence? */
int rfmt=0;
struct ast_silence_generator *silgen = NULL;
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
if (silencethreshold < 0)
silencethreshold = global_silence_threshold;
if (maxsilence < 0)
maxsilence = global_maxsilence;
/* barf if no pointer passed to store duration in */
if (duration == NULL) {
ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
return -1;
}
ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
if (playfile) {
d = ast_play_and_wait(chan, playfile);
if (d > -1)
d = ast_streamfile(chan, "beep",chan->language);
if (!d)
d = ast_waitstream(chan,"");
if (d < 0)
return -1;
}
fmts = ast_strdupa(fmt);
stringp=fmts;
strsep(&stringp, "|");
ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
sfmt[0] = ast_strdupa(fmts);
while((fmt = strsep(&stringp, "|"))) {
if (fmtcnt > MAX_OTHER_FORMATS - 1) {
ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
break;
}
sfmt[fmtcnt++] = ast_strdupa(fmt);
}
time(&start);
end=start; /* pre-initialize end to be same as start in case we never get into loop */
for (x=0;x<fmtcnt;x++) {
others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
Kevin P. Fleming
committed
ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing: %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
if (!others[x]) {
break;
}
}
if (path)
ast_unlock_path(path);
if (maxsilence > 0) {
sildet = ast_dsp_new(); /* Create the silence detector */
if (!sildet) {
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
return -1;
}
ast_dsp_set_threshold(sildet, silencethreshold);
rfmt = chan->readformat;
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
ast_dsp_free(sildet);
return -1;
}
}
/* Request a video update */
ast_indicate(chan, AST_CONTROL_VIDUPDATE);
Russell Bryant
committed
if (ast_opt_transmit_silence)
silgen = ast_channel_start_silence_generator(chan);
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
if (x == fmtcnt) {
/* Loop forever, writing the packets we read to the writer(s), until
we read a # or get a hangup */
f = NULL;
for(;;) {
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
/* Try one more time in case of masq */
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
res = -1;
}
}
if (res < 0) {
f = NULL;
break;
}
f = ast_read(chan);
if (!f)
break;
if (f->frametype == AST_FRAME_VOICE) {
/* write each format */
for (x=0;x<fmtcnt;x++) {
res = ast_writestream(others[x], f);
}
/* Silence Detection */
if (maxsilence > 0) {
dspsilence = 0;
ast_dsp_silence(sildet, f, &dspsilence);
if (dspsilence)
totalsilence = dspsilence;
else
totalsilence = 0;
if (totalsilence > maxsilence) {
/* Ended happily with silence */
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
ast_frfree(f);
gotsilence = 1;
outmsg=2;
break;
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
}
}
/* Exit on any error */
if (res) {
ast_log(LOG_WARNING, "Error writing frame\n");
ast_frfree(f);
break;
}
} else if (f->frametype == AST_FRAME_VIDEO) {
/* Write only once */
ast_writestream(others[0], f);
} else if (f->frametype == AST_FRAME_DTMF) {
if (f->subclass == '#') {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
res = '#';
outmsg = 2;
ast_frfree(f);
break;
}
if (f->subclass == '0') {
/* Check for a '0' during message recording also, in case caller wants operator */
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
res = '0';
outmsg = 0;
ast_frfree(f);
break;
}
if (maxtime) {
time(&end);
if (maxtime < (end - start)) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
outmsg = 2;
res = 't';
ast_frfree(f);
break;
}
}
ast_frfree(f);
}
if (end == start) time(&end);
if (!f) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
res = -1;
outmsg=1;
}
} else {
ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
}
if (silgen)
ast_channel_stop_silence_generator(chan, silgen);
*duration = end - start;
for (x=0;x<fmtcnt;x++) {
if (!others[x])
break;
if (res > 0) {
if (totalsilence)
ast_stream_rewind(others[x], totalsilence-200);
else
ast_stream_rewind(others[x], 200);
}
ast_truncstream(others[x]);
ast_closestream(others[x]);
}
if (rfmt) {
if (ast_set_read_format(chan, rfmt)) {
ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
}
}
/* Let them know recording is stopped */
if(!ast_streamfile(chan, "auth-thankyou", chan->language))
ast_waitstream(chan, "");
}
if (sildet)
ast_dsp_free(sildet);
return res;
}
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
{
Kevin P. Fleming
committed
int d = 0;
char *fmts;
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
char comment[256];
int x, fmtcnt=1, res=-1,outmsg=0;
struct ast_frame *f;
struct ast_filestream *others[MAX_OTHER_FORMATS];
struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
char *sfmt[MAX_OTHER_FORMATS];
char *stringp=NULL;
time_t start, end;
struct ast_dsp *sildet; /* silence detector dsp */
int totalsilence = 0;
int dspsilence = 0;
int gotsilence = 0; /* did we timeout for silence? */
int rfmt=0;
char prependfile[80];
if (silencethreshold < 0)
silencethreshold = global_silence_threshold;
if (maxsilence < 0)
maxsilence = global_maxsilence;
/* barf if no pointer passed to store duration in */
if (duration == NULL) {
ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
return -1;
}
ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
if (playfile || beep) {
if (!beep)
d = ast_play_and_wait(chan, playfile);
if (d > -1)
d = ast_streamfile(chan, "beep",chan->language);
if (!d)
d = ast_waitstream(chan,"");
if (d < 0)
return -1;
}
ast_copy_string(prependfile, recordfile, sizeof(prependfile));
strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
fmts = ast_strdupa(fmt);
stringp=fmts;
strsep(&stringp, "|");
ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
sfmt[0] = ast_strdupa(fmts);
while((fmt = strsep(&stringp, "|"))) {
if (fmtcnt > MAX_OTHER_FORMATS - 1) {
ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
break;
}
sfmt[fmtcnt++] = ast_strdupa(fmt);
}
time(&start);
end=start; /* pre-initialize end to be same as start in case we never get into loop */
for (x=0;x<fmtcnt;x++) {
others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
Kevin P. Fleming
committed
ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing: %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
if (!others[x]) {
break;
}
}
sildet = ast_dsp_new(); /* Create the silence detector */
if (!sildet) {
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
return -1;
}
ast_dsp_set_threshold(sildet, silencethreshold);
if (maxsilence > 0) {
rfmt = chan->readformat;
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
return -1;
}
}
if (x == fmtcnt) {
/* Loop forever, writing the packets we read to the writer(s), until
we read a # or get a hangup */
f = NULL;
for(;;) {
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
/* Try one more time in case of masq */
res = ast_waitfor(chan, 2000);
if (!res) {
ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
res = -1;
}
}
if (res < 0) {
f = NULL;
break;
}
f = ast_read(chan);
if (!f)
break;
if (f->frametype == AST_FRAME_VOICE) {
/* write each format */
for (x=0;x<fmtcnt;x++) {
if (!others[x])
break;
res = ast_writestream(others[x], f);
}
/* Silence Detection */
if (maxsilence > 0) {
dspsilence = 0;
ast_dsp_silence(sildet, f, &dspsilence);
if (dspsilence)
totalsilence = dspsilence;
else
totalsilence = 0;
if (totalsilence > maxsilence) {
/* Ended happily with silence */
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
ast_frfree(f);
gotsilence = 1;
outmsg=2;
break;
}
}
/* Exit on any error */
if (res) {
ast_log(LOG_WARNING, "Error writing frame\n");
ast_frfree(f);
break;
}
} else if (f->frametype == AST_FRAME_VIDEO) {
/* Write only once */
ast_writestream(others[0], f);
} else if (f->frametype == AST_FRAME_DTMF) {
/* stop recording with any digit */
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
res = 't';
outmsg = 2;
ast_frfree(f);
break;
}
if (maxtime) {
time(&end);
if (maxtime < (end - start)) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
res = 't';
outmsg=2;
ast_frfree(f);
break;
}
}
ast_frfree(f);
}
if (end == start) time(&end);
if (!f) {
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
res = -1;
outmsg=1;
#if 0
/* delete all the prepend files */
for (x=0;x<fmtcnt;x++) {
if (!others[x])
break;
ast_closestream(others[x]);
ast_filedelete(prependfile, sfmt[x]);
}
#endif
}
} else {
ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
}
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
*duration = end - start;
#if 0
if (outmsg > 1) {
#else
if (outmsg) {
#endif
struct ast_frame *fr;
for (x=0;x<fmtcnt;x++) {
snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
if (!others[x] || !realfiles[x])
break;
if (totalsilence)
ast_stream_rewind(others[x], totalsilence-200);
else
ast_stream_rewind(others[x], 200);
ast_truncstream(others[x]);
/* add the original file too */
while ((fr = ast_readframe(realfiles[x]))) {
ast_writestream(others[x],fr);
}
ast_closestream(others[x]);
ast_closestream(realfiles[x]);
ast_filerename(prependfile, recordfile, sfmt[x]);
#if 0
ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
#endif
ast_filedelete(prependfile, sfmt[x]);
}
}
if (rfmt) {
if (ast_set_read_format(chan, rfmt)) {
ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
}
}
if (outmsg) {
if (outmsg > 1) {
/* Let them know it worked */
ast_streamfile(chan, "auth-thankyou", chan->language);
ast_waitstream(chan, "");
}
}
return res;
}
/* Channel group core functions */
int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
char *grp=NULL, *cat=NULL;
if (!ast_strlen_zero(data)) {
ast_copy_string(tmp, data, sizeof(tmp));
grp = tmp;
cat = strchr(tmp, '@');
if (cat) {
*cat = '\0';
cat++;
}
}
if (!ast_strlen_zero(grp))
ast_copy_string(group, grp, group_max);
else
res = -1;
if (cat)
snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
else
ast_copy_string(category, GROUP_CATEGORY_PREFIX, category_max);
return res;
}
int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
{
int res=0;
char group[80] = "";
char category[80] = "";
if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
pbx_builtin_setvar_helper(chan, category, group);
} else
res = -1;
return res;
}
int ast_app_group_get_count(const char *group, const char *category)
{
struct ast_channel *chan;
int count = 0;
const char *test;
const char *s;
if (ast_strlen_zero(group))
Kevin P. Fleming
committed
return 0;
s = S_OR(category, GROUP_CATEGORY_PREFIX);
Kevin P. Fleming
committed
ast_copy_string(cat, s, sizeof(cat));
chan = NULL;
while ((chan = ast_channel_walk_locked(chan)) != NULL) {
test = pbx_builtin_getvar_helper(chan, cat);
if (test && !strcasecmp(test, group))
count++;
ast_mutex_unlock(&chan->lock);
}
return count;
}
int ast_app_group_match_get_count(const char *groupmatch, const char *category)
{
regex_t regexbuf;
struct ast_channel *chan;
int count = 0;
const char *test;
const char *s;
if (ast_strlen_zero(groupmatch))
return 0;
/* if regex compilation fails, return zero matches */
if (regcomp(®exbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
return 0;
s = S_OR(category, GROUP_CATEGORY_PREFIX);
Kevin P. Fleming
committed
ast_copy_string(cat, s, sizeof(cat));
Kevin P. Fleming
committed
chan = NULL;
while ((chan = ast_channel_walk_locked(chan)) != NULL) {
test = pbx_builtin_getvar_helper(chan, cat);
if (test && !regexec(®exbuf, test, 0, NULL, 0))
count++;
ast_mutex_unlock(&chan->lock);
}
regfree(®exbuf);
return count;
}
unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
int argc;
Kevin P. Fleming
committed
char *scan;
Tilghman Lesher
committed
int paren = 0, quote = 0;
Kevin P. Fleming
committed
if (!buf || !array || !arraylen)
return 0;
Kevin P. Fleming
committed
memset(array, 0, arraylen * sizeof(*array));
scan = buf;
for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
array[argc] = scan;
for (; *scan; scan++) {
if (*scan == '(')
paren++;
else if (*scan == ')') {
if (paren)
paren--;
Tilghman Lesher
committed
} else if (*scan == '"') {
quote = quote ? 0 : 1;
/* Remove quote character from argument */
memmove(scan, scan + 1, strlen(scan));
scan--;
} else if (*scan == '\\') {
/* Literal character, don't parse */
memmove(scan, scan + 1, strlen(scan));
} else if ((*scan == delim) && !paren && !quote) {
*scan++ = '\0';
break;
}
}
Kevin P. Fleming
committed
if (*scan)
array[argc++] = scan;
Kevin P. Fleming
committed
return argc;
enum AST_LOCK_RESULT ast_lock_path(const char *path)
{
char *s;
char *fs;
int res;
int fd;
time_t start;
if (!(s = alloca(strlen(path) + 10)) || !(fs = alloca(strlen(path) + 20))) {
ast_log(LOG_WARNING, "Out of memory!\n");
}
snprintf(fs, strlen(path) + 19, "%s/.lock-%08x", path, rand());
fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0) {
fprintf(stderr, "Unable to create lock file '%s': %s\n", path, strerror(errno));
return AST_LOCK_PATH_NOT_FOUND;
}
close(fd);
snprintf(s, strlen(path) + 9, "%s/.lock", path);
time(&start);
while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
usleep(1);
ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
return AST_LOCK_TIMEOUT;
} else {
unlink(fs);
ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
return AST_LOCK_SUCCESS;
}
}
int ast_unlock_path(const char *path)
{
char *s;
if (!(s = alloca(strlen(path) + 10)))
return -1;
snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
return unlink(s);
}
int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
{
int silencethreshold = 128;
int maxsilence=0;
int res = 0;
int cmd = 0;
int max_attempts = 3;
int attempts = 0;
int recorded = 0;
int message_exists = 0;
/* Note that urgent and private are for flagging messages as such in the future */
/* barf if no pointer passed to store duration in */
if (duration == NULL) {
ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
return -1;
}
cmd = '3'; /* Want to start by recording */
while ((cmd >= 0) && (cmd != 't')) {
switch (cmd) {
case '1':
if (!message_exists) {
/* In this case, 1 is to record a message */
cmd = '3';
break;
} else {
ast_streamfile(chan, "vm-msgsaved", chan->language);
ast_waitstream(chan, "");
cmd = 't';
return res;
}
case '2':
/* Review */
ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
ast_streamfile(chan, recordfile, chan->language);
cmd = ast_waitstream(chan, AST_DIGIT_ANY);
break;
case '3':
message_exists = 0;
/* Record */
if (recorded == 1)
ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
else
ast_verbose(VERBOSE_PREFIX_3 "Recording\n");
recorded = 1;
cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
if (cmd == -1) {
/* User has hung up, no options to give */
return cmd;
}
if (cmd == '0') {
break;
} else if (cmd == '*') {
break;
}
else {
/* If all is well, a message exists */
message_exists = 1;
cmd = 0;
}
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '*':
case '#':
cmd = ast_play_and_wait(chan, "vm-sorry");
break;
default:
if (message_exists) {
cmd = ast_play_and_wait(chan, "vm-review");
}
else {
cmd = ast_play_and_wait(chan, "vm-torerecord");
if (!cmd)
cmd = ast_waitfordigit(chan, 600);
}
if (!cmd)
cmd = ast_waitfordigit(chan, 6000);
if (!cmd) {
attempts++;
}
if (attempts > max_attempts) {
cmd = 't';
}
}
}
if (cmd == 't')
cmd = 0;
return cmd;
}
#define RES_UPONE (1 << 16)
#define RES_EXIT (1 << 17)
#define RES_REPEAT (1 << 18)
#define RES_RESTART ((1 << 19) | RES_REPEAT)
static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
{
int res;
int (*ivr_func)(struct ast_channel *, void *);
char *c;
char *n;
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
switch(option->action) {
case AST_ACTION_UPONE:
return RES_UPONE;
case AST_ACTION_EXIT:
return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
case AST_ACTION_REPEAT:
return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
case AST_ACTION_RESTART:
return RES_RESTART ;
case AST_ACTION_NOOP:
return 0;
case AST_ACTION_BACKGROUND:
res = ast_streamfile(chan, (char *)option->adata, chan->language);
if (!res) {
res = ast_waitstream(chan, AST_DIGIT_ANY);
} else {
ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
res = 0;
}
return res;
case AST_ACTION_PLAYBACK:
res = ast_streamfile(chan, (char *)option->adata, chan->language);
if (!res) {
res = ast_waitstream(chan, "");
} else {
ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
res = 0;
}
return res;
case AST_ACTION_MENU:
res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
/* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
if (res == -2)
res = 0;
return res;
case AST_ACTION_WAITOPTION:
res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
if (!res)
return 't';
return res;
case AST_ACTION_CALLBACK:
ivr_func = option->adata;
res = ivr_func(chan, cbdata);
return res;
case AST_ACTION_TRANSFER:
res = ast_parseable_goto(chan, option->adata);
case AST_ACTION_PLAYLIST:
case AST_ACTION_BACKLIST:
res = 0;
c = ast_strdupa(option->adata);
Russell Bryant
committed
if (c) {
while((n = strsep(&c, ";")))
if ((res = ast_streamfile(chan, n, chan->language)) || (res = ast_waitstream(chan, (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
break;
ast_stopstream(chan);
}
return res;
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
default:
ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
return 0;
};
return -1;
}
static int option_exists(struct ast_ivr_menu *menu, char *option)
{
int x;
for (x=0;menu->options[x].option;x++)
if (!strcasecmp(menu->options[x].option, option))
return x;
return -1;
}
static int option_matchmore(struct ast_ivr_menu *menu, char *option)
{
int x;
for (x=0;menu->options[x].option;x++)
if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
(menu->options[x].option[strlen(option)]))
return x;
return -1;
}
static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
{
int res=0;
int ms;
while(option_matchmore(menu, exten)) {
ms = chan->pbx ? chan->pbx->dtimeout : 5000;
if (strlen(exten) >= maxexten - 1)
break;
res = ast_waitfordigit(chan, ms);
if (res < 1)
break;
exten[strlen(exten) + 1] = '\0';
exten[strlen(exten)] = res;
}
return res > 0 ? 0 : res;
}
static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
{
/* Execute an IVR menu structure */
int res=0;
int pos = 0;
int retries = 0;
char exten[AST_MAX_EXTENSION] = "s";
if (option_exists(menu, "s") < 0) {
strcpy(exten, "g");
if (option_exists(menu, "g") < 0) {
ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
return -1;
}
}
while(!res) {
while(menu->options[pos].option) {
if (!strcasecmp(menu->options[pos].option, exten)) {
res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
if (res < 0)
break;
else if (res & RES_UPONE)
return 0;
else if (res & RES_EXIT)
return res;
else if (res & RES_REPEAT) {
int maxretries = res & 0xffff;
if ((res & RES_RESTART) == RES_RESTART) {
retries++;
if (!maxretries)
maxretries = 3;
if ((maxretries > 0) && (retries >= maxretries)) {
ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
if (option_exists(menu, "g") > -1)
strcpy(exten, "g");
else if (option_exists(menu, "s") > -1)
strcpy(exten, "s");
}
pos=0;
} else if (res && strchr(AST_DIGIT_ANY, res)) {
ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
exten[1] = '\0';
exten[0] = res;
if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
break;
if (option_exists(menu, exten) < 0) {
if (option_exists(menu, "i")) {
ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
strcpy(exten, "i");
pos = 0;
continue;
} else {
ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
res = -2;
break;
}
} else {
ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
pos = 0;
continue;
}
}
}
pos++;
}
ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
pos = 0;
if (!strcasecmp(exten, "s"))
strcpy(exten, "g");
else
break;
}
return res;
}
int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
{
int res;
res = ast_ivr_menu_run_internal(chan, menu, cbdata);
/* Hide internal coding */
if (res > 0)
res = 0;
return res;
}
char *ast_read_textfile(const char *filename)
{
int fd;
char *output=NULL;
struct stat filesize;
int count=0;
int res;
if(stat(filename,&filesize)== -1){
ast_log(LOG_WARNING,"Error can't stat %s\n", filename);
return NULL;
}
count=filesize.st_size + 1;
fd = open(filename, O_RDONLY);
if (fd < 0) {
ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
return NULL;
}
if ((output = ast_malloc(count))) {
res = read(fd, output, count - 1);
if (res == count - 1) {
output[res] = '\0';
} else {
ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
free(output);
output = NULL;
}
close(fd);
return output;
}
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
{
char *s;
int curarg;
unsigned int argloc;
char *arg;
int res = 0;
ast_clear_flag(flags, AST_FLAGS_ALL);
if (!optstr)
return 0;
while (*s) {
curarg = *s++ & 0x7f;
ast_set_flag(flags, options[curarg].flag);
argloc = options[curarg].arg_index;
if (*s == '(') {
/* Has argument */
if (*s) {
if (argloc)
args[argloc - 1] = arg;
*s++ = '\0';
ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
} else if (argloc) {
args[argloc - 1] = NULL;
return res;
}