Don't keep repeating the warning over and over when the end of the call is reached...
[asterisk-bristuff.git] / main / app.c
blobf99a33226d2a94ff7d1eb4df183bc570e940c01d
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Convenient Application Routines
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/time.h>
34 #include <signal.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <dirent.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <regex.h>
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/file.h"
45 #include "asterisk/app.h"
46 #include "asterisk/dsp.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/options.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/indications.h"
53 #define MAX_OTHER_FORMATS 10
56 /* !
57 This function presents a dialtone and reads an extension into 'collect'
58 which must be a pointer to a **pre-initialized** array of char having a
59 size of 'size' suitable for writing to. It will collect no more than the smaller
60 of 'maxlen' or 'size' minus the original strlen() of collect digits.
61 \return 0 if extension does not exist, 1 if extension exists
63 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
65 struct tone_zone_sound *ts;
66 int res=0, x=0;
68 if (maxlen > size)
69 maxlen = size;
71 if (!timeout && chan->pbx)
72 timeout = chan->pbx->dtimeout;
73 else if (!timeout)
74 timeout = 5;
76 ts = ast_get_indication_tone(chan->zone,"dial");
77 if (ts && ts->data[0])
78 res = ast_playtones_start(chan, 0, ts->data, 0);
79 else
80 ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
82 for (x = strlen(collect); x < maxlen; ) {
83 res = ast_waitfordigit(chan, timeout);
84 if (!ast_ignore_pattern(context, collect))
85 ast_playtones_stop(chan);
86 if (res < 1)
87 break;
88 collect[x++] = res;
89 if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
90 if (collect[x-1] == '#') {
91 /* Not a valid extension, ending in #, assume the # was to finish dialing */
92 collect[x-1] = '\0';
94 break;
97 if (res >= 0)
98 res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
99 return res;
102 /*! \param c The channel to read from
103 * \param prompt The file to stream to the channel
104 * \param s The string to read in to. Must be at least the size of your length
105 * \param maxlen How many digits to read (maximum)
106 * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
107 * "ludicrous time" (essentially never times out) */
108 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
110 int res,to,fto;
111 /* XXX Merge with full version? XXX */
112 if (maxlen)
113 s[0] = '\0';
114 if (prompt) {
115 res = ast_streamfile(c, prompt, c->language);
116 if (res < 0)
117 return res;
119 fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
120 to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
122 if (timeout > 0)
123 fto = to = timeout;
124 if (timeout < 0)
125 fto = to = 1000000000;
126 res = ast_readstring(c, s, maxlen, to, fto, "#");
127 return res;
131 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
133 int res, to, fto;
134 if (prompt) {
135 res = ast_streamfile(c, prompt, c->language);
136 if (res < 0)
137 return res;
139 fto = 6000;
140 to = 2000;
141 if (timeout > 0)
142 fto = to = timeout;
143 if (timeout < 0)
144 fto = to = 1000000000;
145 res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
146 return res;
149 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
150 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
151 static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
153 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
154 int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
155 int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
157 ast_has_voicemail_func = has_voicemail_func;
158 ast_inboxcount_func = inboxcount_func;
159 ast_messagecount_func = messagecount_func;
162 void ast_uninstall_vm_functions(void)
164 ast_has_voicemail_func = NULL;
165 ast_inboxcount_func = NULL;
166 ast_messagecount_func = NULL;
169 int ast_app_has_voicemail(const char *mailbox, const char *folder)
171 static int warned = 0;
172 if (ast_has_voicemail_func)
173 return ast_has_voicemail_func(mailbox, folder);
175 if ((option_verbose > 2) && !warned) {
176 ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
177 warned++;
179 return 0;
183 int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
185 static int warned = 0;
186 if (newmsgs)
187 *newmsgs = 0;
188 if (oldmsgs)
189 *oldmsgs = 0;
190 if (ast_inboxcount_func)
191 return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
193 if (!warned && (option_verbose > 2)) {
194 warned++;
195 ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
198 return 0;
201 int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
203 static int warned = 0;
204 if (ast_messagecount_func)
205 return ast_messagecount_func(context, mailbox, folder);
207 if (!warned && (option_verbose > 2)) {
208 warned++;
209 ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox, context, folder);
212 return 0;
215 int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between)
217 const char *ptr;
218 int res = 0;
220 if (!between)
221 between = 100;
223 if (peer)
224 res = ast_autoservice_start(peer);
226 if (!res)
227 res = ast_waitfor(chan, 100);
229 /* ast_waitfor will return the number of remaining ms on success */
230 if (res < 0)
231 return res;
233 for (ptr = digits; *ptr; ptr++) {
234 if (*ptr == 'w') {
235 /* 'w' -- wait half a second */
236 if ((res = ast_safe_sleep(chan, 500)))
237 break;
238 } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
239 /* Character represents valid DTMF */
240 if (*ptr == 'f' || *ptr == 'F') {
241 /* ignore return values if not supported by channel */
242 ast_indicate(chan, AST_CONTROL_FLASH);
243 } else
244 ast_senddigit(chan, *ptr);
245 /* pause between digits */
246 if ((res = ast_safe_sleep(chan, between)))
247 break;
248 } else
249 ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
252 if (peer) {
253 /* Stop autoservice on the peer channel, but don't overwrite any error condition
254 that has occurred previously while acting on the primary channel */
255 if (ast_autoservice_stop(peer) && !res)
256 res = -1;
259 return res;
262 struct linear_state {
263 int fd;
264 int autoclose;
265 int allowoverride;
266 int origwfmt;
269 static void linear_release(struct ast_channel *chan, void *params)
271 struct linear_state *ls = params;
272 if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
273 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
275 if (ls->autoclose)
276 close(ls->fd);
277 free(params);
280 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
282 struct ast_frame f;
283 short buf[2048 + AST_FRIENDLY_OFFSET / 2];
284 struct linear_state *ls = data;
285 int res;
286 len = samples * 2;
287 if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
288 ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
289 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
291 memset(&f, 0, sizeof(f));
292 res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
293 if (res > 0) {
294 f.frametype = AST_FRAME_VOICE;
295 f.subclass = AST_FORMAT_SLINEAR;
296 f.data = buf + AST_FRIENDLY_OFFSET/2;
297 f.datalen = res;
298 f.samples = res / 2;
299 f.offset = AST_FRIENDLY_OFFSET;
300 ast_write(chan, &f);
301 if (res == len)
302 return 0;
304 return -1;
307 static void *linear_alloc(struct ast_channel *chan, void *params)
309 struct linear_state *ls;
310 /* In this case, params is already malloc'd */
311 if (params) {
312 ls = params;
313 if (ls->allowoverride)
314 ast_set_flag(chan, AST_FLAG_WRITE_INT);
315 else
316 ast_clear_flag(chan, AST_FLAG_WRITE_INT);
317 ls->origwfmt = chan->writeformat;
318 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
319 ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
320 free(ls);
321 ls = params = NULL;
324 return params;
327 static struct ast_generator linearstream =
329 alloc: linear_alloc,
330 release: linear_release,
331 generate: linear_generator,
334 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
336 struct linear_state *lin;
337 char tmpf[256];
338 int res = -1;
339 int autoclose = 0;
340 if (fd < 0) {
341 if (ast_strlen_zero(filename))
342 return -1;
343 autoclose = 1;
344 if (filename[0] == '/')
345 ast_copy_string(tmpf, filename, sizeof(tmpf));
346 else
347 snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR, "sounds", filename);
348 fd = open(tmpf, O_RDONLY);
349 if (fd < 0){
350 ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
351 return -1;
354 if ((lin = ast_calloc(1, sizeof(*lin)))) {
355 lin->fd = fd;
356 lin->allowoverride = allowoverride;
357 lin->autoclose = autoclose;
358 res = ast_activate_generator(chan, &linearstream, lin);
360 return res;
363 int ast_control_streamfile(struct ast_channel *chan, const char *file,
364 const char *fwd, const char *rev,
365 const char *stop, const char *pause,
366 const char *restart, int skipms)
368 char *breaks = NULL;
369 char *end = NULL;
370 int blen = 2;
371 int res;
372 long pause_restart_point = 0;
374 if (stop)
375 blen += strlen(stop);
376 if (pause)
377 blen += strlen(pause);
378 if (restart)
379 blen += strlen(restart);
381 if (blen > 2) {
382 breaks = alloca(blen + 1);
383 breaks[0] = '\0';
384 if (stop)
385 strcat(breaks, stop);
386 if (pause)
387 strcat(breaks, pause);
388 if (restart)
389 strcat(breaks, restart);
391 if (chan->_state != AST_STATE_UP)
392 res = ast_answer(chan);
394 if (file) {
395 if ((end = strchr(file,':'))) {
396 if (!strcasecmp(end, ":end")) {
397 *end = '\0';
398 end++;
403 for (;;) {
404 ast_stopstream(chan);
405 res = ast_streamfile(chan, file, chan->language);
406 if (!res) {
407 if (pause_restart_point) {
408 ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
409 pause_restart_point = 0;
411 else if (end) {
412 ast_seekstream(chan->stream, 0, SEEK_END);
413 end = NULL;
415 res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
418 if (res < 1)
419 break;
421 /* We go at next loop if we got the restart char */
422 if (restart && strchr(restart, res)) {
423 ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
424 pause_restart_point = 0;
425 continue;
428 if (pause && strchr(pause, res)) {
429 pause_restart_point = ast_tellstream(chan->stream);
430 for (;;) {
431 ast_stopstream(chan);
432 res = ast_waitfordigit(chan, 1000);
433 if (!res)
434 continue;
435 else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
436 break;
438 if (res == *pause) {
439 res = 0;
440 continue;
444 if (res == -1)
445 break;
447 /* if we get one of our stop chars, return it to the calling function */
448 if (stop && strchr(stop, res))
449 break;
452 ast_stopstream(chan);
454 return res;
457 int ast_play_and_wait(struct ast_channel *chan, const char *fn)
459 int d;
460 d = ast_streamfile(chan, fn, chan->language);
461 if (d)
462 return d;
463 d = ast_waitstream(chan, AST_DIGIT_ANY);
464 ast_stopstream(chan);
465 return d;
468 static int global_silence_threshold = 128;
469 static int global_maxsilence = 0;
471 /*! Optionally play a sound file or a beep, then record audio and video from the channel.
472 * @param chan Channel to playback to/record from.
473 * @param playfile Filename of sound to play before recording begins.
474 * @param recordfile Filename to record to.
475 * @param maxtime Maximum length of recording (in milliseconds).
476 * @param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
477 * @param duration Where to store actual length of the recorded message (in milliseconds).
478 * @param beep Whether to play a beep before starting to record.
479 * @param silencethreshold
480 * @param maxsilence Length of silence that will end a recording (in milliseconds).
481 * @param path Optional filesystem path to unlock.
482 * @param prepend If true, prepend the recorded audio to an existing file.
483 * @param acceptdtmf DTMF digits that will end the recording.
484 * @param canceldtmf DTMF digits that will cancel the recording.
487 static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
489 int d = 0;
490 char *fmts;
491 char comment[256];
492 int x, fmtcnt = 1, res = -1, outmsg = 0;
493 struct ast_filestream *others[MAX_OTHER_FORMATS];
494 char *sfmt[MAX_OTHER_FORMATS];
495 char *stringp = NULL;
496 time_t start, end;
497 struct ast_dsp *sildet = NULL; /* silence detector dsp */
498 int totalsilence = 0;
499 int rfmt = 0;
500 struct ast_silence_generator *silgen = NULL;
501 char prependfile[80];
503 if (silencethreshold < 0)
504 silencethreshold = global_silence_threshold;
506 if (maxsilence < 0)
507 maxsilence = global_maxsilence;
509 /* barf if no pointer passed to store duration in */
510 if (duration == NULL) {
511 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
512 return -1;
515 ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
516 snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
518 if (playfile || beep) {
519 if (!beep)
520 d = ast_play_and_wait(chan, playfile);
521 if (d > -1)
522 d = ast_stream_and_wait(chan, "beep", chan->language, "");
523 if (d < 0)
524 return -1;
527 if (prepend) {
528 ast_copy_string(prependfile, recordfile, sizeof(prependfile));
529 strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
532 fmts = ast_strdupa(fmt);
534 stringp = fmts;
535 strsep(&stringp, "|");
536 ast_log(LOG_DEBUG, "Recording Formats: sfmts=%s\n", fmts);
537 sfmt[0] = ast_strdupa(fmts);
539 while ((fmt = strsep(&stringp, "|"))) {
540 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
541 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
542 break;
544 sfmt[fmtcnt++] = ast_strdupa(fmt);
547 end = start = time(NULL); /* pre-initialize end to be same as start in case we never get into loop */
548 for (x = 0; x < fmtcnt; x++) {
549 others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
550 if (option_verbose > 2)
551 ast_verbose(VERBOSE_PREFIX_3 "x=%d, open writing: %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
553 if (!others[x])
554 break;
557 if (path)
558 ast_unlock_path(path);
560 if (maxsilence > 0) {
561 sildet = ast_dsp_new(); /* Create the silence detector */
562 if (!sildet) {
563 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
564 return -1;
566 ast_dsp_set_threshold(sildet, silencethreshold);
567 rfmt = chan->readformat;
568 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
569 if (res < 0) {
570 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
571 ast_dsp_free(sildet);
572 return -1;
576 if (!prepend) {
577 /* Request a video update */
578 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
580 if (ast_opt_transmit_silence)
581 silgen = ast_channel_start_silence_generator(chan);
584 if (x == fmtcnt) {
585 /* Loop forever, writing the packets we read to the writer(s), until
586 we read a digit or get a hangup */
587 struct ast_frame *f;
588 for (;;) {
589 res = ast_waitfor(chan, 2000);
590 if (!res) {
591 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
592 /* Try one more time in case of masq */
593 res = ast_waitfor(chan, 2000);
594 if (!res) {
595 ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
596 res = -1;
600 if (res < 0) {
601 f = NULL;
602 break;
604 f = ast_read(chan);
605 if (!f)
606 break;
607 if (f->frametype == AST_FRAME_VOICE) {
608 /* write each format */
609 for (x = 0; x < fmtcnt; x++) {
610 if (prepend && !others[x])
611 break;
612 res = ast_writestream(others[x], f);
615 /* Silence Detection */
616 if (maxsilence > 0) {
617 int dspsilence = 0;
618 ast_dsp_silence(sildet, f, &dspsilence);
619 if (dspsilence)
620 totalsilence = dspsilence;
621 else
622 totalsilence = 0;
624 if (totalsilence > maxsilence) {
625 /* Ended happily with silence */
626 if (option_verbose > 2)
627 ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
628 res = 'S';
629 outmsg = 2;
630 break;
633 /* Exit on any error */
634 if (res) {
635 ast_log(LOG_WARNING, "Error writing frame\n");
636 break;
638 } else if (f->frametype == AST_FRAME_VIDEO) {
639 /* Write only once */
640 ast_writestream(others[0], f);
641 } else if (f->frametype == AST_FRAME_DTMF) {
642 if (prepend) {
643 /* stop recording with any digit */
644 if (option_verbose > 2)
645 ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
646 res = 't';
647 outmsg = 2;
648 break;
650 if (strchr(acceptdtmf, f->subclass)) {
651 if (option_verbose > 2)
652 ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
653 res = f->subclass;
654 outmsg = 2;
655 break;
657 if (strchr(canceldtmf, f->subclass)) {
658 if (option_verbose > 2)
659 ast_verbose(VERBOSE_PREFIX_3 "User cancelled message by pressing %c\n", f->subclass);
660 res = f->subclass;
661 outmsg = 0;
662 break;
665 if (maxtime) {
666 end = time(NULL);
667 if (maxtime < (end - start)) {
668 if (option_verbose > 2)
669 ast_verbose(VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
670 res = 't';
671 outmsg = 2;
672 break;
675 ast_frfree(f);
677 if (!f) {
678 if (option_verbose > 2)
679 ast_verbose(VERBOSE_PREFIX_3 "User hung up\n");
680 res = -1;
681 outmsg = 1;
682 } else {
683 ast_frfree(f);
685 if (end == start)
686 end = time(NULL);
687 } else {
688 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
691 if (!prepend) {
692 if (silgen)
693 ast_channel_stop_silence_generator(chan, silgen);
695 *duration = end - start;
697 if (!prepend) {
698 for (x = 0; x < fmtcnt; x++) {
699 if (!others[x])
700 break;
701 if (res > 0)
702 ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
703 ast_truncstream(others[x]);
704 ast_closestream(others[x]);
708 if (prepend && outmsg) {
709 struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
710 struct ast_frame *fr;
712 for (x = 0; x < fmtcnt; x++) {
713 snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
714 realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
715 if (!others[x] || !realfiles[x])
716 break;
717 ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
718 ast_truncstream(others[x]);
719 /* add the original file too */
720 while ((fr = ast_readframe(realfiles[x]))) {
721 ast_writestream(others[x], fr);
722 ast_frfree(fr);
724 ast_closestream(others[x]);
725 ast_closestream(realfiles[x]);
726 ast_filerename(prependfile, recordfile, sfmt[x]);
727 if (option_verbose > 3)
728 ast_verbose(VERBOSE_PREFIX_4 "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
729 ast_filedelete(prependfile, sfmt[x]);
732 if (rfmt && ast_set_read_format(chan, rfmt)) {
733 ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
735 if (outmsg == 2) {
736 ast_stream_and_wait(chan, "auth-thankyou", chan->language, "");
738 if (sildet)
739 ast_dsp_free(sildet);
740 return res;
743 static char default_acceptdtmf[] = "#";
744 static char default_canceldtmf[] = "";
746 int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
748 return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
751 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)
753 return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
756 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)
758 return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
761 /* Channel group core functions */
763 int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
765 int res=0;
766 char tmp[256];
767 char *grp=NULL, *cat=NULL;
769 if (!ast_strlen_zero(data)) {
770 ast_copy_string(tmp, data, sizeof(tmp));
771 grp = tmp;
772 cat = strchr(tmp, '@');
773 if (cat) {
774 *cat = '\0';
775 cat++;
779 if (!ast_strlen_zero(grp))
780 ast_copy_string(group, grp, group_max);
781 else
782 res = -1;
784 if (cat)
785 snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
786 else
787 ast_copy_string(category, GROUP_CATEGORY_PREFIX, category_max);
789 return res;
792 int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
794 int res=0;
795 char group[80] = "";
796 char category[80] = "";
798 if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
799 pbx_builtin_setvar_helper(chan, category, group);
800 } else
801 res = -1;
803 return res;
806 int ast_app_group_get_count(const char *group, const char *category)
808 struct ast_channel *chan;
809 int count = 0;
810 const char *test;
811 char cat[80];
812 const char *s;
814 if (ast_strlen_zero(group))
815 return 0;
817 s = S_OR(category, GROUP_CATEGORY_PREFIX);
818 ast_copy_string(cat, s, sizeof(cat));
820 chan = NULL;
821 while ((chan = ast_channel_walk_locked(chan)) != NULL) {
822 test = pbx_builtin_getvar_helper(chan, cat);
823 if (test && !strcasecmp(test, group))
824 count++;
825 ast_channel_unlock(chan);
828 return count;
831 int ast_app_group_match_get_count(const char *groupmatch, const char *category)
833 regex_t regexbuf;
834 struct ast_channel *chan;
835 int count = 0;
836 const char *test;
837 char cat[80];
838 const char *s;
840 if (ast_strlen_zero(groupmatch))
841 return 0;
843 /* if regex compilation fails, return zero matches */
844 if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
845 return 0;
847 s = S_OR(category, GROUP_CATEGORY_PREFIX);
848 ast_copy_string(cat, s, sizeof(cat));
850 chan = NULL;
851 while ((chan = ast_channel_walk_locked(chan)) != NULL) {
852 test = pbx_builtin_getvar_helper(chan, cat);
853 if (test && !regexec(&regexbuf, test, 0, NULL, 0))
854 count++;
855 ast_channel_unlock(chan);
858 regfree(&regexbuf);
860 return count;
863 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
865 int argc;
866 char *scan;
867 int paren = 0, quote = 0;
869 if (!buf || !array || !arraylen)
870 return 0;
872 memset(array, 0, arraylen * sizeof(*array));
874 scan = buf;
876 for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
877 array[argc] = scan;
878 for (; *scan; scan++) {
879 if (*scan == '(')
880 paren++;
881 else if (*scan == ')') {
882 if (paren)
883 paren--;
884 } else if (*scan == '"' && delim != '"') {
885 quote = quote ? 0 : 1;
886 /* Remove quote character from argument */
887 memmove(scan, scan + 1, strlen(scan));
888 scan--;
889 } else if (*scan == '\\') {
890 /* Literal character, don't parse */
891 memmove(scan, scan + 1, strlen(scan));
892 } else if ((*scan == delim) && !paren && !quote) {
893 *scan++ = '\0';
894 break;
899 if (*scan)
900 array[argc++] = scan;
902 return argc;
905 enum AST_LOCK_RESULT ast_lock_path(const char *path)
907 char *s;
908 char *fs;
909 int res;
910 int fd;
911 int lp = strlen(path);
912 time_t start;
914 if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
915 ast_log(LOG_WARNING, "Out of memory!\n");
916 return AST_LOCK_FAILURE;
919 snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
920 fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
921 if (fd < 0) {
922 ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
923 return AST_LOCK_PATH_NOT_FOUND;
925 close(fd);
927 snprintf(s, strlen(path) + 9, "%s/.lock", path);
928 start = time(NULL);
929 while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
930 usleep(1);
932 unlink(fs);
934 if (res) {
935 ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
936 return AST_LOCK_TIMEOUT;
937 } else {
938 ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
939 return AST_LOCK_SUCCESS;
943 int ast_unlock_path(const char *path)
945 char *s;
946 int res;
948 if (!(s = alloca(strlen(path) + 10))) {
949 ast_log(LOG_WARNING, "Out of memory!\n");
950 return -1;
953 snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
955 if ((res = unlink(s)))
956 ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
957 else
958 ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
960 return res;
963 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
965 int silencethreshold = 128;
966 int maxsilence=0;
967 int res = 0;
968 int cmd = 0;
969 int max_attempts = 3;
970 int attempts = 0;
971 int recorded = 0;
972 int message_exists = 0;
973 /* Note that urgent and private are for flagging messages as such in the future */
975 /* barf if no pointer passed to store duration in */
976 if (duration == NULL) {
977 ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
978 return -1;
981 cmd = '3'; /* Want to start by recording */
983 while ((cmd >= 0) && (cmd != 't')) {
984 switch (cmd) {
985 case '1':
986 if (!message_exists) {
987 /* In this case, 1 is to record a message */
988 cmd = '3';
989 break;
990 } else {
991 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
992 cmd = 't';
993 return res;
995 case '2':
996 /* Review */
997 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
998 cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
999 break;
1000 case '3':
1001 message_exists = 0;
1002 /* Record */
1003 if (recorded == 1)
1004 ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
1005 else
1006 ast_verbose(VERBOSE_PREFIX_3 "Recording\n");
1007 recorded = 1;
1008 cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
1009 if (cmd == -1) {
1010 /* User has hung up, no options to give */
1011 return cmd;
1013 if (cmd == '0') {
1014 break;
1015 } else if (cmd == '*') {
1016 break;
1018 else {
1019 /* If all is well, a message exists */
1020 message_exists = 1;
1021 cmd = 0;
1023 break;
1024 case '4':
1025 case '5':
1026 case '6':
1027 case '7':
1028 case '8':
1029 case '9':
1030 case '*':
1031 case '#':
1032 cmd = ast_play_and_wait(chan, "vm-sorry");
1033 break;
1034 default:
1035 if (message_exists) {
1036 cmd = ast_play_and_wait(chan, "vm-review");
1038 else {
1039 cmd = ast_play_and_wait(chan, "vm-torerecord");
1040 if (!cmd)
1041 cmd = ast_waitfordigit(chan, 600);
1044 if (!cmd)
1045 cmd = ast_waitfordigit(chan, 6000);
1046 if (!cmd) {
1047 attempts++;
1049 if (attempts > max_attempts) {
1050 cmd = 't';
1054 if (cmd == 't')
1055 cmd = 0;
1056 return cmd;
1059 #define RES_UPONE (1 << 16)
1060 #define RES_EXIT (1 << 17)
1061 #define RES_REPEAT (1 << 18)
1062 #define RES_RESTART ((1 << 19) | RES_REPEAT)
1064 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
1066 static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
1068 int res;
1069 int (*ivr_func)(struct ast_channel *, void *);
1070 char *c;
1071 char *n;
1073 switch(option->action) {
1074 case AST_ACTION_UPONE:
1075 return RES_UPONE;
1076 case AST_ACTION_EXIT:
1077 return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
1078 case AST_ACTION_REPEAT:
1079 return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
1080 case AST_ACTION_RESTART:
1081 return RES_RESTART ;
1082 case AST_ACTION_NOOP:
1083 return 0;
1084 case AST_ACTION_BACKGROUND:
1085 res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, AST_DIGIT_ANY);
1086 if (res < 0) {
1087 ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
1088 res = 0;
1090 return res;
1091 case AST_ACTION_PLAYBACK:
1092 res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, "");
1093 if (res < 0) {
1094 ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
1095 res = 0;
1097 return res;
1098 case AST_ACTION_MENU:
1099 res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
1100 /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
1101 if (res == -2)
1102 res = 0;
1103 return res;
1104 case AST_ACTION_WAITOPTION:
1105 res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
1106 if (!res)
1107 return 't';
1108 return res;
1109 case AST_ACTION_CALLBACK:
1110 ivr_func = option->adata;
1111 res = ivr_func(chan, cbdata);
1112 return res;
1113 case AST_ACTION_TRANSFER:
1114 res = ast_parseable_goto(chan, option->adata);
1115 return 0;
1116 case AST_ACTION_PLAYLIST:
1117 case AST_ACTION_BACKLIST:
1118 res = 0;
1119 c = ast_strdupa(option->adata);
1120 while ((n = strsep(&c, ";"))) {
1121 if ((res = ast_stream_and_wait(chan, n, chan->language,
1122 (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
1123 break;
1125 ast_stopstream(chan);
1126 return res;
1127 default:
1128 ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
1129 return 0;
1131 return -1;
1134 static int option_exists(struct ast_ivr_menu *menu, char *option)
1136 int x;
1137 for (x = 0; menu->options[x].option; x++)
1138 if (!strcasecmp(menu->options[x].option, option))
1139 return x;
1140 return -1;
1143 static int option_matchmore(struct ast_ivr_menu *menu, char *option)
1145 int x;
1146 for (x = 0; menu->options[x].option; x++)
1147 if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
1148 (menu->options[x].option[strlen(option)]))
1149 return x;
1150 return -1;
1153 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
1155 int res=0;
1156 int ms;
1157 while (option_matchmore(menu, exten)) {
1158 ms = chan->pbx ? chan->pbx->dtimeout : 5000;
1159 if (strlen(exten) >= maxexten - 1)
1160 break;
1161 res = ast_waitfordigit(chan, ms);
1162 if (res < 1)
1163 break;
1164 exten[strlen(exten) + 1] = '\0';
1165 exten[strlen(exten)] = res;
1167 return res > 0 ? 0 : res;
1170 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
1172 /* Execute an IVR menu structure */
1173 int res=0;
1174 int pos = 0;
1175 int retries = 0;
1176 char exten[AST_MAX_EXTENSION] = "s";
1177 if (option_exists(menu, "s") < 0) {
1178 strcpy(exten, "g");
1179 if (option_exists(menu, "g") < 0) {
1180 ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
1181 return -1;
1184 while(!res) {
1185 while(menu->options[pos].option) {
1186 if (!strcasecmp(menu->options[pos].option, exten)) {
1187 res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
1188 ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
1189 if (res < 0)
1190 break;
1191 else if (res & RES_UPONE)
1192 return 0;
1193 else if (res & RES_EXIT)
1194 return res;
1195 else if (res & RES_REPEAT) {
1196 int maxretries = res & 0xffff;
1197 if ((res & RES_RESTART) == RES_RESTART) {
1198 retries = 0;
1199 } else
1200 retries++;
1201 if (!maxretries)
1202 maxretries = 3;
1203 if ((maxretries > 0) && (retries >= maxretries)) {
1204 ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
1205 return -2;
1206 } else {
1207 if (option_exists(menu, "g") > -1)
1208 strcpy(exten, "g");
1209 else if (option_exists(menu, "s") > -1)
1210 strcpy(exten, "s");
1212 pos = 0;
1213 continue;
1214 } else if (res && strchr(AST_DIGIT_ANY, res)) {
1215 ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
1216 exten[1] = '\0';
1217 exten[0] = res;
1218 if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
1219 break;
1220 if (option_exists(menu, exten) < 0) {
1221 if (option_exists(menu, "i")) {
1222 ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
1223 strcpy(exten, "i");
1224 pos = 0;
1225 continue;
1226 } else {
1227 ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
1228 res = -2;
1229 break;
1231 } else {
1232 ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
1233 pos = 0;
1234 continue;
1238 pos++;
1240 ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
1241 pos = 0;
1242 if (!strcasecmp(exten, "s"))
1243 strcpy(exten, "g");
1244 else
1245 break;
1247 return res;
1250 int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
1252 int res = ast_ivr_menu_run_internal(chan, menu, cbdata);
1253 /* Hide internal coding */
1254 return res > 0 ? 0 : res;
1257 char *ast_read_textfile(const char *filename)
1259 int fd;
1260 char *output = NULL;
1261 struct stat filesize;
1262 int count = 0;
1263 int res;
1264 if (stat(filename, &filesize) == -1) {
1265 ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
1266 return NULL;
1268 count = filesize.st_size + 1;
1269 fd = open(filename, O_RDONLY);
1270 if (fd < 0) {
1271 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
1272 return NULL;
1274 if ((output = ast_malloc(count))) {
1275 res = read(fd, output, count - 1);
1276 if (res == count - 1) {
1277 output[res] = '\0';
1278 } else {
1279 ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
1280 free(output);
1281 output = NULL;
1284 close(fd);
1285 return output;
1288 int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
1290 char *s;
1291 int curarg;
1292 unsigned int argloc;
1293 char *arg;
1294 int res = 0;
1296 ast_clear_flag(flags, AST_FLAGS_ALL);
1298 if (!optstr)
1299 return 0;
1301 s = optstr;
1302 while (*s) {
1303 curarg = *s++ & 0x7f; /* the array (in app.h) has 128 entries */
1304 ast_set_flag(flags, options[curarg].flag);
1305 argloc = options[curarg].arg_index;
1306 if (*s == '(') {
1307 /* Has argument */
1308 arg = ++s;
1309 if ((s = strchr(s, ')'))) {
1310 if (argloc)
1311 args[argloc - 1] = arg;
1312 *s++ = '\0';
1313 } else {
1314 ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
1315 res = -1;
1316 break;
1318 } else if (argloc) {
1319 args[argloc - 1] = NULL;
1323 return res;