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.
21 * \brief Convenient Application Routines
23 * \author Mark Spencer <markster@digium.com>
34 #include <sys/types.h>
40 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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
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
;
71 if(!timeout
&& chan
->pbx
)
72 timeout
= chan
->pbx
->dtimeout
;
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);
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
);
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 */
98 res
= ast_exists_extension(chan
, context
, collect
, 1, chan
->cid
.cid_num
) ? 1 : 0;
104 /*! \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
105 "ludicrous time" (essentially never times out) */
106 int ast_app_getdata(struct ast_channel
*c
, char *prompt
, char *s
, int maxlen
, int timeout
)
109 /* XXX Merge with full version? XXX */
113 res
= ast_streamfile(c
, prompt
, c
->language
);
117 fto
= c
->pbx
? c
->pbx
->rtimeout
* 1000 : 6000;
118 to
= c
->pbx
? c
->pbx
->dtimeout
* 1000 : 2000;
123 fto
= to
= 1000000000;
124 res
= ast_readstring(c
, s
, maxlen
, to
, fto
, "#");
129 int ast_app_getdata_full(struct ast_channel
*c
, char *prompt
, char *s
, int maxlen
, int timeout
, int audiofd
, int ctrlfd
)
133 res
= ast_streamfile(c
, prompt
, c
->language
);
142 fto
= to
= 1000000000;
143 res
= ast_readstring_full(c
, s
, maxlen
, to
, fto
, "#", audiofd
, ctrlfd
);
147 int ast_app_getvoice(struct ast_channel
*c
, char *dest
, char *dstfmt
, char *prompt
, int silence
, int maxsec
)
150 struct ast_filestream
*writer
;
152 int totalms
=0, total
;
155 struct ast_dsp
*sildet
;
156 /* Play prompt if requested */
158 res
= ast_stream_and_wait(c
, prompt
, c
->language
, "");
162 rfmt
= c
->readformat
;
163 res
= ast_set_read_format(c
, AST_FORMAT_SLINEAR
);
165 ast_log(LOG_WARNING
, "Unable to set to linear mode, giving up\n");
168 sildet
= ast_dsp_new();
170 ast_log(LOG_WARNING
, "Unable to create silence detector :(\n");
173 writer
= ast_writefile(dest
, dstfmt
, "Voice file", 0, 0, 0666);
175 ast_log(LOG_WARNING
, "Unable to open file '%s' in format '%s' for writing\n", dest
, dstfmt
);
176 ast_dsp_free(sildet
);
180 if ((res
= ast_waitfor(c
, 2000)) < 0) {
181 ast_log(LOG_NOTICE
, "Waitfor failed while recording file '%s' format '%s'\n", dest
, dstfmt
);
187 ast_log(LOG_NOTICE
, "Hungup while recording file '%s' format '%s'\n", dest
, dstfmt
);
190 if ((f
->frametype
== AST_FRAME_DTMF
) && (f
->subclass
== '#')) {
191 /* Ended happily with DTMF */
194 } else if (f
->frametype
== AST_FRAME_VOICE
) {
195 ast_dsp_silence(sildet
, f
, &total
);
196 if (total
> silence
) {
197 /* Ended happily with silence */
201 totalms
+= f
->samples
/ 8;
202 if (totalms
> maxsec
* 1000) {
203 /* Ended happily with too much stuff */
204 ast_log(LOG_NOTICE
, "Constraining voice on '%s' to %d seconds\n", c
->name
, maxsec
);
208 res
= ast_writestream(writer
, f
);
210 ast_log(LOG_WARNING
, "Failed to write to stream at %s!\n", dest
);
219 res
= ast_set_read_format(c
, rfmt
);
221 ast_log(LOG_WARNING
, "Unable to restore read format on '%s'\n", c
->name
);
222 ast_dsp_free(sildet
);
223 ast_closestream(writer
);
227 static int (*ast_has_voicemail_func
)(const char *mailbox
, const char *folder
) = NULL
;
228 static int (*ast_messagecount_func
)(const char *mailbox
, int *newmsgs
, int *oldmsgs
) = NULL
;
230 void ast_install_vm_functions(int (*has_voicemail_func
)(const char *mailbox
, const char *folder
),
231 int (*messagecount_func
)(const char *mailbox
, int *newmsgs
, int *oldmsgs
))
233 ast_has_voicemail_func
= has_voicemail_func
;
234 ast_messagecount_func
= messagecount_func
;
237 void ast_uninstall_vm_functions(void)
239 ast_has_voicemail_func
= NULL
;
240 ast_messagecount_func
= NULL
;
243 int ast_app_has_voicemail(const char *mailbox
, const char *folder
)
245 static int warned
= 0;
246 if (ast_has_voicemail_func
)
247 return ast_has_voicemail_func(mailbox
, folder
);
249 if ((option_verbose
> 2) && !warned
) {
250 ast_verbose(VERBOSE_PREFIX_3
"Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox
, folder
? folder
: "INBOX");
257 int ast_app_messagecount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
259 static int warned
= 0;
264 if (ast_messagecount_func
)
265 return ast_messagecount_func(mailbox
, newmsgs
, oldmsgs
);
267 if (!warned
&& (option_verbose
> 2)) {
269 ast_verbose(VERBOSE_PREFIX_3
"Message count requested for mailbox %s but voicemail not loaded.\n", mailbox
);
275 int ast_dtmf_stream(struct ast_channel
*chan
, struct ast_channel
*peer
, const char *digits
, int between
)
279 struct ast_frame f
= {
280 .frametype
= AST_FRAME_DTMF
,
281 .src
= "ast_dtmf_stream"
288 res
= ast_autoservice_start(peer
);
291 res
= ast_waitfor(chan
, 100);
293 /* ast_waitfor will return the number of remaining ms on success */
297 for (ptr
= digits
; *ptr
; ptr
++) {
299 /* 'w' -- wait half a second */
300 if ((res
= ast_safe_sleep(chan
, 500)))
302 } else if (strchr("0123456789*#abcdfABCDF", *ptr
)) {
303 /* Character represents valid DTMF */
304 if (*ptr
== 'f' || *ptr
== 'F') {
305 /* ignore return values if not supported by channel */
306 ast_indicate(chan
, AST_CONTROL_FLASH
);
309 if ((res
= ast_write(chan
, &f
)))
312 /* pause between digits */
313 if ((res
= ast_safe_sleep(chan
, between
)))
316 ast_log(LOG_WARNING
, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr
);
320 /* Stop autoservice on the peer channel, but don't overwrite any error condition
321 that has occurred previously while acting on the primary channel */
322 if (ast_autoservice_stop(peer
) && !res
)
329 struct linear_state
{
336 static void linear_release(struct ast_channel
*chan
, void *params
)
338 struct linear_state
*ls
= params
;
339 if (ls
->origwfmt
&& ast_set_write_format(chan
, ls
->origwfmt
)) {
340 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format '%d'\n", chan
->name
, ls
->origwfmt
);
347 static int linear_generator(struct ast_channel
*chan
, void *data
, int len
, int samples
)
350 short buf
[2048 + AST_FRIENDLY_OFFSET
/ 2];
351 struct linear_state
*ls
= data
;
354 if (len
> sizeof(buf
) - AST_FRIENDLY_OFFSET
) {
355 ast_log(LOG_WARNING
, "Can't generate %d bytes of data!\n" ,len
);
356 len
= sizeof(buf
) - AST_FRIENDLY_OFFSET
;
358 memset(&f
, 0, sizeof(f
));
359 res
= read(ls
->fd
, buf
+ AST_FRIENDLY_OFFSET
/2, len
);
361 f
.frametype
= AST_FRAME_VOICE
;
362 f
.subclass
= AST_FORMAT_SLINEAR
;
363 f
.data
= buf
+ AST_FRIENDLY_OFFSET
/2;
366 f
.offset
= AST_FRIENDLY_OFFSET
;
374 static void *linear_alloc(struct ast_channel
*chan
, void *params
)
376 struct linear_state
*ls
;
377 /* In this case, params is already malloc'd */
380 if (ls
->allowoverride
)
381 ast_set_flag(chan
, AST_FLAG_WRITE_INT
);
383 ast_clear_flag(chan
, AST_FLAG_WRITE_INT
);
384 ls
->origwfmt
= chan
->writeformat
;
385 if (ast_set_write_format(chan
, AST_FORMAT_SLINEAR
)) {
386 ast_log(LOG_WARNING
, "Unable to set '%s' to linear format (write)\n", chan
->name
);
394 static struct ast_generator linearstream
=
397 release
: linear_release
,
398 generate
: linear_generator
,
401 int ast_linear_stream(struct ast_channel
*chan
, const char *filename
, int fd
, int allowoverride
)
403 struct linear_state
*lin
;
408 if (ast_strlen_zero(filename
))
411 if (filename
[0] == '/')
412 ast_copy_string(tmpf
, filename
, sizeof(tmpf
));
414 snprintf(tmpf
, sizeof(tmpf
), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR
, "sounds", filename
);
415 fd
= open(tmpf
, O_RDONLY
);
417 ast_log(LOG_WARNING
, "Unable to open file '%s': %s\n", tmpf
, strerror(errno
));
421 if ((lin
= ast_calloc(1, sizeof(*lin
)))) {
423 lin
->allowoverride
= allowoverride
;
424 lin
->autoclose
= autoclose
;
425 res
= ast_activate_generator(chan
, &linearstream
, lin
);
430 int ast_control_streamfile(struct ast_channel
*chan
, const char *file
,
431 const char *fwd
, const char *rev
,
432 const char *stop
, const char *pause
,
433 const char *restart
, int skipms
)
439 long pause_restart_point
= 0;
442 blen
+= strlen(stop
);
444 blen
+= strlen(pause
);
446 blen
+= strlen(restart
);
449 breaks
= alloca(blen
+ 1);
452 strcat(breaks
, stop
);
454 strcat(breaks
, pause
);
456 strcat(breaks
, restart
);
458 if (chan
->_state
!= AST_STATE_UP
)
459 res
= ast_answer(chan
);
462 if ((end
= strchr(file
,':'))) {
463 if (!strcasecmp(end
, ":end")) {
471 ast_stopstream(chan
);
472 res
= ast_streamfile(chan
, file
, chan
->language
);
474 if (pause_restart_point
) {
475 ast_seekstream(chan
->stream
, pause_restart_point
, SEEK_SET
);
476 pause_restart_point
= 0;
479 ast_seekstream(chan
->stream
, 0, SEEK_END
);
482 res
= ast_waitstream_fr(chan
, breaks
, fwd
, rev
, skipms
);
488 /* We go at next loop if we got the restart char */
489 if (restart
&& strchr(restart
, res
)) {
490 ast_log(LOG_DEBUG
, "we'll restart the stream here at next loop\n");
491 pause_restart_point
= 0;
495 if (pause
&& strchr(pause
, res
)) {
496 pause_restart_point
= ast_tellstream(chan
->stream
);
498 ast_stopstream(chan
);
499 res
= ast_waitfordigit(chan
, 1000);
502 else if (res
== -1 || strchr(pause
, res
) || (stop
&& strchr(stop
, res
)))
514 /* if we get one of our stop chars, return it to the calling function */
515 if (stop
&& strchr(stop
, res
))
519 ast_stopstream(chan
);
524 int ast_play_and_wait(struct ast_channel
*chan
, const char *fn
)
527 d
= ast_streamfile(chan
, fn
, chan
->language
);
530 d
= ast_waitstream(chan
, AST_DIGIT_ANY
);
531 ast_stopstream(chan
);
535 static int global_silence_threshold
= 128;
536 static int global_maxsilence
= 0;
538 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
)
543 int x
, fmtcnt
=1, res
=-1,outmsg
=0;
545 struct ast_filestream
*others
[MAX_OTHER_FORMATS
];
546 char *sfmt
[MAX_OTHER_FORMATS
];
549 struct ast_dsp
*sildet
=NULL
; /* silence detector dsp */
550 int totalsilence
= 0;
553 struct ast_silence_generator
*silgen
= NULL
;
555 if (silencethreshold
< 0)
556 silencethreshold
= global_silence_threshold
;
559 maxsilence
= global_maxsilence
;
561 /* barf if no pointer passed to store duration in */
562 if (duration
== NULL
) {
563 ast_log(LOG_WARNING
, "Error play_and_record called without duration pointer\n");
567 ast_log(LOG_DEBUG
,"play_and_record: %s, %s, '%s'\n", playfile
? playfile
: "<None>", recordfile
, fmt
);
568 snprintf(comment
,sizeof(comment
),"Playing %s, Recording to: %s on %s\n", playfile
? playfile
: "<None>", recordfile
, chan
->name
);
571 d
= ast_play_and_wait(chan
, playfile
);
573 d
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
578 fmts
= ast_strdupa(fmt
);
581 strsep(&stringp
, "|");
582 ast_log(LOG_DEBUG
,"Recording Formats: sfmts=%s\n", fmts
);
583 sfmt
[0] = ast_strdupa(fmts
);
585 while((fmt
= strsep(&stringp
, "|"))) {
586 if (fmtcnt
> MAX_OTHER_FORMATS
- 1) {
587 ast_log(LOG_WARNING
, "Please increase MAX_OTHER_FORMATS in app.c\n");
590 sfmt
[fmtcnt
++] = ast_strdupa(fmt
);
594 end
=start
; /* pre-initialize end to be same as start in case we never get into loop */
595 for (x
=0;x
<fmtcnt
;x
++) {
596 others
[x
] = ast_writefile(recordfile
, sfmt
[x
], comment
, O_TRUNC
, 0, 0700);
597 ast_verbose( VERBOSE_PREFIX_3
"x=%d, open writing: %s format: %s, %p\n", x
, recordfile
, sfmt
[x
], others
[x
]);
605 ast_unlock_path(path
);
607 if (maxsilence
> 0) {
608 sildet
= ast_dsp_new(); /* Create the silence detector */
610 ast_log(LOG_WARNING
, "Unable to create silence detector :(\n");
613 ast_dsp_set_threshold(sildet
, silencethreshold
);
614 rfmt
= chan
->readformat
;
615 res
= ast_set_read_format(chan
, AST_FORMAT_SLINEAR
);
617 ast_log(LOG_WARNING
, "Unable to set to linear mode, giving up\n");
618 ast_dsp_free(sildet
);
623 /* Request a video update */
624 ast_indicate(chan
, AST_CONTROL_VIDUPDATE
);
626 if (ast_opt_transmit_silence
)
627 silgen
= ast_channel_start_silence_generator(chan
);
630 /* Loop forever, writing the packets we read to the writer(s), until
631 we read a # or get a hangup */
634 res
= ast_waitfor(chan
, 2000);
636 ast_log(LOG_DEBUG
, "One waitfor failed, trying another\n");
637 /* Try one more time in case of masq */
638 res
= ast_waitfor(chan
, 2000);
640 ast_log(LOG_WARNING
, "No audio available on %s??\n", chan
->name
);
652 if (f
->frametype
== AST_FRAME_VOICE
) {
653 /* write each format */
654 for (x
=0;x
<fmtcnt
;x
++) {
655 res
= ast_writestream(others
[x
], f
);
658 /* Silence Detection */
659 if (maxsilence
> 0) {
661 ast_dsp_silence(sildet
, f
, &dspsilence
);
663 totalsilence
= dspsilence
;
667 if (totalsilence
> maxsilence
) {
668 /* Ended happily with silence */
669 if (option_verbose
> 2)
670 ast_verbose( VERBOSE_PREFIX_3
"Recording automatically stopped after a silence of %d seconds\n", totalsilence
/1000);
677 /* Exit on any error */
679 ast_log(LOG_WARNING
, "Error writing frame\n");
683 } else if (f
->frametype
== AST_FRAME_VIDEO
) {
684 /* Write only once */
685 ast_writestream(others
[0], f
);
686 } else if (f
->frametype
== AST_FRAME_DTMF
) {
687 if (f
->subclass
== '#') {
688 if (option_verbose
> 2)
689 ast_verbose( VERBOSE_PREFIX_3
"User ended message by pressing %c\n", f
->subclass
);
695 if (f
->subclass
== '0') {
696 /* Check for a '0' during message recording also, in case caller wants operator */
697 if (option_verbose
> 2)
698 ast_verbose(VERBOSE_PREFIX_3
"User cancelled by pressing %c\n", f
->subclass
);
707 if (maxtime
< (end
- start
)) {
708 if (option_verbose
> 2)
709 ast_verbose( VERBOSE_PREFIX_3
"Took too long, cutting it short...\n");
718 if (end
== start
) time(&end
);
720 if (option_verbose
> 2)
721 ast_verbose( VERBOSE_PREFIX_3
"User hung up\n");
726 ast_log(LOG_WARNING
, "Error creating writestream '%s', format '%s'\n", recordfile
, sfmt
[x
]);
730 ast_channel_stop_silence_generator(chan
, silgen
);
732 *duration
= end
- start
;
734 for (x
=0;x
<fmtcnt
;x
++) {
738 ast_stream_rewind(others
[x
], totalsilence
? totalsilence
-200 : 200);
739 ast_truncstream(others
[x
]);
740 ast_closestream(others
[x
]);
742 if (rfmt
&& ast_set_read_format(chan
, rfmt
)) {
743 ast_log(LOG_WARNING
, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt
), chan
->name
);
746 /* Let them know recording is stopped */
747 ast_stream_and_wait(chan
, "auth-thankyou", chan
->language
, "");
750 ast_dsp_free(sildet
);
754 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
)
759 int x
, fmtcnt
=1, res
=-1,outmsg
=0;
761 struct ast_filestream
*others
[MAX_OTHER_FORMATS
];
762 struct ast_filestream
*realfiles
[MAX_OTHER_FORMATS
];
763 char *sfmt
[MAX_OTHER_FORMATS
];
766 struct ast_dsp
*sildet
; /* silence detector dsp */
767 int totalsilence
= 0;
770 char prependfile
[80];
772 if (silencethreshold
< 0)
773 silencethreshold
= global_silence_threshold
;
776 maxsilence
= global_maxsilence
;
778 /* barf if no pointer passed to store duration in */
779 if (duration
== NULL
) {
780 ast_log(LOG_WARNING
, "Error play_and_prepend called without duration pointer\n");
784 ast_log(LOG_DEBUG
,"play_and_prepend: %s, %s, '%s'\n", playfile
? playfile
: "<None>", recordfile
, fmt
);
785 snprintf(comment
,sizeof(comment
),"Playing %s, Recording to: %s on %s\n", playfile
? playfile
: "<None>", recordfile
, chan
->name
);
787 if (playfile
|| beep
) {
789 d
= ast_play_and_wait(chan
, playfile
);
791 d
= ast_stream_and_wait(chan
, "beep",chan
->language
, "");
795 ast_copy_string(prependfile
, recordfile
, sizeof(prependfile
));
796 strncat(prependfile
, "-prepend", sizeof(prependfile
) - strlen(prependfile
) - 1);
798 fmts
= ast_strdupa(fmt
);
801 strsep(&stringp
, "|");
802 ast_log(LOG_DEBUG
,"Recording Formats: sfmts=%s\n", fmts
);
803 sfmt
[0] = ast_strdupa(fmts
);
805 while((fmt
= strsep(&stringp
, "|"))) {
806 if (fmtcnt
> MAX_OTHER_FORMATS
- 1) {
807 ast_log(LOG_WARNING
, "Please increase MAX_OTHER_FORMATS in app.c\n");
810 sfmt
[fmtcnt
++] = ast_strdupa(fmt
);
814 end
=start
; /* pre-initialize end to be same as start in case we never get into loop */
815 for (x
=0;x
<fmtcnt
;x
++) {
816 others
[x
] = ast_writefile(prependfile
, sfmt
[x
], comment
, O_TRUNC
, 0, 0700);
817 ast_verbose( VERBOSE_PREFIX_3
"x=%d, open writing: %s format: %s, %p\n", x
, prependfile
, sfmt
[x
], others
[x
]);
823 sildet
= ast_dsp_new(); /* Create the silence detector */
825 ast_log(LOG_WARNING
, "Unable to create silence detector :(\n");
828 ast_dsp_set_threshold(sildet
, silencethreshold
);
830 if (maxsilence
> 0) {
831 rfmt
= chan
->readformat
;
832 res
= ast_set_read_format(chan
, AST_FORMAT_SLINEAR
);
834 ast_log(LOG_WARNING
, "Unable to set to linear mode, giving up\n");
835 ast_dsp_free(sildet
);
841 /* Loop forever, writing the packets we read to the writer(s), until
842 we read a # or get a hangup */
845 res
= ast_waitfor(chan
, 2000);
847 ast_log(LOG_DEBUG
, "One waitfor failed, trying another\n");
848 /* Try one more time in case of masq */
849 res
= ast_waitfor(chan
, 2000);
851 ast_log(LOG_WARNING
, "No audio available on %s??\n", chan
->name
);
863 if (f
->frametype
== AST_FRAME_VOICE
) {
864 /* write each format */
865 for (x
=0;x
<fmtcnt
;x
++) {
868 res
= ast_writestream(others
[x
], f
);
871 /* Silence Detection */
872 if (maxsilence
> 0) {
874 ast_dsp_silence(sildet
, f
, &dspsilence
);
876 totalsilence
= dspsilence
;
880 if (totalsilence
> maxsilence
) {
881 /* Ended happily with silence */
882 if (option_verbose
> 2)
883 ast_verbose( VERBOSE_PREFIX_3
"Recording automatically stopped after a silence of %d seconds\n", totalsilence
/1000);
890 /* Exit on any error */
892 ast_log(LOG_WARNING
, "Error writing frame\n");
896 } else if (f
->frametype
== AST_FRAME_VIDEO
) {
897 /* Write only once */
898 ast_writestream(others
[0], f
);
899 } else if (f
->frametype
== AST_FRAME_DTMF
) {
900 /* stop recording with any digit */
901 if (option_verbose
> 2)
902 ast_verbose( VERBOSE_PREFIX_3
"User ended message by pressing %c\n", f
->subclass
);
910 if (maxtime
< (end
- start
)) {
911 if (option_verbose
> 2)
912 ast_verbose( VERBOSE_PREFIX_3
"Took too long, cutting it short...\n");
921 if (end
== start
) time(&end
);
923 if (option_verbose
> 2)
924 ast_verbose( VERBOSE_PREFIX_3
"User hung up\n");
928 /* delete all the prepend files */
929 for (x
=0;x
<fmtcnt
;x
++) {
932 ast_closestream(others
[x
]);
933 ast_filedelete(prependfile
, sfmt
[x
]);
938 ast_log(LOG_WARNING
, "Error creating writestream '%s', format '%s'\n", prependfile
, sfmt
[x
]);
940 ast_dsp_free(sildet
);
941 *duration
= end
- start
;
947 struct ast_frame
*fr
;
948 for (x
=0;x
<fmtcnt
;x
++) {
949 snprintf(comment
, sizeof(comment
), "Opening the real file %s.%s\n", recordfile
, sfmt
[x
]);
950 realfiles
[x
] = ast_readfile(recordfile
, sfmt
[x
], comment
, O_RDONLY
, 0, 0);
951 if (!others
[x
] || !realfiles
[x
])
954 ast_stream_rewind(others
[x
], totalsilence
-200);
956 ast_stream_rewind(others
[x
], 200);
957 ast_truncstream(others
[x
]);
958 /* add the original file too */
959 while ((fr
= ast_readframe(realfiles
[x
]))) {
960 ast_writestream(others
[x
],fr
);
962 ast_closestream(others
[x
]);
963 ast_closestream(realfiles
[x
]);
964 ast_filerename(prependfile
, recordfile
, sfmt
[x
]);
966 ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt
[x
],prependfile
,recordfile
);
968 ast_filedelete(prependfile
, sfmt
[x
]);
971 if (rfmt
&& ast_set_read_format(chan
, rfmt
)) {
972 ast_log(LOG_WARNING
, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt
), chan
->name
);
975 /* Let them know it worked */
976 ast_stream_and_wait(chan
, "auth-thankyou", chan
->language
, "");
981 /* Channel group core functions */
983 int ast_app_group_split_group(const char *data
, char *group
, int group_max
, char *category
, int category_max
)
987 char *grp
=NULL
, *cat
=NULL
;
989 if (!ast_strlen_zero(data
)) {
990 ast_copy_string(tmp
, data
, sizeof(tmp
));
992 cat
= strchr(tmp
, '@');
999 if (!ast_strlen_zero(grp
))
1000 ast_copy_string(group
, grp
, group_max
);
1005 snprintf(category
, category_max
, "%s_%s", GROUP_CATEGORY_PREFIX
, cat
);
1007 ast_copy_string(category
, GROUP_CATEGORY_PREFIX
, category_max
);
1012 int ast_app_group_set_channel(struct ast_channel
*chan
, const char *data
)
1015 char group
[80] = "";
1016 char category
[80] = "";
1018 if (!ast_app_group_split_group(data
, group
, sizeof(group
), category
, sizeof(category
))) {
1019 pbx_builtin_setvar_helper(chan
, category
, group
);
1026 int ast_app_group_get_count(const char *group
, const char *category
)
1028 struct ast_channel
*chan
;
1034 if (ast_strlen_zero(group
))
1037 s
= S_OR(category
, GROUP_CATEGORY_PREFIX
);
1038 ast_copy_string(cat
, s
, sizeof(cat
));
1041 while ((chan
= ast_channel_walk_locked(chan
)) != NULL
) {
1042 test
= pbx_builtin_getvar_helper(chan
, cat
);
1043 if (test
&& !strcasecmp(test
, group
))
1045 ast_channel_unlock(chan
);
1051 int ast_app_group_match_get_count(const char *groupmatch
, const char *category
)
1054 struct ast_channel
*chan
;
1060 if (ast_strlen_zero(groupmatch
))
1063 /* if regex compilation fails, return zero matches */
1064 if (regcomp(®exbuf
, groupmatch
, REG_EXTENDED
| REG_NOSUB
))
1067 s
= S_OR(category
, GROUP_CATEGORY_PREFIX
);
1068 ast_copy_string(cat
, s
, sizeof(cat
));
1071 while ((chan
= ast_channel_walk_locked(chan
)) != NULL
) {
1072 test
= pbx_builtin_getvar_helper(chan
, cat
);
1073 if (test
&& !regexec(®exbuf
, test
, 0, NULL
, 0))
1075 ast_channel_unlock(chan
);
1083 unsigned int ast_app_separate_args(char *buf
, char delim
, char **array
, int arraylen
)
1087 int paren
= 0, quote
= 0;
1089 if (!buf
|| !array
|| !arraylen
)
1092 memset(array
, 0, arraylen
* sizeof(*array
));
1096 for (argc
= 0; *scan
&& (argc
< arraylen
- 1); argc
++) {
1098 for (; *scan
; scan
++) {
1101 else if (*scan
== ')') {
1104 } else if (*scan
== '"') {
1105 quote
= quote
? 0 : 1;
1106 /* Remove quote character from argument */
1107 memmove(scan
, scan
+ 1, strlen(scan
));
1109 } else if (*scan
== '\\') {
1110 /* Literal character, don't parse */
1111 memmove(scan
, scan
+ 1, strlen(scan
));
1112 } else if ((*scan
== delim
) && !paren
&& !quote
) {
1120 array
[argc
++] = scan
;
1125 enum AST_LOCK_RESULT
ast_lock_path(const char *path
)
1131 int lp
= strlen(path
);
1134 if (!(s
= alloca(lp
+ 10)) || !(fs
= alloca(lp
+ 20))) {
1135 ast_log(LOG_WARNING
, "Out of memory!\n");
1136 return AST_LOCK_FAILURE
;
1139 snprintf(fs
, strlen(path
) + 19, "%s/.lock-%08lx", path
, ast_random());
1140 fd
= open(fs
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
1142 fprintf(stderr
, "Unable to create lock file '%s': %s\n", path
, strerror(errno
));
1143 return AST_LOCK_PATH_NOT_FOUND
;
1147 snprintf(s
, strlen(path
) + 9, "%s/.lock", path
);
1149 while (((res
= link(fs
, s
)) < 0) && (errno
== EEXIST
) && (time(NULL
) - start
< 5))
1152 ast_log(LOG_WARNING
, "Failed to lock path '%s': %s\n", path
, strerror(errno
));
1153 return AST_LOCK_TIMEOUT
;
1156 ast_log(LOG_DEBUG
, "Locked path '%s'\n", path
);
1157 return AST_LOCK_SUCCESS
;
1161 int ast_unlock_path(const char *path
)
1164 if (!(s
= alloca(strlen(path
) + 10)))
1166 snprintf(s
, strlen(path
) + 9, "%s/%s", path
, ".lock");
1167 ast_log(LOG_DEBUG
, "Unlocked path '%s'\n", path
);
1171 int ast_record_review(struct ast_channel
*chan
, const char *playfile
, const char *recordfile
, int maxtime
, const char *fmt
, int *duration
, const char *path
)
1173 int silencethreshold
= 128;
1177 int max_attempts
= 3;
1180 int message_exists
= 0;
1181 /* Note that urgent and private are for flagging messages as such in the future */
1183 /* barf if no pointer passed to store duration in */
1184 if (duration
== NULL
) {
1185 ast_log(LOG_WARNING
, "Error ast_record_review called without duration pointer\n");
1189 cmd
= '3'; /* Want to start by recording */
1191 while ((cmd
>= 0) && (cmd
!= 't')) {
1194 if (!message_exists
) {
1195 /* In this case, 1 is to record a message */
1199 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
1205 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the recording\n");
1206 cmd
= ast_stream_and_wait(chan
, recordfile
, chan
->language
, AST_DIGIT_ANY
);
1212 ast_verbose(VERBOSE_PREFIX_3
"Re-recording\n");
1214 ast_verbose(VERBOSE_PREFIX_3
"Recording\n");
1216 cmd
= ast_play_and_record(chan
, playfile
, recordfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, path
);
1218 /* User has hung up, no options to give */
1223 } else if (cmd
== '*') {
1227 /* If all is well, a message exists */
1240 cmd
= ast_play_and_wait(chan
, "vm-sorry");
1243 if (message_exists
) {
1244 cmd
= ast_play_and_wait(chan
, "vm-review");
1247 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
1249 cmd
= ast_waitfordigit(chan
, 600);
1253 cmd
= ast_waitfordigit(chan
, 6000);
1257 if (attempts
> max_attempts
) {
1267 #define RES_UPONE (1 << 16)
1268 #define RES_EXIT (1 << 17)
1269 #define RES_REPEAT (1 << 18)
1270 #define RES_RESTART ((1 << 19) | RES_REPEAT)
1272 static int ast_ivr_menu_run_internal(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
);
1273 static int ivr_dispatch(struct ast_channel
*chan
, struct ast_ivr_option
*option
, char *exten
, void *cbdata
)
1276 int (*ivr_func
)(struct ast_channel
*, void *);
1280 switch(option
->action
) {
1281 case AST_ACTION_UPONE
:
1283 case AST_ACTION_EXIT
:
1284 return RES_EXIT
| (((unsigned long)(option
->adata
)) & 0xffff);
1285 case AST_ACTION_REPEAT
:
1286 return RES_REPEAT
| (((unsigned long)(option
->adata
)) & 0xffff);
1287 case AST_ACTION_RESTART
:
1288 return RES_RESTART
;
1289 case AST_ACTION_NOOP
:
1291 case AST_ACTION_BACKGROUND
:
1292 res
= ast_stream_and_wait(chan
, (char *)option
->adata
, chan
->language
, AST_DIGIT_ANY
);
1294 ast_log(LOG_NOTICE
, "Unable to find file '%s'!\n", (char *)option
->adata
);
1298 case AST_ACTION_PLAYBACK
:
1299 res
= ast_stream_and_wait(chan
, (char *)option
->adata
, chan
->language
, "");
1301 ast_log(LOG_NOTICE
, "Unable to find file '%s'!\n", (char *)option
->adata
);
1305 case AST_ACTION_MENU
:
1306 res
= ast_ivr_menu_run_internal(chan
, (struct ast_ivr_menu
*)option
->adata
, cbdata
);
1307 /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
1311 case AST_ACTION_WAITOPTION
:
1312 res
= ast_waitfordigit(chan
, 1000 * (chan
->pbx
? chan
->pbx
->rtimeout
: 10));
1316 case AST_ACTION_CALLBACK
:
1317 ivr_func
= option
->adata
;
1318 res
= ivr_func(chan
, cbdata
);
1320 case AST_ACTION_TRANSFER
:
1321 res
= ast_parseable_goto(chan
, option
->adata
);
1323 case AST_ACTION_PLAYLIST
:
1324 case AST_ACTION_BACKLIST
:
1326 c
= ast_strdupa(option
->adata
);
1328 while((n
= strsep(&c
, ";")))
1329 if ((res
= ast_stream_and_wait(chan
, n
, chan
->language
,
1330 (option
->action
== AST_ACTION_BACKLIST
) ? AST_DIGIT_ANY
: "")))
1332 ast_stopstream(chan
);
1336 ast_log(LOG_NOTICE
, "Unknown dispatch function %d, ignoring!\n", option
->action
);
1342 static int option_exists(struct ast_ivr_menu
*menu
, char *option
)
1345 for (x
=0;menu
->options
[x
].option
;x
++)
1346 if (!strcasecmp(menu
->options
[x
].option
, option
))
1351 static int option_matchmore(struct ast_ivr_menu
*menu
, char *option
)
1354 for (x
=0;menu
->options
[x
].option
;x
++)
1355 if ((!strncasecmp(menu
->options
[x
].option
, option
, strlen(option
))) &&
1356 (menu
->options
[x
].option
[strlen(option
)]))
1361 static int read_newoption(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, char *exten
, int maxexten
)
1365 while(option_matchmore(menu
, exten
)) {
1366 ms
= chan
->pbx
? chan
->pbx
->dtimeout
: 5000;
1367 if (strlen(exten
) >= maxexten
- 1)
1369 res
= ast_waitfordigit(chan
, ms
);
1372 exten
[strlen(exten
) + 1] = '\0';
1373 exten
[strlen(exten
)] = res
;
1375 return res
> 0 ? 0 : res
;
1378 static int ast_ivr_menu_run_internal(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
)
1380 /* Execute an IVR menu structure */
1384 char exten
[AST_MAX_EXTENSION
] = "s";
1385 if (option_exists(menu
, "s") < 0) {
1387 if (option_exists(menu
, "g") < 0) {
1388 ast_log(LOG_WARNING
, "No 's' nor 'g' extension in menu '%s'!\n", menu
->title
);
1393 while(menu
->options
[pos
].option
) {
1394 if (!strcasecmp(menu
->options
[pos
].option
, exten
)) {
1395 res
= ivr_dispatch(chan
, menu
->options
+ pos
, exten
, cbdata
);
1396 ast_log(LOG_DEBUG
, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten
, pos
, res
);
1399 else if (res
& RES_UPONE
)
1401 else if (res
& RES_EXIT
)
1403 else if (res
& RES_REPEAT
) {
1404 int maxretries
= res
& 0xffff;
1405 if ((res
& RES_RESTART
) == RES_RESTART
) {
1411 if ((maxretries
> 0) && (retries
>= maxretries
)) {
1412 ast_log(LOG_DEBUG
, "Max retries %d exceeded\n", maxretries
);
1415 if (option_exists(menu
, "g") > -1)
1417 else if (option_exists(menu
, "s") > -1)
1422 } else if (res
&& strchr(AST_DIGIT_ANY
, res
)) {
1423 ast_log(LOG_DEBUG
, "Got start of extension, %c\n", res
);
1426 if ((res
= read_newoption(chan
, menu
, exten
, sizeof(exten
))))
1428 if (option_exists(menu
, exten
) < 0) {
1429 if (option_exists(menu
, "i")) {
1430 ast_log(LOG_DEBUG
, "Invalid extension entered, going to 'i'!\n");
1435 ast_log(LOG_DEBUG
, "Aborting on invalid entry, with no 'i' option!\n");
1440 ast_log(LOG_DEBUG
, "New existing extension: %s\n", exten
);
1448 ast_log(LOG_DEBUG
, "Stopping option '%s', res is %d\n", exten
, res
);
1450 if (!strcasecmp(exten
, "s"))
1458 int ast_ivr_menu_run(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
)
1461 res
= ast_ivr_menu_run_internal(chan
, menu
, cbdata
);
1462 /* Hide internal coding */
1468 char *ast_read_textfile(const char *filename
)
1472 struct stat filesize
;
1475 if(stat(filename
,&filesize
)== -1){
1476 ast_log(LOG_WARNING
,"Error can't stat %s\n", filename
);
1479 count
=filesize
.st_size
+ 1;
1480 fd
= open(filename
, O_RDONLY
);
1482 ast_log(LOG_WARNING
, "Cannot open file '%s' for reading: %s\n", filename
, strerror(errno
));
1485 if ((output
= ast_malloc(count
))) {
1486 res
= read(fd
, output
, count
- 1);
1487 if (res
== count
- 1) {
1490 ast_log(LOG_WARNING
, "Short read of %s (%d of %d): %s\n", filename
, res
, count
- 1, strerror(errno
));
1499 int ast_app_parse_options(const struct ast_app_option
*options
, struct ast_flags
*flags
, char **args
, char *optstr
)
1503 unsigned int argloc
;
1507 ast_clear_flag(flags
, AST_FLAGS_ALL
);
1514 curarg
= *s
++ & 0x7f; /* the array (in app.h) has 128 entries */
1515 ast_set_flag(flags
, options
[curarg
].flag
);
1516 argloc
= options
[curarg
].arg_index
;
1523 args
[argloc
- 1] = arg
;
1526 ast_log(LOG_WARNING
, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg
, arg
);
1529 } else if (argloc
) {
1530 args
[argloc
- 1] = NULL
;