| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Copyright (C) 1999 - 2005, Digium, Inc. | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-03-10 07:47:02 +00:00
										 |  |  |  * Mark Spencer <markster@digium.com> | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  |  * This program is free software, distributed under the terms of | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  | /*! \file
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-10-24 20:12:06 +00:00
										 |  |  |  * \brief Trivial application to playback a sound file | 
					
						
							| 
									
										
										
										
											2005-12-30 21:18:06 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * \author Mark Spencer <markster@digium.com> | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  *  | 
					
						
							| 
									
										
										
										
											2005-11-06 15:09:47 +00:00
										 |  |  |  * \ingroup applications | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2005-06-06 22:39:32 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-21 06:02:45 +00:00
										 |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/file.h"
 | 
					
						
							|  |  |  | #include "asterisk/logger.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/translate.h"
 | 
					
						
							|  |  |  | #include "asterisk/utils.h"
 | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | #include "asterisk/options.h"
 | 
					
						
							|  |  |  | #include "asterisk/app.h"
 | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | #include "asterisk/cli.h"
 | 
					
						
							|  |  |  | #include "asterisk/localtime.h"
 | 
					
						
							|  |  |  | #include "asterisk/say.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | static char *app = "Playback"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-10 17:18:04 +00:00
										 |  |  | static char *synopsis = "Play a file"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char *descrip =  | 
					
						
							| 
									
										
										
										
											2005-03-10 07:47:02 +00:00
										 |  |  | "  Playback(filename[&filename2...][|option]):  Plays back given filenames (do not put\n" | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | "extension). Options may also be included following a pipe symbol. The 'skip'\n" | 
					
						
							|  |  |  | "option causes the playback of the message to be skipped if the channel\n" | 
					
						
							|  |  |  | "is not in the 'up' state (i.e. it hasn't been  answered  yet). If 'skip' is \n" | 
					
						
							| 
									
										
										
										
											2004-07-19 02:18:11 +00:00
										 |  |  | "specified, the application will return immediately should the channel not be\n" | 
					
						
							| 
									
										
										
										
											2005-11-07 22:01:22 +00:00
										 |  |  | "off hook.  Otherwise, unless 'noanswer' is specified, the channel will\n" | 
					
						
							| 
									
										
										
										
											2004-07-19 02:18:11 +00:00
										 |  |  | "be answered before the sound is played. Not all channels support playing\n" | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | "messages while still on hook. If 'j' is specified, the application\n" | 
					
						
							|  |  |  | "will jump to priority n+101 if present when a file specified to be played\n" | 
					
						
							|  |  |  | "does not exist.\n" | 
					
						
							|  |  |  | "This application sets the following channel variable upon completion:\n" | 
					
						
							|  |  |  | " PLAYBACKSTATUS    The status of the playback attempt as a text string, one of\n" | 
					
						
							|  |  |  | "               SUCCESS | FAILED\n" | 
					
						
							|  |  |  | ; | 
					
						
							| 
									
										
										
										
											2001-04-10 17:18:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-05 17:06:18 +00:00
										 |  |  | static struct ast_config *say_cfg = NULL; | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | /* save the say' api calls.
 | 
					
						
							|  |  |  |  * The first entry is NULL if we have the standard source, | 
					
						
							|  |  |  |  * otherwise we are sourcing from here. | 
					
						
							|  |  |  |  * 'say load [new|old]' will enable the new or old method, or report status | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static const void * say_api_buf[40]; | 
					
						
							|  |  |  | static const char *say_old = "old"; | 
					
						
							|  |  |  | static const char *say_new = "new"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void save_say_mode(const void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	say_api_buf[i++] = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_number_full; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_enumeration_full; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_digit_str_full; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_character_str_full; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_phonetic_str_full; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_datetime; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_time; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_date; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_datetime_from_now; | 
					
						
							|  |  |  |         say_api_buf[i++] = ast_say_date_with_format; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void restore_say_mode(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 	say_api_buf[i++] = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ast_say_number_full = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_enumeration_full = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_digit_str_full = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_character_str_full = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_phonetic_str_full = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_datetime = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_time = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_date = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_datetime_from_now = say_api_buf[i++]; | 
					
						
							|  |  |  |         ast_say_date_with_format = say_api_buf[i++]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* 
 | 
					
						
							|  |  |  |  * Typical 'say' arguments in addition to the date or number or string | 
					
						
							|  |  |  |  * to say. We do not include 'options' because they may be different | 
					
						
							|  |  |  |  * in recursive calls, and so they are better left as an external | 
					
						
							|  |  |  |  * parameter. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |         struct ast_channel *chan; | 
					
						
							|  |  |  |         const char *ints; | 
					
						
							|  |  |  |         const char *language; | 
					
						
							|  |  |  |         int audiofd; | 
					
						
							|  |  |  |         int ctrlfd; | 
					
						
							|  |  |  | } say_args_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int s_streamwait3(const say_args_t *a, const char *fn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |         int res = ast_streamfile(a->chan, fn, a->language); | 
					
						
							|  |  |  |         if (res) { | 
					
						
							|  |  |  |                 ast_log(LOG_WARNING, "Unable to play message %s\n", fn); | 
					
						
							|  |  |  |                 return res; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         res = (a->audiofd  > -1 && a->ctrlfd > -1) ? | 
					
						
							|  |  |  |                 ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) : | 
					
						
							|  |  |  |                 ast_waitstream(a->chan, a->ints); | 
					
						
							|  |  |  |         ast_stopstream(a->chan); | 
					
						
							|  |  |  |         return res;   | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * the string is 'prefix:data' or prefix:fmt:data' | 
					
						
							|  |  |  |  * with ':' being invalid in strings. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int do_say(say_args_t *a, const char *s, const char *options, int depth) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_variable *v; | 
					
						
							|  |  |  | 	char *lang, *x, *rule = NULL; | 
					
						
							|  |  |  | 	int ret = 0;    | 
					
						
							|  |  |  | 	struct varshead head = { .first = NULL, .last = NULL }; | 
					
						
							|  |  |  | 	struct ast_var_t *n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_log(LOG_WARNING, "string <%s> depth <%d>\n", s, depth); | 
					
						
							|  |  |  | 	if (depth++ > 10) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "recursion too deep, exiting\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2006-10-13 16:20:58 +00:00
										 |  |  | 	} else if (!say_cfg) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* scan languages same as in file.c */ | 
					
						
							|  |  |  | 	if (a->language == NULL) | 
					
						
							|  |  |  | 		a->language = "en";     /* default */ | 
					
						
							|  |  |  | 	ast_log(LOG_WARNING, "try <%s> in <%s>\n", s, a->language); | 
					
						
							|  |  |  | 	lang = ast_strdupa(a->language); | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) { | 
					
						
							|  |  |  | 			if (ast_extension_match(v->name, s)) { | 
					
						
							|  |  |  | 				rule = ast_strdupa(v->value); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (rule) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if ( (x = strchr(lang, '_')) ) | 
					
						
							|  |  |  | 			*x = '\0';      /* try without suffix */ | 
					
						
							|  |  |  | 		else if (strcmp(lang, "en")) | 
					
						
							|  |  |  | 			lang = "en";    /* last resort, try 'en' if not done yet */ | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!rule) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* skip up to two prefixes to get the value */ | 
					
						
							|  |  |  | 	if ( (x = strchr(s, ':')) ) | 
					
						
							|  |  |  | 		s = x + 1; | 
					
						
							|  |  |  | 	if ( (x = strchr(s, ':')) ) | 
					
						
							|  |  |  | 		s = x + 1; | 
					
						
							|  |  |  | 	ast_log(LOG_WARNING, "value is <%s>\n", s); | 
					
						
							|  |  |  | 	n = ast_var_assign("SAY", s); | 
					
						
							|  |  |  | 	AST_LIST_INSERT_HEAD(&head, n, entries); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* scan the body, one piece at a time */ | 
					
						
							|  |  |  | 	while ( ret <= 0 && (x = strsep(&rule, ",")) ) { /* exit on key */ | 
					
						
							|  |  |  | 		char fn[128]; | 
					
						
							|  |  |  | 		const char *p, *fmt, *data; /* format and data pointers */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* prepare a decent file name */ | 
					
						
							|  |  |  | 		x = ast_skip_blanks(x); | 
					
						
							|  |  |  | 		ast_trim_blanks(x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* replace variables */ | 
					
						
							|  |  |  | 		memset(fn, 0, sizeof(fn)); /* XXX why isn't done in pbx_substitute_variables_helper! */ | 
					
						
							|  |  |  | 		pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn)); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "doing [%s]\n", fn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* locate prefix and data, if any */ | 
					
						
							|  |  |  | 		fmt = index(fn, ':'); | 
					
						
							|  |  |  | 		if (!fmt || fmt == fn)	{	/* regular filename */ | 
					
						
							|  |  |  | 			ret = s_streamwait3(a, fn); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fmt++; | 
					
						
							|  |  |  | 		data = index(fmt, ':');	/* colon before data */ | 
					
						
							|  |  |  | 		if (!data || data == fmt) {	/* simple prefix-fmt */ | 
					
						
							|  |  |  | 			ret = do_say(a, fn, options, depth); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* prefix:fmt:data */ | 
					
						
							|  |  |  | 		for (p = fmt; p < data && ret <= 0; p++) { | 
					
						
							| 
									
										
										
										
											2006-05-29 05:14:52 +00:00
										 |  |  | 			char fn2[sizeof(fn)]; | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 			if (*p == ' ' || *p == '\t')	/* skip blanks */ | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			if (*p == '\'') {/* file name - we trim them */ | 
					
						
							|  |  |  | 				char *y; | 
					
						
							|  |  |  | 				strcpy(fn2, ast_skip_blanks(p+1));	/* make a full copy */ | 
					
						
							|  |  |  | 				y = index(fn2, '\''); | 
					
						
							|  |  |  | 				if (!y) { | 
					
						
							|  |  |  | 					p = data;	/* invalid. prepare to end */ | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				*y = '\0'; | 
					
						
							|  |  |  | 				ast_trim_blanks(fn2); | 
					
						
							|  |  |  | 				p = index(p+1, '\''); | 
					
						
							|  |  |  | 				ret = s_streamwait3(a, fn2); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				int l = fmt-fn; | 
					
						
							|  |  |  | 				strcpy(fn2, fn); /* copy everything */ | 
					
						
							|  |  |  | 				/* after prefix, append the format */ | 
					
						
							|  |  |  | 				fn2[l++] = *p; | 
					
						
							|  |  |  | 				strcpy(fn2 + l, data); | 
					
						
							|  |  |  | 				ret = do_say(a, fn2, options, depth); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_var_delete(n); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_full(struct ast_channel *chan, const char *string, | 
					
						
							|  |  |  |         const char *ints, const char *lang, const char *options, | 
					
						
							|  |  |  |         int audiofd, int ctrlfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |         say_args_t a = { chan, ints, lang, audiofd, ctrlfd }; | 
					
						
							|  |  |  |         return do_say(&a, string, options, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_number_full(struct ast_channel *chan, int num, | 
					
						
							|  |  |  | 	const char *ints, const char *lang, const char *options, | 
					
						
							|  |  |  | 	int audiofd, int ctrlfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[64]; | 
					
						
							|  |  |  |         say_args_t a = { chan, ints, lang, audiofd, ctrlfd }; | 
					
						
							|  |  |  | 	snprintf(buf, sizeof(buf), "num:%d", num); | 
					
						
							|  |  |  |         return do_say(&a, buf, options, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_enumeration_full(struct ast_channel *chan, int num, | 
					
						
							|  |  |  | 	const char *ints, const char *lang, const char *options, | 
					
						
							|  |  |  | 	int audiofd, int ctrlfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[64]; | 
					
						
							|  |  |  |         say_args_t a = { chan, ints, lang, audiofd, ctrlfd }; | 
					
						
							|  |  |  | 	snprintf(buf, sizeof(buf), "enum:%d", num); | 
					
						
							|  |  |  |         return do_say(&a, buf, options, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_date_generic(struct ast_channel *chan, time_t t, | 
					
						
							|  |  |  | 	const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[128]; | 
					
						
							|  |  |  | 	struct tm tm; | 
					
						
							|  |  |  |         say_args_t a = { chan, ints, lang, -1, -1 }; | 
					
						
							|  |  |  | 	if (format == NULL) | 
					
						
							|  |  |  | 		format = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_localtime(&t, &tm, NULL); | 
					
						
							|  |  |  | 	snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d", | 
					
						
							|  |  |  | 		prefix, | 
					
						
							|  |  |  | 		format, | 
					
						
							|  |  |  | 		tm.tm_year+1900, | 
					
						
							|  |  |  | 		tm.tm_mon+1, | 
					
						
							|  |  |  | 		tm.tm_mday, | 
					
						
							|  |  |  | 		tm.tm_hour, | 
					
						
							|  |  |  | 		tm.tm_min, | 
					
						
							|  |  |  | 		tm.tm_sec, | 
					
						
							|  |  |  | 		tm.tm_wday, | 
					
						
							|  |  |  | 		tm.tm_yday); | 
					
						
							|  |  |  |         return do_say(&a, buf, NULL, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_date_with_format(struct ast_channel *chan, time_t t, | 
					
						
							|  |  |  | 	const char *ints, const char *lang, const char *format, const char *timezone) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return say_date_generic(chan, t, ints, lang, format, timezone, "datetime"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return say_date_generic(chan, t, ints, lang, "", NULL, "date"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return say_date_generic(chan, t, ints, lang, "", NULL, "time"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return say_date_generic(chan, t, ints, lang, "", NULL, "datetime"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * remap the 'say' functions to use those in this file | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int __say_init(int fd, int argc, char *argv[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const char *old_mode = say_api_buf[0] ? say_new : say_old; | 
					
						
							|  |  |  | 	char *mode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (argc == 2) { | 
					
						
							|  |  |  | 		ast_cli(fd, "say mode is [%s]\n", old_mode); | 
					
						
							|  |  |  | 		return RESULT_SUCCESS; | 
					
						
							|  |  |  |         } else if (argc != 3) | 
					
						
							|  |  |  |                 return RESULT_SHOWUSAGE; | 
					
						
							|  |  |  |         mode = argv[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-28 18:05:43 +00:00
										 |  |  | 	ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode); | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcmp(mode, old_mode)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "say mode is %s already\n", mode); | 
					
						
							|  |  |  | 	} else if (!strcmp(mode, say_new)) { | 
					
						
							|  |  |  | 		if (say_cfg == NULL) | 
					
						
							|  |  |  | 			say_cfg = ast_config_load("say.conf"); | 
					
						
							|  |  |  | 		save_say_mode(say_new); | 
					
						
							|  |  |  | 		ast_say_number_full = say_number_full; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_say_enumeration_full = say_enumeration_full; | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 		ast_say_digits_full = say_digits_full; | 
					
						
							|  |  |  | 		ast_say_digit_str_full = say_digit_str_full; | 
					
						
							|  |  |  | 		ast_say_character_str_full = say_character_str_full; | 
					
						
							|  |  |  | 		ast_say_phonetic_str_full = say_phonetic_str_full; | 
					
						
							|  |  |  | 		ast_say_datetime_from_now = say_datetime_from_now; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		ast_say_datetime = say_datetime; | 
					
						
							|  |  |  | 		ast_say_time = say_time; | 
					
						
							|  |  |  | 		ast_say_date = say_date; | 
					
						
							|  |  |  | 		ast_say_date_with_format = say_date_with_format; | 
					
						
							|  |  |  | 	} else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) { | 
					
						
							|  |  |  | 		restore_say_mode(NULL); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "unrecognized mode %s\n", mode); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return RESULT_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-18 19:54:18 +00:00
										 |  |  | static struct ast_cli_entry cli_playback[] = { | 
					
						
							|  |  |  |         { { "say", "load", NULL }, | 
					
						
							|  |  |  | 	__say_init, "set/show the say mode", | 
					
						
							|  |  |  | 	"say load new|old" }, | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | static int playback_exec(struct ast_channel *chan, void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-08 00:08:49 +00:00
										 |  |  | 	int res = 0; | 
					
						
							| 
									
										
										
										
											2006-09-27 19:44:02 +00:00
										 |  |  | 	int mres = 0; | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 	struct ast_module_user *u; | 
					
						
							| 
									
										
										
										
											2006-01-08 00:08:49 +00:00
										 |  |  | 	char *tmp; | 
					
						
							| 
									
										
										
										
											2001-06-19 07:19:07 +00:00
										 |  |  | 	int option_skip=0; | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 	int option_say=0; | 
					
						
							| 
									
										
										
										
											2001-11-10 02:55:10 +00:00
										 |  |  | 	int option_noanswer = 0; | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 	int priority_jump = 0; | 
					
						
							| 
									
										
										
										
											2006-01-08 00:08:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(filenames); | 
					
						
							|  |  |  | 		AST_APP_ARG(options); | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2005-10-26 19:48:14 +00:00
										 |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Playback requires an argument (filename)\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-10 13:22:15 +00:00
										 |  |  | 	tmp = ast_strdupa(data); | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 	u = ast_module_user_add(chan); | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 	AST_STANDARD_APP_ARGS(args, tmp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (args.options) { | 
					
						
							| 
									
										
										
										
											2005-11-15 19:33:15 +00:00
										 |  |  | 		if (strcasestr(args.options, "skip")) | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 			option_skip = 1; | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 		if (strcasestr(args.options, "say")) | 
					
						
							|  |  |  | 			option_say = 1; | 
					
						
							| 
									
										
										
										
											2005-11-15 19:33:15 +00:00
										 |  |  | 		if (strcasestr(args.options, "noanswer")) | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 			option_noanswer = 1; | 
					
						
							|  |  |  | 		if (strchr(args.options, 'j')) | 
					
						
							|  |  |  | 			priority_jump = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2002-07-12 09:03:50 +00:00
										 |  |  | 	if (chan->_state != AST_STATE_UP) { | 
					
						
							| 
									
										
										
										
											2001-06-19 07:19:07 +00:00
										 |  |  | 		if (option_skip) { | 
					
						
							|  |  |  | 			/* At the user's option, skip if the line is not up */ | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 			goto done; | 
					
						
							| 
									
										
										
										
											2001-11-10 02:55:10 +00:00
										 |  |  | 		} else if (!option_noanswer) | 
					
						
							|  |  |  | 			/* Otherwise answer unless we're supposed to send this while on-hook */ | 
					
						
							| 
									
										
										
										
											2001-06-19 07:19:07 +00:00
										 |  |  | 			res = ast_answer(chan); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2000-01-09 19:58:18 +00:00
										 |  |  | 	if (!res) { | 
					
						
							| 
									
										
										
										
											2006-09-27 19:44:02 +00:00
										 |  |  | 		char *back = args.filenames; | 
					
						
							| 
									
										
										
										
											2006-01-08 00:08:49 +00:00
										 |  |  | 		char *front; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-01-09 19:58:18 +00:00
										 |  |  | 		ast_stopstream(chan); | 
					
						
							| 
									
										
										
										
											2006-09-27 19:44:02 +00:00
										 |  |  | 		while (!res && (front = strsep(&back, "&"))) { | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | 			if (option_say) | 
					
						
							|  |  |  | 				res = say_full(chan, front, "", chan->language, NULL, -1, -1); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				res = ast_streamfile(chan, front, chan->language); | 
					
						
							| 
									
										
										
										
											2005-03-10 07:47:02 +00:00
										 |  |  | 			if (!res) {  | 
					
						
							|  |  |  | 				res = ast_waitstream(chan, "");	 | 
					
						
							|  |  |  | 				ast_stopstream(chan); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data); | 
					
						
							| 
									
										
										
										
											2005-12-04 20:40:46 +00:00
										 |  |  | 				if (priority_jump || ast_opt_priority_jumping) | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 					ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); | 
					
						
							| 
									
										
										
										
											2005-03-10 07:47:02 +00:00
										 |  |  | 				res = 0; | 
					
						
							| 
									
										
										
										
											2005-11-07 23:10:02 +00:00
										 |  |  | 				mres = 1; | 
					
						
							| 
									
										
										
										
											2005-03-10 07:47:02 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2002-07-12 09:03:50 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2000-01-09 19:58:18 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-04-19 10:27:31 +00:00
										 |  |  | done: | 
					
						
							| 
									
										
										
										
											2006-09-27 19:44:02 +00:00
										 |  |  | 	pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS"); | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 	ast_module_user_remove(u); | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int reload(void) | 
					
						
							| 
									
										
										
										
											2006-05-10 15:38:54 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (say_cfg) { | 
					
						
							|  |  |  | 		ast_config_destroy(say_cfg); | 
					
						
							|  |  |  | 		ast_log(LOG_NOTICE, "Reloading say.conf\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	say_cfg = ast_config_load("say.conf"); | 
					
						
							| 
									
										
										
										
											2006-05-29 05:14:52 +00:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * XXX here we should sort rules according to the same order | 
					
						
							|  |  |  | 	 * we have in pbx.c so we have the same matching behaviour. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-05-10 15:38:54 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int unload_module(void) | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2005-10-18 22:52:21 +00:00
										 |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = ast_unregister_application(app); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-05 16:02:00 +00:00
										 |  |  | 	ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 	ast_module_user_hangup_all(); | 
					
						
							| 
									
										
										
										
											2005-10-18 22:52:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-13 04:40:15 +00:00
										 |  |  | 	if (say_cfg) | 
					
						
							|  |  |  | 		ast_config_destroy(say_cfg); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-18 22:52:21 +00:00
										 |  |  | 	return res;	 | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int load_module(void) | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-02-07 17:04:44 +00:00
										 |  |  | 	say_cfg = ast_config_load("say.conf"); | 
					
						
							| 
									
										
										
										
											2006-09-18 19:54:18 +00:00
										 |  |  |         ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry)); | 
					
						
							| 
									
										
										
										
											2001-04-10 17:18:04 +00:00
										 |  |  | 	return ast_register_application(app, playback_exec, synopsis, descrip); | 
					
						
							| 
									
										
										
										
											1999-11-21 21:15:14 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application", | 
					
						
							|  |  |  | 		.load = load_module, | 
					
						
							|  |  |  | 		.unload = unload_module, | 
					
						
							|  |  |  | 		.reload = reload, | 
					
						
							|  |  |  | 	       ); |