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>
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
38 #include <sys/types.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"
52 #include "asterisk/linkedlists.h"
54 #define MAX_OTHER_FORMATS 10
56 static AST_LIST_HEAD_STATIC(groups
, ast_group_info
);
59 This function presents a dialtone and reads an extension into 'collect'
60 which must be a pointer to a **pre-initialized** array of char having a
61 size of 'size' suitable for writing to. It will collect no more than the smaller
62 of 'maxlen' or 'size' minus the original strlen() of collect digits.
63 \return 0 if extension does not exist, 1 if extension exists
65 int ast_app_dtget(struct ast_channel
*chan
, const char *context
, char *collect
, size_t size
, int maxlen
, int timeout
)
67 struct ind_tone_zone_sound
*ts
;
73 if (!timeout
&& chan
->pbx
)
74 timeout
= chan
->pbx
->dtimeout
;
78 ts
= ast_get_indication_tone(chan
->zone
,"dial");
79 if (ts
&& ts
->data
[0])
80 res
= ast_playtones_start(chan
, 0, ts
->data
, 0);
82 ast_log(LOG_NOTICE
,"Huh....? no dial for indications?\n");
84 for (x
= strlen(collect
); x
< maxlen
; ) {
85 res
= ast_waitfordigit(chan
, timeout
);
86 if (!ast_ignore_pattern(context
, collect
))
87 ast_playtones_stop(chan
);
93 if (!ast_matchmore_extension(chan
, context
, collect
, 1, chan
->cid
.cid_num
))
97 res
= ast_exists_extension(chan
, context
, collect
, 1, chan
->cid
.cid_num
) ? 1 : 0;
101 /*! \param c The channel to read from
102 * \param prompt The file to stream to the channel
103 * \param s The string to read in to. Must be at least the size of your length
104 * \param maxlen How many digits to read (maximum)
105 * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
106 * "ludicrous time" (essentially never times out) */
107 int ast_app_getdata(struct ast_channel
*c
, char *prompt
, char *s
, int maxlen
, int timeout
)
110 /* XXX Merge with full version? XXX */
114 res
= ast_streamfile(c
, prompt
, c
->language
);
118 fto
= c
->pbx
? c
->pbx
->rtimeout
* 1000 : 6000;
119 to
= c
->pbx
? c
->pbx
->dtimeout
* 1000 : 2000;
124 fto
= to
= 1000000000;
125 res
= ast_readstring(c
, s
, maxlen
, to
, fto
, "#");
130 int ast_app_getdata_full(struct ast_channel
*c
, char *prompt
, char *s
, int maxlen
, int timeout
, int audiofd
, int ctrlfd
)
134 res
= ast_streamfile(c
, prompt
, c
->language
);
143 fto
= to
= 1000000000;
144 res
= ast_readstring_full(c
, s
, maxlen
, to
, fto
, "#", audiofd
, ctrlfd
);
148 static int (*ast_has_voicemail_func
)(const char *mailbox
, const char *folder
) = NULL
;
149 static int (*ast_inboxcount_func
)(const char *mailbox
, int *newmsgs
, int *oldmsgs
) = NULL
;
150 static int (*ast_messagecount_func
)(const char *context
, const char *mailbox
, const char *folder
) = NULL
;
152 void ast_install_vm_functions(int (*has_voicemail_func
)(const char *mailbox
, const char *folder
),
153 int (*inboxcount_func
)(const char *mailbox
, int *newmsgs
, int *oldmsgs
),
154 int (*messagecount_func
)(const char *context
, const char *mailbox
, const char *folder
))
156 ast_has_voicemail_func
= has_voicemail_func
;
157 ast_inboxcount_func
= inboxcount_func
;
158 ast_messagecount_func
= messagecount_func
;
161 void ast_uninstall_vm_functions(void)
163 ast_has_voicemail_func
= NULL
;
164 ast_inboxcount_func
= NULL
;
165 ast_messagecount_func
= NULL
;
168 int ast_app_has_voicemail(const char *mailbox
, const char *folder
)
170 static int warned
= 0;
171 if (ast_has_voicemail_func
)
172 return ast_has_voicemail_func(mailbox
, folder
);
174 if ((option_verbose
> 2) && !warned
) {
175 ast_verbose(VERBOSE_PREFIX_3
"Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox
, folder
? folder
: "INBOX");
182 int ast_app_inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
184 static int warned
= 0;
189 if (ast_inboxcount_func
)
190 return ast_inboxcount_func(mailbox
, newmsgs
, oldmsgs
);
192 if (!warned
&& (option_verbose
> 2)) {
194 ast_verbose(VERBOSE_PREFIX_3
"Message count requested for mailbox %s but voicemail not loaded.\n", mailbox
);
200 int ast_app_messagecount(const char *context
, const char *mailbox
, const char *folder
)
202 static int warned
= 0;
203 if (ast_messagecount_func
)
204 return ast_messagecount_func(context
, mailbox
, folder
);
206 if (!warned
&& (option_verbose
> 2)) {
208 ast_verbose(VERBOSE_PREFIX_3
"Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox
, context
, folder
);
214 int ast_dtmf_stream(struct ast_channel
*chan
, struct ast_channel
*peer
, const char *digits
, int between
)
218 struct ast_silence_generator
*silgen
= NULL
;
224 res
= ast_autoservice_start(peer
);
227 res
= ast_waitfor(chan
, 100);
229 /* ast_waitfor will return the number of remaining ms on success */
232 ast_autoservice_stop(peer
);
237 if (ast_opt_transmit_silence
) {
238 silgen
= ast_channel_start_silence_generator(chan
);
241 for (ptr
= digits
; *ptr
; ptr
++) {
243 /* 'w' -- wait half a second */
244 if ((res
= ast_safe_sleep(chan
, 500)))
246 } else if (strchr("0123456789*#abcdfABCDF", *ptr
)) {
247 /* Character represents valid DTMF */
248 if (*ptr
== 'f' || *ptr
== 'F') {
249 /* ignore return values if not supported by channel */
250 ast_indicate(chan
, AST_CONTROL_FLASH
);
252 ast_senddigit(chan
, *ptr
);
253 /* pause between digits */
254 if ((res
= ast_safe_sleep(chan
, between
)))
257 ast_log(LOG_WARNING
, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr
);
261 /* Stop autoservice on the peer channel, but don't overwrite any error condition
262 that has occurred previously while acting on the primary channel */
263 if (ast_autoservice_stop(peer
) && !res
)
268 ast_channel_stop_silence_generator(chan
, silgen
);
274 struct linear_state
{
281 static void linear_release(struct ast_channel
*chan
, void *params
)
283 struct linear_state
*ls
= params
;
284 if (ls
->origwfmt
&& ast_set_write_format(chan
, ls
->origwfmt
)) {
285 ast_log(LOG_WARNING
, "Unable to restore channel '%s' to format '%d'\n", chan
->name
, ls
->origwfmt
);
292 static int linear_generator(struct ast_channel
*chan
, void *data
, int len
, int samples
)
295 short buf
[2048 + AST_FRIENDLY_OFFSET
/ 2];
296 struct linear_state
*ls
= data
;
299 if (len
> sizeof(buf
) - AST_FRIENDLY_OFFSET
) {
300 ast_log(LOG_WARNING
, "Can't generate %d bytes of data!\n" ,len
);
301 len
= sizeof(buf
) - AST_FRIENDLY_OFFSET
;
303 memset(&f
, 0, sizeof(f
));
304 res
= read(ls
->fd
, buf
+ AST_FRIENDLY_OFFSET
/2, len
);
306 f
.frametype
= AST_FRAME_VOICE
;
307 f
.subclass
= AST_FORMAT_SLINEAR
;
308 f
.data
= buf
+ AST_FRIENDLY_OFFSET
/2;
311 f
.offset
= AST_FRIENDLY_OFFSET
;
319 static void *linear_alloc(struct ast_channel
*chan
, void *params
)
321 struct linear_state
*ls
;
322 /* In this case, params is already malloc'd */
325 if (ls
->allowoverride
)
326 ast_set_flag(chan
, AST_FLAG_WRITE_INT
);
328 ast_clear_flag(chan
, AST_FLAG_WRITE_INT
);
329 ls
->origwfmt
= chan
->writeformat
;
330 if (ast_set_write_format(chan
, AST_FORMAT_SLINEAR
)) {
331 ast_log(LOG_WARNING
, "Unable to set '%s' to linear format (write)\n", chan
->name
);
339 static struct ast_generator linearstream
=
342 release
: linear_release
,
343 generate
: linear_generator
,
346 int ast_linear_stream(struct ast_channel
*chan
, const char *filename
, int fd
, int allowoverride
)
348 struct linear_state
*lin
;
353 if (ast_strlen_zero(filename
))
356 if (filename
[0] == '/')
357 ast_copy_string(tmpf
, filename
, sizeof(tmpf
));
359 snprintf(tmpf
, sizeof(tmpf
), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR
, "sounds", filename
);
360 fd
= open(tmpf
, O_RDONLY
);
362 ast_log(LOG_WARNING
, "Unable to open file '%s': %s\n", tmpf
, strerror(errno
));
366 if ((lin
= ast_calloc(1, sizeof(*lin
)))) {
368 lin
->allowoverride
= allowoverride
;
369 lin
->autoclose
= autoclose
;
370 res
= ast_activate_generator(chan
, &linearstream
, lin
);
375 int ast_control_streamfile(struct ast_channel
*chan
, const char *file
,
376 const char *fwd
, const char *rev
,
377 const char *stop
, const char *pause
,
378 const char *restart
, int skipms
)
384 long pause_restart_point
= 0;
387 blen
+= strlen(stop
);
389 blen
+= strlen(pause
);
391 blen
+= strlen(restart
);
394 breaks
= alloca(blen
+ 1);
397 strcat(breaks
, stop
);
399 strcat(breaks
, pause
);
401 strcat(breaks
, restart
);
403 if (chan
->_state
!= AST_STATE_UP
)
404 res
= ast_answer(chan
);
407 if ((end
= strchr(file
,':'))) {
408 if (!strcasecmp(end
, ":end")) {
416 ast_stopstream(chan
);
417 res
= ast_streamfile(chan
, file
, chan
->language
);
419 if (pause_restart_point
) {
420 ast_seekstream(chan
->stream
, pause_restart_point
, SEEK_SET
);
421 pause_restart_point
= 0;
424 ast_seekstream(chan
->stream
, 0, SEEK_END
);
427 res
= ast_waitstream_fr(chan
, breaks
, fwd
, rev
, skipms
);
433 /* We go at next loop if we got the restart char */
434 if (restart
&& strchr(restart
, res
)) {
436 ast_log(LOG_DEBUG
, "we'll restart the stream here at next loop\n");
437 pause_restart_point
= 0;
441 if (pause
&& strchr(pause
, res
)) {
442 pause_restart_point
= ast_tellstream(chan
->stream
);
444 ast_stopstream(chan
);
445 res
= ast_waitfordigit(chan
, 1000);
448 else if (res
== -1 || strchr(pause
, res
) || (stop
&& strchr(stop
, res
)))
460 /* if we get one of our stop chars, return it to the calling function */
461 if (stop
&& strchr(stop
, res
))
465 /* If we are returning a digit cast it as char */
466 if (res
> 0 || chan
->stream
)
469 ast_stopstream(chan
);
474 int ast_play_and_wait(struct ast_channel
*chan
, const char *fn
)
477 d
= ast_streamfile(chan
, fn
, chan
->language
);
480 d
= ast_waitstream(chan
, AST_DIGIT_ANY
);
481 ast_stopstream(chan
);
485 static int global_silence_threshold
= 128;
486 static int global_maxsilence
= 0;
488 /*! Optionally play a sound file or a beep, then record audio and video from the channel.
489 * @param chan Channel to playback to/record from.
490 * @param playfile Filename of sound to play before recording begins.
491 * @param recordfile Filename to record to.
492 * @param maxtime Maximum length of recording (in milliseconds).
493 * @param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
494 * @param duration Where to store actual length of the recorded message (in milliseconds).
495 * @param beep Whether to play a beep before starting to record.
496 * @param silencethreshold
497 * @param maxsilence Length of silence that will end a recording (in milliseconds).
498 * @param path Optional filesystem path to unlock.
499 * @param prepend If true, prepend the recorded audio to an existing file.
500 * @param acceptdtmf DTMF digits that will end the recording.
501 * @param canceldtmf DTMF digits that will cancel the recording.
504 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
)
509 int x
, fmtcnt
= 1, res
= -1, outmsg
= 0;
510 struct ast_filestream
*others
[MAX_OTHER_FORMATS
];
511 char *sfmt
[MAX_OTHER_FORMATS
];
512 char *stringp
= NULL
;
514 struct ast_dsp
*sildet
= NULL
; /* silence detector dsp */
515 int totalsilence
= 0;
517 struct ast_silence_generator
*silgen
= NULL
;
518 char prependfile
[80];
520 if (silencethreshold
< 0)
521 silencethreshold
= global_silence_threshold
;
524 maxsilence
= global_maxsilence
;
526 /* barf if no pointer passed to store duration in */
527 if (duration
== NULL
) {
528 ast_log(LOG_WARNING
, "Error play_and_record called without duration pointer\n");
533 ast_log(LOG_DEBUG
,"play_and_record: %s, %s, '%s'\n", playfile
? playfile
: "<None>", recordfile
, fmt
);
534 snprintf(comment
, sizeof(comment
), "Playing %s, Recording to: %s on %s\n", playfile
? playfile
: "<None>", recordfile
, chan
->name
);
536 if (playfile
|| beep
) {
538 d
= ast_play_and_wait(chan
, playfile
);
540 d
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
546 ast_copy_string(prependfile
, recordfile
, sizeof(prependfile
));
547 strncat(prependfile
, "-prepend", sizeof(prependfile
) - strlen(prependfile
) - 1);
550 fmts
= ast_strdupa(fmt
);
553 strsep(&stringp
, "|");
555 ast_log(LOG_DEBUG
, "Recording Formats: sfmts=%s\n", fmts
);
556 sfmt
[0] = ast_strdupa(fmts
);
558 while ((fmt
= strsep(&stringp
, "|"))) {
559 if (fmtcnt
> MAX_OTHER_FORMATS
- 1) {
560 ast_log(LOG_WARNING
, "Please increase MAX_OTHER_FORMATS in app.c\n");
563 sfmt
[fmtcnt
++] = ast_strdupa(fmt
);
566 end
= start
= time(NULL
); /* pre-initialize end to be same as start in case we never get into loop */
567 for (x
= 0; x
< fmtcnt
; x
++) {
568 others
[x
] = ast_writefile(prepend
? prependfile
: recordfile
, sfmt
[x
], comment
, O_TRUNC
, 0, 0777);
569 if (option_verbose
> 2)
570 ast_verbose(VERBOSE_PREFIX_3
"x=%d, open writing: %s format: %s, %p\n", x
, prepend
? prependfile
: recordfile
, sfmt
[x
], others
[x
]);
577 ast_unlock_path(path
);
579 if (maxsilence
> 0) {
580 sildet
= ast_dsp_new(); /* Create the silence detector */
582 ast_log(LOG_WARNING
, "Unable to create silence detector :(\n");
585 ast_dsp_set_threshold(sildet
, silencethreshold
);
586 rfmt
= chan
->readformat
;
587 res
= ast_set_read_format(chan
, AST_FORMAT_SLINEAR
);
589 ast_log(LOG_WARNING
, "Unable to set to linear mode, giving up\n");
590 ast_dsp_free(sildet
);
596 /* Request a video update */
597 ast_indicate(chan
, AST_CONTROL_VIDUPDATE
);
599 if (ast_opt_transmit_silence
)
600 silgen
= ast_channel_start_silence_generator(chan
);
604 /* Loop forever, writing the packets we read to the writer(s), until
605 we read a digit or get a hangup */
608 res
= ast_waitfor(chan
, 2000);
611 ast_log(LOG_DEBUG
, "One waitfor failed, trying another\n");
612 /* Try one more time in case of masq */
613 res
= ast_waitfor(chan
, 2000);
615 ast_log(LOG_WARNING
, "No audio available on %s??\n", chan
->name
);
627 if (f
->frametype
== AST_FRAME_VOICE
) {
628 /* write each format */
629 for (x
= 0; x
< fmtcnt
; x
++) {
630 if (prepend
&& !others
[x
])
632 res
= ast_writestream(others
[x
], f
);
635 /* Silence Detection */
636 if (maxsilence
> 0) {
638 ast_dsp_silence(sildet
, f
, &dspsilence
);
640 totalsilence
= dspsilence
;
644 if (totalsilence
> maxsilence
) {
645 /* Ended happily with silence */
646 if (option_verbose
> 2)
647 ast_verbose( VERBOSE_PREFIX_3
"Recording automatically stopped after a silence of %d seconds\n", totalsilence
/1000);
653 /* Exit on any error */
655 ast_log(LOG_WARNING
, "Error writing frame\n");
658 } else if (f
->frametype
== AST_FRAME_VIDEO
) {
659 /* Write only once */
660 ast_writestream(others
[0], f
);
661 } else if (f
->frametype
== AST_FRAME_DTMF
) {
663 /* stop recording with any digit */
664 if (option_verbose
> 2)
665 ast_verbose(VERBOSE_PREFIX_3
"User ended message by pressing %c\n", f
->subclass
);
670 if (strchr(acceptdtmf
, f
->subclass
)) {
671 if (option_verbose
> 2)
672 ast_verbose(VERBOSE_PREFIX_3
"User ended message by pressing %c\n", f
->subclass
);
677 if (strchr(canceldtmf
, f
->subclass
)) {
678 if (option_verbose
> 2)
679 ast_verbose(VERBOSE_PREFIX_3
"User cancelled message by pressing %c\n", f
->subclass
);
687 if (maxtime
< (end
- start
)) {
688 if (option_verbose
> 2)
689 ast_verbose(VERBOSE_PREFIX_3
"Took too long, cutting it short...\n");
698 if (option_verbose
> 2)
699 ast_verbose(VERBOSE_PREFIX_3
"User hung up\n");
706 ast_log(LOG_WARNING
, "Error creating writestream '%s', format '%s'\n", recordfile
, sfmt
[x
]);
711 ast_channel_stop_silence_generator(chan
, silgen
);
715 * Instead of asking how much time passed (end - start), calculate the number
716 * of seconds of audio which actually went into the file. This fixes a
717 * problem where audio is stopped up on the network and never gets to us.
719 * Note that we still want to use the number of seconds passed for the max
720 * message, otherwise we could get a situation where this stream is never
721 * closed (which would create a resource leak).
723 *duration
= others
[0] ? ast_tellstream(others
[0]) / 8000 : 0;
726 for (x
= 0; x
< fmtcnt
; x
++) {
730 * If we ended with silence, trim all but the first 200ms of silence
731 * off the recording. However, if we ended with '#', we don't want
732 * to trim ANY part of the recording.
734 if (res
> 0 && totalsilence
)
735 ast_stream_rewind(others
[x
], totalsilence
- 200);
736 ast_truncstream(others
[x
]);
737 ast_closestream(others
[x
]);
741 if (prepend
&& outmsg
) {
742 struct ast_filestream
*realfiles
[MAX_OTHER_FORMATS
];
743 struct ast_frame
*fr
;
745 for (x
= 0; x
< fmtcnt
; x
++) {
746 snprintf(comment
, sizeof(comment
), "Opening the real file %s.%s\n", recordfile
, sfmt
[x
]);
747 realfiles
[x
] = ast_readfile(recordfile
, sfmt
[x
], comment
, O_RDONLY
, 0, 0);
748 if (!others
[x
] || !realfiles
[x
])
750 /*!\note Same logic as above. */
752 ast_stream_rewind(others
[x
], totalsilence
- 200);
753 ast_truncstream(others
[x
]);
754 /* add the original file too */
755 while ((fr
= ast_readframe(realfiles
[x
]))) {
756 ast_writestream(others
[x
], fr
);
759 ast_closestream(others
[x
]);
760 ast_closestream(realfiles
[x
]);
761 ast_filerename(prependfile
, recordfile
, sfmt
[x
]);
762 if (option_verbose
> 3)
763 ast_verbose(VERBOSE_PREFIX_4
"Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt
[x
], prependfile
, recordfile
);
764 ast_filedelete(prependfile
, sfmt
[x
]);
767 if (rfmt
&& ast_set_read_format(chan
, rfmt
)) {
768 ast_log(LOG_WARNING
, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt
), chan
->name
);
771 ast_stream_and_wait(chan
, "auth-thankyou", chan
->language
, "");
774 ast_dsp_free(sildet
);
778 static char default_acceptdtmf
[] = "#";
779 static char default_canceldtmf
[] = "";
781 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
)
783 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
));
786 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
)
788 return __ast_play_and_record(chan
, playfile
, recordfile
, maxtime
, fmt
, duration
, 0, silencethreshold
, maxsilence
, path
, 0, default_acceptdtmf
, default_canceldtmf
);
791 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
)
793 return __ast_play_and_record(chan
, playfile
, recordfile
, maxtime
, fmt
, duration
, beep
, silencethreshold
, maxsilence
, NULL
, 1, default_acceptdtmf
, default_canceldtmf
);
796 /* Channel group core functions */
798 int ast_app_group_split_group(const char *data
, char *group
, int group_max
, char *category
, int category_max
)
802 char *grp
=NULL
, *cat
=NULL
;
804 if (!ast_strlen_zero(data
)) {
805 ast_copy_string(tmp
, data
, sizeof(tmp
));
807 cat
= strchr(tmp
, '@');
814 if (!ast_strlen_zero(grp
))
815 ast_copy_string(group
, grp
, group_max
);
819 if (!ast_strlen_zero(cat
))
820 ast_copy_string(category
, cat
, category_max
);
825 int ast_app_group_set_channel(struct ast_channel
*chan
, const char *data
)
828 char group
[80] = "", category
[80] = "";
829 struct ast_group_info
*gi
= NULL
;
832 if (ast_app_group_split_group(data
, group
, sizeof(group
), category
, sizeof(category
)))
835 /* Calculate memory we will need if this is new */
836 len
= sizeof(*gi
) + strlen(group
) + 1;
837 if (!ast_strlen_zero(category
))
838 len
+= strlen(category
) + 1;
840 AST_LIST_LOCK(&groups
);
841 AST_LIST_TRAVERSE_SAFE_BEGIN(&groups
, gi
, list
) {
842 if ((gi
->chan
== chan
) && ((ast_strlen_zero(category
) && ast_strlen_zero(gi
->category
)) || (!ast_strlen_zero(gi
->category
) && !strcasecmp(gi
->category
, category
)))) {
843 AST_LIST_REMOVE_CURRENT(&groups
, list
);
848 AST_LIST_TRAVERSE_SAFE_END
850 if (ast_strlen_zero(group
)) {
851 /* Enable unsetting the group */
852 } else if ((gi
= calloc(1, len
))) {
854 gi
->group
= (char *) gi
+ sizeof(*gi
);
855 strcpy(gi
->group
, group
);
856 if (!ast_strlen_zero(category
)) {
857 gi
->category
= (char *) gi
+ sizeof(*gi
) + strlen(group
) + 1;
858 strcpy(gi
->category
, category
);
860 AST_LIST_INSERT_TAIL(&groups
, gi
, list
);
865 AST_LIST_UNLOCK(&groups
);
870 int ast_app_group_get_count(const char *group
, const char *category
)
872 struct ast_group_info
*gi
= NULL
;
875 if (ast_strlen_zero(group
))
878 AST_LIST_LOCK(&groups
);
879 AST_LIST_TRAVERSE(&groups
, gi
, list
) {
880 if (!strcasecmp(gi
->group
, group
) && (ast_strlen_zero(category
) || (!ast_strlen_zero(gi
->category
) && !strcasecmp(gi
->category
, category
))))
883 AST_LIST_UNLOCK(&groups
);
888 int ast_app_group_match_get_count(const char *groupmatch
, const char *category
)
890 struct ast_group_info
*gi
= NULL
;
894 if (ast_strlen_zero(groupmatch
))
897 /* if regex compilation fails, return zero matches */
898 if (regcomp(®exbuf
, groupmatch
, REG_EXTENDED
| REG_NOSUB
))
901 AST_LIST_LOCK(&groups
);
902 AST_LIST_TRAVERSE(&groups
, gi
, list
) {
903 if (!regexec(®exbuf
, gi
->group
, 0, NULL
, 0) && (ast_strlen_zero(category
) || (!ast_strlen_zero(gi
->category
) && !strcasecmp(gi
->category
, category
))))
906 AST_LIST_UNLOCK(&groups
);
913 int ast_app_group_update(struct ast_channel
*old
, struct ast_channel
*new)
915 struct ast_group_info
*gi
= NULL
;
917 AST_LIST_LOCK(&groups
);
918 AST_LIST_TRAVERSE(&groups
, gi
, list
) {
922 AST_LIST_UNLOCK(&groups
);
927 int ast_app_group_discard(struct ast_channel
*chan
)
929 struct ast_group_info
*gi
= NULL
;
931 AST_LIST_LOCK(&groups
);
932 AST_LIST_TRAVERSE_SAFE_BEGIN(&groups
, gi
, list
) {
933 if (gi
->chan
== chan
) {
934 AST_LIST_REMOVE_CURRENT(&groups
, list
);
938 AST_LIST_TRAVERSE_SAFE_END
939 AST_LIST_UNLOCK(&groups
);
944 int ast_app_group_list_lock(void)
946 return AST_LIST_LOCK(&groups
);
949 struct ast_group_info
*ast_app_group_list_head(void)
951 return AST_LIST_FIRST(&groups
);
954 int ast_app_group_list_unlock(void)
956 return AST_LIST_UNLOCK(&groups
);
959 unsigned int ast_app_separate_args(char *buf
, char delim
, char **array
, int arraylen
)
962 char *scan
, *wasdelim
= NULL
;
963 int paren
= 0, quote
= 0;
965 if (!buf
|| !array
|| !arraylen
)
968 memset(array
, 0, arraylen
* sizeof(*array
));
972 for (argc
= 0; *scan
&& (argc
< arraylen
- 1); argc
++) {
974 for (; *scan
; scan
++) {
977 else if (*scan
== ')') {
980 } else if (*scan
== '"' && delim
!= '"') {
981 quote
= quote
? 0 : 1;
982 /* Remove quote character from argument */
983 memmove(scan
, scan
+ 1, strlen(scan
));
985 } else if (*scan
== '\\') {
986 /* Literal character, don't parse */
987 memmove(scan
, scan
+ 1, strlen(scan
));
988 } else if ((*scan
== delim
) && !paren
&& !quote
) {
996 /* If the last character in the original string was the delimiter, then
997 * there is one additional argument. */
998 if (*scan
|| (scan
> buf
&& (scan
- 1) == wasdelim
)) {
999 array
[argc
++] = scan
;
1005 enum AST_LOCK_RESULT
ast_lock_path(const char *path
)
1011 int lp
= strlen(path
);
1014 if (!(s
= alloca(lp
+ 10)) || !(fs
= alloca(lp
+ 20))) {
1015 ast_log(LOG_WARNING
, "Out of memory!\n");
1016 return AST_LOCK_FAILURE
;
1019 snprintf(fs
, strlen(path
) + 19, "%s/.lock-%08lx", path
, ast_random());
1020 fd
= open(fs
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600);
1022 ast_log(LOG_ERROR
, "Unable to create lock file '%s': %s\n", path
, strerror(errno
));
1023 return AST_LOCK_PATH_NOT_FOUND
;
1027 snprintf(s
, strlen(path
) + 9, "%s/.lock", path
);
1029 while (((res
= link(fs
, s
)) < 0) && (errno
== EEXIST
) && (time(NULL
) - start
< 5))
1035 ast_log(LOG_WARNING
, "Failed to lock path '%s': %s\n", path
, strerror(errno
));
1036 return AST_LOCK_TIMEOUT
;
1039 ast_log(LOG_DEBUG
, "Locked path '%s'\n", path
);
1040 return AST_LOCK_SUCCESS
;
1044 int ast_unlock_path(const char *path
)
1049 if (!(s
= alloca(strlen(path
) + 10))) {
1050 ast_log(LOG_WARNING
, "Out of memory!\n");
1054 snprintf(s
, strlen(path
) + 9, "%s/%s", path
, ".lock");
1056 if ((res
= unlink(s
)))
1057 ast_log(LOG_ERROR
, "Could not unlock path '%s': %s\n", path
, strerror(errno
));
1060 ast_log(LOG_DEBUG
, "Unlocked path '%s'\n", path
);
1066 int ast_record_review(struct ast_channel
*chan
, const char *playfile
, const char *recordfile
, int maxtime
, const char *fmt
, int *duration
, const char *path
)
1068 int silencethreshold
= 128;
1072 int max_attempts
= 3;
1075 int message_exists
= 0;
1076 /* Note that urgent and private are for flagging messages as such in the future */
1078 /* barf if no pointer passed to store duration in */
1079 if (duration
== NULL
) {
1080 ast_log(LOG_WARNING
, "Error ast_record_review called without duration pointer\n");
1084 cmd
= '3'; /* Want to start by recording */
1086 while ((cmd
>= 0) && (cmd
!= 't')) {
1089 if (!message_exists
) {
1090 /* In this case, 1 is to record a message */
1094 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
1100 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the recording\n");
1101 cmd
= ast_stream_and_wait(chan
, recordfile
, chan
->language
, AST_DIGIT_ANY
);
1107 ast_verbose(VERBOSE_PREFIX_3
"Re-recording\n");
1109 ast_verbose(VERBOSE_PREFIX_3
"Recording\n");
1111 cmd
= ast_play_and_record(chan
, playfile
, recordfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, path
);
1113 /* User has hung up, no options to give */
1118 } else if (cmd
== '*') {
1122 /* If all is well, a message exists */
1135 cmd
= ast_play_and_wait(chan
, "vm-sorry");
1138 if (message_exists
) {
1139 cmd
= ast_play_and_wait(chan
, "vm-review");
1142 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
1144 cmd
= ast_waitfordigit(chan
, 600);
1148 cmd
= ast_waitfordigit(chan
, 6000);
1152 if (attempts
> max_attempts
) {
1162 #define RES_UPONE (1 << 16)
1163 #define RES_EXIT (1 << 17)
1164 #define RES_REPEAT (1 << 18)
1165 #define RES_RESTART ((1 << 19) | RES_REPEAT)
1167 static int ast_ivr_menu_run_internal(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
);
1169 static int ivr_dispatch(struct ast_channel
*chan
, struct ast_ivr_option
*option
, char *exten
, void *cbdata
)
1172 int (*ivr_func
)(struct ast_channel
*, void *);
1176 switch(option
->action
) {
1177 case AST_ACTION_UPONE
:
1179 case AST_ACTION_EXIT
:
1180 return RES_EXIT
| (((unsigned long)(option
->adata
)) & 0xffff);
1181 case AST_ACTION_REPEAT
:
1182 return RES_REPEAT
| (((unsigned long)(option
->adata
)) & 0xffff);
1183 case AST_ACTION_RESTART
:
1184 return RES_RESTART
;
1185 case AST_ACTION_NOOP
:
1187 case AST_ACTION_BACKGROUND
:
1188 res
= ast_stream_and_wait(chan
, (char *)option
->adata
, chan
->language
, AST_DIGIT_ANY
);
1190 ast_log(LOG_NOTICE
, "Unable to find file '%s'!\n", (char *)option
->adata
);
1194 case AST_ACTION_PLAYBACK
:
1195 res
= ast_stream_and_wait(chan
, (char *)option
->adata
, chan
->language
, "");
1197 ast_log(LOG_NOTICE
, "Unable to find file '%s'!\n", (char *)option
->adata
);
1201 case AST_ACTION_MENU
:
1202 res
= ast_ivr_menu_run_internal(chan
, (struct ast_ivr_menu
*)option
->adata
, cbdata
);
1203 /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
1207 case AST_ACTION_WAITOPTION
:
1208 res
= ast_waitfordigit(chan
, 1000 * (chan
->pbx
? chan
->pbx
->rtimeout
: 10));
1212 case AST_ACTION_CALLBACK
:
1213 ivr_func
= option
->adata
;
1214 res
= ivr_func(chan
, cbdata
);
1216 case AST_ACTION_TRANSFER
:
1217 res
= ast_parseable_goto(chan
, option
->adata
);
1219 case AST_ACTION_PLAYLIST
:
1220 case AST_ACTION_BACKLIST
:
1222 c
= ast_strdupa(option
->adata
);
1223 while ((n
= strsep(&c
, ";"))) {
1224 if ((res
= ast_stream_and_wait(chan
, n
, chan
->language
,
1225 (option
->action
== AST_ACTION_BACKLIST
) ? AST_DIGIT_ANY
: "")))
1228 ast_stopstream(chan
);
1231 ast_log(LOG_NOTICE
, "Unknown dispatch function %d, ignoring!\n", option
->action
);
1237 static int option_exists(struct ast_ivr_menu
*menu
, char *option
)
1240 for (x
= 0; menu
->options
[x
].option
; x
++)
1241 if (!strcasecmp(menu
->options
[x
].option
, option
))
1246 static int option_matchmore(struct ast_ivr_menu
*menu
, char *option
)
1249 for (x
= 0; menu
->options
[x
].option
; x
++)
1250 if ((!strncasecmp(menu
->options
[x
].option
, option
, strlen(option
))) &&
1251 (menu
->options
[x
].option
[strlen(option
)]))
1256 static int read_newoption(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, char *exten
, int maxexten
)
1260 while (option_matchmore(menu
, exten
)) {
1261 ms
= chan
->pbx
? chan
->pbx
->dtimeout
: 5000;
1262 if (strlen(exten
) >= maxexten
- 1)
1264 res
= ast_waitfordigit(chan
, ms
);
1267 exten
[strlen(exten
) + 1] = '\0';
1268 exten
[strlen(exten
)] = res
;
1270 return res
> 0 ? 0 : res
;
1273 static int ast_ivr_menu_run_internal(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
)
1275 /* Execute an IVR menu structure */
1279 char exten
[AST_MAX_EXTENSION
] = "s";
1280 if (option_exists(menu
, "s") < 0) {
1282 if (option_exists(menu
, "g") < 0) {
1283 ast_log(LOG_WARNING
, "No 's' nor 'g' extension in menu '%s'!\n", menu
->title
);
1288 while(menu
->options
[pos
].option
) {
1289 if (!strcasecmp(menu
->options
[pos
].option
, exten
)) {
1290 res
= ivr_dispatch(chan
, menu
->options
+ pos
, exten
, cbdata
);
1292 ast_log(LOG_DEBUG
, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten
, pos
, res
);
1295 else if (res
& RES_UPONE
)
1297 else if (res
& RES_EXIT
)
1299 else if (res
& RES_REPEAT
) {
1300 int maxretries
= res
& 0xffff;
1301 if ((res
& RES_RESTART
) == RES_RESTART
) {
1307 if ((maxretries
> 0) && (retries
>= maxretries
)) {
1309 ast_log(LOG_DEBUG
, "Max retries %d exceeded\n", maxretries
);
1312 if (option_exists(menu
, "g") > -1)
1314 else if (option_exists(menu
, "s") > -1)
1319 } else if (res
&& strchr(AST_DIGIT_ANY
, res
)) {
1321 ast_log(LOG_DEBUG
, "Got start of extension, %c\n", res
);
1324 if ((res
= read_newoption(chan
, menu
, exten
, sizeof(exten
))))
1326 if (option_exists(menu
, exten
) < 0) {
1327 if (option_exists(menu
, "i")) {
1329 ast_log(LOG_DEBUG
, "Invalid extension entered, going to 'i'!\n");
1335 ast_log(LOG_DEBUG
, "Aborting on invalid entry, with no 'i' option!\n");
1341 ast_log(LOG_DEBUG
, "New existing extension: %s\n", exten
);
1350 ast_log(LOG_DEBUG
, "Stopping option '%s', res is %d\n", exten
, res
);
1352 if (!strcasecmp(exten
, "s"))
1360 int ast_ivr_menu_run(struct ast_channel
*chan
, struct ast_ivr_menu
*menu
, void *cbdata
)
1362 int res
= ast_ivr_menu_run_internal(chan
, menu
, cbdata
);
1363 /* Hide internal coding */
1364 return res
> 0 ? 0 : res
;
1367 char *ast_read_textfile(const char *filename
)
1370 char *output
= NULL
;
1371 struct stat filesize
;
1374 if (stat(filename
, &filesize
) == -1) {
1375 ast_log(LOG_WARNING
, "Error can't stat %s\n", filename
);
1378 count
= filesize
.st_size
+ 1;
1379 fd
= open(filename
, O_RDONLY
);
1381 ast_log(LOG_WARNING
, "Cannot open file '%s' for reading: %s\n", filename
, strerror(errno
));
1384 if ((output
= ast_malloc(count
))) {
1385 res
= read(fd
, output
, count
- 1);
1386 if (res
== count
- 1) {
1389 ast_log(LOG_WARNING
, "Short read of %s (%d of %d): %s\n", filename
, res
, count
- 1, strerror(errno
));
1398 void ast_app_options2str(const struct ast_app_option
*options
, struct ast_flags
*flags
, char *buf
, size_t len
)
1400 unsigned int i
, found
= 0;
1402 for (i
= 32; i
< 128 && found
< len
;i
++) {
1403 if (ast_test_flag(flags
, options
[i
].flag
)) {
1410 int ast_app_parse_options(const struct ast_app_option
*options
, struct ast_flags
*flags
, char **args
, char *optstr
)
1414 unsigned int argloc
;
1418 ast_clear_flag(flags
, AST_FLAGS_ALL
);
1425 curarg
= *s
++ & 0x7f; /* the array (in app.h) has 128 entries */
1426 argloc
= options
[curarg
].arg_index
;
1430 if ((s
= strchr(s
, ')'))) {
1432 args
[argloc
- 1] = arg
;
1435 ast_log(LOG_WARNING
, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg
, arg
);
1439 } else if (argloc
) {
1440 args
[argloc
- 1] = "";
1442 ast_set_flag(flags
, options
[curarg
].flag
);