| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +00:00
										 |  |  |  * Copyright (C) 1999 - 2005, Digium, Inc. | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Kevin P. Fleming <kpfleming@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Portions taken from the file-based music-on-hold work | 
					
						
							|  |  |  |  * created by Anthony Minessale II in res_musiconhold.c | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +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 External IVR application interface | 
					
						
							| 
									
										
										
										
											2005-12-30 21:18:06 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * \author Kevin P. Fleming <kpfleming@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \note Portions taken from the file-based music-on-hold work | 
					
						
							|  |  |  |  * created by Anthony Minessale II in res_musiconhold.c | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-11-06 15:09:47 +00:00
										 |  |  |  * \ingroup applications | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-11 00:52:19 +00:00
										 |  |  | #include <signal.h>
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/file.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/linkedlists.h"
 | 
					
						
							| 
									
										
										
										
											2005-12-01 02:32:10 +00:00
										 |  |  | #include "asterisk/app.h"
 | 
					
						
							| 
									
										
										
										
											2006-01-11 22:41:34 +00:00
										 |  |  | #include "asterisk/utils.h"
 | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | #include "asterisk/tcptls.h"
 | 
					
						
							| 
									
										
										
										
											2008-10-19 19:11:28 +00:00
										 |  |  | #include "asterisk/astobj2.h"
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-07 17:29:44 +00:00
										 |  |  | /*** DOCUMENTATION
 | 
					
						
							|  |  |  | 	<application name="ExternalIVR" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							|  |  |  | 			Interfaces with an external IVR application. | 
					
						
							|  |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							|  |  |  | 			<parameter name="command|ivr://host" required="true" hasparams="true"> | 
					
						
							|  |  |  | 				<argument name="arg1" /> | 
					
						
							|  |  |  | 				<argument name="arg2" multiple="yes" /> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 			<parameter name="options"> | 
					
						
							|  |  |  | 				<optionlist> | 
					
						
							|  |  |  | 					<option name="n"> | 
					
						
							|  |  |  | 						<para>Tells ExternalIVR() not to answer the channel.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 					<option name="i"> | 
					
						
							|  |  |  | 						<para>Tells ExternalIVR() not to send a hangup and exit when the | 
					
						
							|  |  |  | 						channel receives a hangup, instead it sends an <literal>I</literal> | 
					
						
							|  |  |  | 						informative message meaning that the external application MUST hang | 
					
						
							|  |  |  | 						up the call with an <literal>H</literal> command.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 					<option name="d"> | 
					
						
							|  |  |  | 						<para>Tells ExternalIVR() to run on a channel that has been hung up | 
					
						
							|  |  |  | 						and will not look for hangups. The external application must exit with | 
					
						
							|  |  |  | 						an <literal>E</literal> command.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 				</optionlist> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							|  |  |  | 			<para>Either forks a process to run given command or makes a socket to connect | 
					
						
							|  |  |  | 			to given host and starts a generator on the channel. The generator's play list | 
					
						
							|  |  |  | 			is controlled by the external application, which can add and clear entries via | 
					
						
							|  |  |  | 			simple commands issued over its stdout. The external application will receive | 
					
						
							|  |  |  | 			all DTMF events received on the channel, and notification if the channel is | 
					
						
							|  |  |  | 			hung up. The received on the channel, and notification if the channel is hung | 
					
						
							|  |  |  | 			up. The application will not be forcibly terminated when the channel is hung up. | 
					
						
							| 
									
										
										
										
											2011-02-02 19:27:19 +00:00
										 |  |  | 			For more information see <filename>doc/AST.pdf</filename>.</para> | 
					
						
							| 
									
										
										
										
											2009-06-07 17:29:44 +00:00
										 |  |  | 		</description> | 
					
						
							|  |  |  | 	</application> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char app[] = "ExternalIVR"; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-06 14:30:53 +00:00
										 |  |  | /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */ | 
					
						
							|  |  |  | #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
 | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | /* Commands */ | 
					
						
							|  |  |  | #define EIVR_CMD_APND 'A' /* append to prompt queue */
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | #define EIVR_CMD_DTMF 'D' /* send DTMF */
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | #define EIVR_CMD_EXIT 'E' /* exit */
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | #define EIVR_CMD_GET  'G' /* get channel varable(s) */
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | #define EIVR_CMD_HGUP 'H' /* hangup */
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | #define EIVR_CMD_LOG  'L' /* log message */
 | 
					
						
							|  |  |  | #define EIVR_CMD_OPT  'O' /* option */
 | 
					
						
							|  |  |  | #define EIVR_CMD_PARM 'P' /* return supplied params */
 | 
					
						
							|  |  |  | #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
 | 
					
						
							|  |  |  | #define EIVR_CMD_ANS  'T' /* answer channel */
 | 
					
						
							|  |  |  | #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
 | 
					
						
							|  |  |  | #define EIVR_CMD_XIT  'X' /* exit **depricated** */
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | enum options_flags { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	noanswer = (1 << 0), | 
					
						
							|  |  |  | 	ignore_hangup = (1 << 1), | 
					
						
							|  |  |  | 	run_dead = (1 << 2), | 
					
						
							| 
									
										
										
										
											2009-06-15 19:10:10 +00:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | AST_APP_OPTIONS(app_opts, { | 
					
						
							|  |  |  | 	AST_APP_OPTION('n', noanswer), | 
					
						
							|  |  |  | 	AST_APP_OPTION('i', ignore_hangup), | 
					
						
							|  |  |  | 	AST_APP_OPTION('d', run_dead), | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | struct playlist_entry { | 
					
						
							|  |  |  | 	AST_LIST_ENTRY(playlist_entry) list; | 
					
						
							|  |  |  | 	char filename[1]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | struct ivr_localuser { | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	struct ast_channel *chan; | 
					
						
							|  |  |  | 	AST_LIST_HEAD(playlist, playlist_entry) playlist; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 	AST_LIST_HEAD(finishlist, playlist_entry) finishlist; | 
					
						
							| 
									
										
										
										
											2005-09-07 15:32:01 +00:00
										 |  |  | 	int abort_current_sound; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 	int playing_silence; | 
					
						
							|  |  |  | 	int option_autoclear; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	int gen_active; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct gen_state { | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 	struct ivr_localuser *u; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	struct ast_filestream *stream; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 	struct playlist_entry *current; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	int sample_queue; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,  | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,  | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	const struct ast_str *args, const struct ast_flags flags); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | int eivr_connect_socket(struct ast_channel *chan, const char *host, int port); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | static void send_eivr_event(FILE *handle, const char event, const char *data, | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 	const struct ast_channel *chan) | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	struct ast_str *tmp = ast_str_create(12); | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL)); | 
					
						
							|  |  |  | 	if (data) { | 
					
						
							| 
									
										
										
										
											2008-06-19 15:52:17 +00:00
										 |  |  | 		ast_str_append(&tmp, 0, ",%s", data); | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-13 08:36:35 +00:00
										 |  |  | 	fprintf(handle, "%s\n", ast_str_buffer(tmp)); | 
					
						
							|  |  |  | 	ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp)); | 
					
						
							| 
									
										
										
										
											2011-03-04 01:50:44 +00:00
										 |  |  | 	ast_free(tmp); | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | static void *gen_alloc(struct ast_channel *chan, void *params) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 	struct ivr_localuser *u = params; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	struct gen_state *state; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-11 22:41:34 +00:00
										 |  |  | 	if (!(state = ast_calloc(1, sizeof(*state)))) | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state->u = u; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gen_closestream(struct gen_state *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!state->stream) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_closestream(state->stream); | 
					
						
							|  |  |  | 	state->u->chan->stream = NULL; | 
					
						
							|  |  |  | 	state->stream = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void gen_release(struct ast_channel *chan, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gen_state *state = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gen_closestream(state); | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 	ast_free(data); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* caller has the playlist locked */ | 
					
						
							|  |  |  | static int gen_nextfile(struct gen_state *state) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 	struct ivr_localuser *u = state->u; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	char *file_to_stream; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-07 15:32:01 +00:00
										 |  |  | 	u->abort_current_sound = 0; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 	u->playing_silence = 0; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	gen_closestream(state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (!state->stream) { | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  | 		state->current = AST_LIST_FIRST(&u->playlist); | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 		if (state->current) { | 
					
						
							|  |  |  | 			file_to_stream = state->current->filename; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2006-12-11 22:31:20 +00:00
										 |  |  | 			file_to_stream = "silence/10"; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 			u->playing_silence = 1; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 			ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  | 			AST_LIST_LOCK(&u->playlist); | 
					
						
							|  |  |  | 			AST_LIST_REMOVE_HEAD(&u->playlist, list); | 
					
						
							|  |  |  | 			AST_LIST_UNLOCK(&u->playlist); | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 			if (!u->playing_silence) { | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (!state->stream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_frame *gen_readframe(struct gen_state *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_frame *f = NULL; | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 	struct ivr_localuser *u = state->u; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-09-07 15:32:01 +00:00
										 |  |  | 	if (u->abort_current_sound || | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 		(u->playing_silence && AST_LIST_FIRST(&u->playlist))) { | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		gen_closestream(state); | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 		AST_LIST_LOCK(&u->playlist); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		gen_nextfile(state); | 
					
						
							| 
									
										
										
										
											2005-08-24 14:24:55 +00:00
										 |  |  | 		AST_LIST_UNLOCK(&u->playlist); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(state->stream && (f = ast_readframe(state->stream)))) { | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 		if (state->current) { | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  | 			/* remove finished file from playlist */ | 
					
						
							|  |  |  |                         AST_LIST_LOCK(&u->playlist); | 
					
						
							|  |  |  |                         AST_LIST_REMOVE_HEAD(&u->playlist, list); | 
					
						
							|  |  |  |                         AST_LIST_UNLOCK(&u->playlist); | 
					
						
							|  |  |  | 			/* add finished file to finishlist */ | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 			AST_LIST_LOCK(&u->finishlist); | 
					
						
							|  |  |  | 			AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); | 
					
						
							|  |  |  | 			AST_LIST_UNLOCK(&u->finishlist); | 
					
						
							|  |  |  | 			state->current = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		if (!gen_nextfile(state)) | 
					
						
							|  |  |  | 			f = ast_readframe(state->stream); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int gen_generate(struct ast_channel *chan, void *data, int len, int samples) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct gen_state *state = data; | 
					
						
							|  |  |  | 	struct ast_frame *f = NULL; | 
					
						
							|  |  |  | 	int res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state->sample_queue += samples; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (state->sample_queue > 0) { | 
					
						
							|  |  |  | 		if (!(f = gen_readframe(state))) | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		res = ast_write(chan, f); | 
					
						
							|  |  |  | 		ast_frfree(f); | 
					
						
							|  |  |  | 		if (res < 0) { | 
					
						
							| 
									
										
										
										
											2005-08-24 23:10:55 +00:00
										 |  |  | 			ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		state->sample_queue -= f->samples; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_generator gen = | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	alloc: gen_alloc, | 
					
						
							|  |  |  | 	release: gen_release, | 
					
						
							|  |  |  | 	generate: gen_generate, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-02-14 10:19:09 +00:00
										 |  |  | 	/* original input data: "G,var1,var2," */ | 
					
						
							|  |  |  | 	/* data passed as "data":  "var1,var2" */ | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-14 19:47:39 +00:00
										 |  |  | 	char *inbuf, *variable; | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 	const char *value; | 
					
						
							|  |  |  | 	int j; | 
					
						
							| 
									
										
										
										
											2008-02-14 19:47:39 +00:00
										 |  |  | 	struct ast_str *newstring = ast_str_alloca(outbuflen);  | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-14 19:47:39 +00:00
										 |  |  | 	outbuf[0] = '\0'; | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-14 19:47:39 +00:00
										 |  |  | 	for (j = 1, inbuf = data; ; j++) { | 
					
						
							|  |  |  | 		variable = strsep(&inbuf, ","); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 		if (variable == NULL) { | 
					
						
							|  |  |  | 			int outstrlen = strlen(outbuf); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			if (outstrlen && outbuf[outstrlen - 1] == ',') { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 				outbuf[outstrlen - 1] = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2008-04-30 19:21:04 +00:00
										 |  |  | 		ast_channel_lock(chan); | 
					
						
							|  |  |  | 		if (!(value = pbx_builtin_getvar_helper(chan, variable))) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 			value = ""; | 
					
						
							| 
									
										
										
										
											2008-04-30 19:21:04 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-14 19:47:39 +00:00
										 |  |  | 		ast_str_append(&newstring, 0, "%s=%s,", variable, value); | 
					
						
							| 
									
										
										
										
											2008-04-30 19:21:04 +00:00
										 |  |  | 		ast_channel_unlock(chan); | 
					
						
							| 
									
										
										
										
											2008-12-13 08:36:35 +00:00
										 |  |  | 		ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void ast_eivr_setvariable(struct ast_channel *chan, char *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 18:58:26 +00:00
										 |  |  | 	char *inbuf = ast_strdupa(data), *variable; | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 18:58:26 +00:00
										 |  |  | 	for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		ast_debug(1, "Setting up a variable: %s\n", variable); | 
					
						
							| 
									
										
										
										
											2009-05-18 18:58:26 +00:00
										 |  |  | 		/* variable contains "varname=value" */ | 
					
						
							|  |  |  | 		value = strchr(variable, '='); | 
					
						
							|  |  |  | 		if (!value) { | 
					
						
							|  |  |  | 			value = ""; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2009-05-18 18:58:26 +00:00
										 |  |  | 			*value++ = '\0'; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-05-18 18:58:26 +00:00
										 |  |  | 		pbx_builtin_setvar_helper(chan, variable, value); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	char *data; | 
					
						
							|  |  |  | 	int dinterval = 0, duration = 0; | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(digits); | 
					
						
							|  |  |  | 		AST_APP_ARG(dinterval); | 
					
						
							|  |  |  | 		AST_APP_ARG(duration); | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data = ast_strdupa(vdata); | 
					
						
							|  |  |  | 	AST_STANDARD_APP_ARGS(args, data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(args.dinterval)) { | 
					
						
							|  |  |  | 		ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(args.duration)) { | 
					
						
							|  |  |  | 		ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration); | 
					
						
							|  |  |  | 	ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | static struct playlist_entry *make_entry(const char *filename) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct playlist_entry *entry; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 	if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	strcpy(entry->filename, filename); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return entry; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | static int app_exec(struct ast_channel *chan, const char *data) | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-09 17:26:02 +00:00
										 |  |  | 	struct ast_flags flags = { 0, }; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	char *opts[0]; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	struct playlist_entry *entry; | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	int child_stdin[2] = { -1, -1 }; | 
					
						
							|  |  |  | 	int child_stdout[2] = { -1, -1 }; | 
					
						
							|  |  |  | 	int child_stderr[2] = { -1, -1 }; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	int res = -1; | 
					
						
							|  |  |  | 	int pid; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	char hostname[1024]; | 
					
						
							|  |  |  | 	char *port_str = NULL; | 
					
						
							|  |  |  | 	int port = 0; | 
					
						
							| 
									
										
										
										
											2008-03-13 20:38:56 +00:00
										 |  |  | 	struct ast_tcptls_session_instance *ser = NULL; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-05-11 20:07:44 +00:00
										 |  |  | 	struct ivr_localuser foo = { | 
					
						
							|  |  |  | 		.playlist = AST_LIST_HEAD_INIT_VALUE, | 
					
						
							|  |  |  | 		.finishlist = AST_LIST_HEAD_INIT_VALUE, | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		.gen_active = 0, | 
					
						
							| 
									
										
										
										
											2006-05-11 20:07:44 +00:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 	struct ivr_localuser *u = &foo; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	char *buf; | 
					
						
							|  |  |  | 	int j; | 
					
						
							|  |  |  | 	char *s, **app_args, *e;  | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	struct ast_str *comma_delim_args = ast_str_alloca(100); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(eivr_args, | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		AST_APP_ARG(application); | 
					
						
							|  |  |  | 		AST_APP_ARG(options); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	); | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(application_args, | 
					
						
							| 
									
										
										
										
											2007-07-23 19:51:41 +00:00
										 |  |  | 		AST_APP_ARG(cmd)[32]; | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2006-01-27 00:31:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-19 18:19:02 +00:00
										 |  |  | 	u->abort_current_sound = 0; | 
					
						
							| 
									
										
										
										
											2006-02-16 22:59:38 +00:00
										 |  |  | 	u->chan = chan; | 
					
						
							| 
									
										
										
										
											2008-02-09 11:27:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-05 17:08:02 +00:00
										 |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n"); | 
					
						
							|  |  |  | 		goto exit; | 
					
						
							| 
									
										
										
										
											2009-11-05 17:08:02 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	buf = ast_strdupa(data); | 
					
						
							|  |  |  | 	AST_STANDARD_APP_ARGS(eivr_args, buf); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application); | 
					
						
							|  |  |  | 	ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Parse out any application arguments */ | 
					
						
							|  |  |  | 	if ((s = strchr(eivr_args.application, '('))) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		s[0] = ','; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		if ((e = strrchr(s, ')'))) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			*e = '\0'; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n"); | 
					
						
							|  |  |  | 			goto exit; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	AST_STANDARD_APP_ARGS(application_args, eivr_args.application); | 
					
						
							|  |  |  | 	app_args = application_args.argv; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	/* Put the application + the arguments in a , delimited list */ | 
					
						
							|  |  |  | 	ast_str_reset(comma_delim_args); | 
					
						
							|  |  |  | 	for (j = 0; application_args.cmd[j] != NULL; j++) { | 
					
						
							|  |  |  | 		ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Get rid of any extraneous arguments */ | 
					
						
							| 
									
										
										
										
											2009-12-07 19:48:14 +00:00
										 |  |  | 	if (eivr_args.options && (s = strchr(eivr_args.options, ','))) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		*s = '\0'; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	/* Parse the ExternalIVR() arguments */ | 
					
						
							|  |  |  | 	ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options); | 
					
						
							|  |  |  | 	ast_app_parse_options(app_opts, &flags, opts, eivr_args.options); | 
					
						
							|  |  |  | 	if (ast_test_flag(&flags, noanswer)) { | 
					
						
							|  |  |  | 		ast_verb(4, "noanswer is set\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_test_flag(&flags, ignore_hangup)) { | 
					
						
							|  |  |  | 		ast_verb(4, "ignore_hangup is set\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ast_test_flag(&flags, run_dead)) { | 
					
						
							|  |  |  | 		ast_verb(4, "run_dead is set\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	if (!(ast_test_flag(&flags, noanswer))) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		ast_verb(3, "Answering channel and starting generator\n"); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		if (chan->_state != AST_STATE_UP) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			if (ast_test_flag(&flags, run_dead)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 				ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				goto exit; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			ast_answer(chan); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (ast_activate_generator(chan, &gen, u) < 0) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n"); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			u->gen_active = 1; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strncmp(app_args[0], "ivr://", 6)) { | 
					
						
							| 
									
										
										
										
											2008-10-19 19:11:28 +00:00
										 |  |  | 		struct ast_tcptls_session_args ivr_desc = { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			.accept_fd = -1, | 
					
						
							|  |  |  | 			.name = "IVR", | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		struct ast_hostent hp; | 
					
						
							| 
									
										
										
										
											2010-07-08 22:08:07 +00:00
										 |  |  | 		struct sockaddr_in remote_address_tmp; | 
					
						
							| 
									
										
										
										
											2006-05-01 21:48:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		/*communicate through socket to server*/ | 
					
						
							|  |  |  | 		ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); | 
					
						
							|  |  |  | 		ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); | 
					
						
							|  |  |  | 		if ((port_str = strchr(hostname, ':')) != NULL) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			port_str[0] = 0; | 
					
						
							|  |  |  | 			port_str += 1; | 
					
						
							|  |  |  | 			port = atoi(port_str); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		if (!port) { | 
					
						
							|  |  |  | 			port = 2949;  /* default port, if one is not provided */ | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		ast_gethostbyname(hostname, &hp); | 
					
						
							| 
									
										
										
										
											2010-07-08 22:08:07 +00:00
										 |  |  | 		remote_address_tmp.sin_family = AF_INET; | 
					
						
							|  |  |  | 		remote_address_tmp.sin_port = htons(port); | 
					
						
							|  |  |  | 		memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, sizeof(hp.hp.h_addr)); | 
					
						
							|  |  |  | 		ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp); | 
					
						
							| 
									
										
											  
											
												SIP TCP/TLS: move client connection setup/write into tcp helper thread, various related locking/memory fixes.
        What this patch fixes
1.Moves sip TCP/TLS connection setup into the TCP helper thread:
  Connection setup takes awhile and before this it was being
  done while holding the monitor lock.
2.Moves TCP/TLS writing to the TCP helper thread:  Through the
  use of a packet queue and an alert pipe, the TCP helper thread
  can now be woken up to write data as well as read data.
3.Locking error: sip_xmit returned an XMIT_ERROR without giving
  up the tcptls_session lock.  This lock has been completely removed
  from sip_xmit and placed in the new sip_tcptls_write() function.
4.Memory leak:  When creating a tcptls_client the tls_cfg was alloced
  but never freed unless the tcptls_session failed to start.  Now the
  session_args for a sip client are an ao2 object which frees the
  tls_cfg on destruction.
5.Pointer to stack variable: During sip_prepare_socket the creation
  of a client's ast_tcptls_session_args was done on the stack and
  stored as a pointer in the newly created tcptls_session.  Depending
  on the events that followed, there was a slight possibility that
  pointer could have been accessed after the stack returned.  Given
  the new changes, it is always accessed after the stack returns
  which is why I found it.
Notable code changes
1.I broke tcptls.c's ast_tcptls_client_start() function into two
  functions.  One for creating and allocating the new tcptls_session,
  and a separate one for starting and handling the new connection.
  This allowed me to create the tcptls_session, launch the helper
  thread, and then establish the connection within the helper thread.
2.Writes to a tcptls_session are now done within the helper thread.
  This is done by using an alert pipe to wake up the thread if new
  data needs to be sent.  The thread's sip_threadinfo object contains
  the alert pipe as well as the packet queue.
3.Since the threadinfo object contains the alert pipe, it must now be
  accessed outside of the helper thread for every write (queuing of a
  packet).  For easy lookup, I moved the threadinfo objects from a
  linked list to an ao2_container.
(closes issue #13136)
Reported by: pabelanger
Tested by: dvossel, whys
(closes issue #15894)
Reported by: dvossel
Tested by: dvossel
Review: https://reviewboard.asterisk.org/r/380/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@225445 65c4cc65-6c06-0410-ace0-fbb531ad65f3
											
										 
											2009-10-22 19:55:51 +00:00
										 |  |  | 		if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 		res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		if (pipe(child_stdin)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (pipe(child_stdout)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (pipe(child_stderr)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2008-04-16 22:57:54 +00:00
										 |  |  | 		pid = ast_safe_fork(0); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		if (pid < 0) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			goto exit; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 		if (!pid) { | 
					
						
							|  |  |  | 			/* child process */ | 
					
						
							|  |  |  | 			if (ast_opt_high_priority) | 
					
						
							|  |  |  | 				ast_set_priority(0); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 			dup2(child_stdin[0], STDIN_FILENO); | 
					
						
							|  |  |  | 			dup2(child_stdout[1], STDOUT_FILENO); | 
					
						
							|  |  |  | 			dup2(child_stderr[1], STDERR_FILENO); | 
					
						
							| 
									
										
										
										
											2008-04-16 22:57:54 +00:00
										 |  |  | 			ast_close_fds_above_n(STDERR_FILENO); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 			execv(app_args[0], app_args); | 
					
						
							|  |  |  | 			fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			_exit(1); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* parent process */ | 
					
						
							|  |  |  | 			close(child_stdin[0]); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 			child_stdin[0] = -1; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			close(child_stdout[1]); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 			child_stdout[1] = -1; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 			close(child_stderr[1]); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 			child_stderr[1] = -1; | 
					
						
							|  |  |  | 			res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-12-11 00:52:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 	exit: | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	if (u->gen_active) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		ast_deactivate_generator(chan); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stdin[0] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stdin[0]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stdin[1] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stdin[1]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stdout[0] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stdout[0]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stdout[1] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stdout[1]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stderr[0] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stderr[0]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (child_stderr[1] > -1) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		close(child_stderr[1]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 	if (ser) { | 
					
						
							| 
									
										
										
										
											2008-06-17 21:46:57 +00:00
										 |  |  | 		ao2_ref(ser, -1); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		ast_free(entry); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 	return res; | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,  | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  |  				int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,  | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  				const struct ast_str *args, const struct ast_flags flags) | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct playlist_entry *entry; | 
					
						
							|  |  |  | 	struct ast_frame *f; | 
					
						
							|  |  |  | 	int ms; | 
					
						
							|  |  |  |  	int exception; | 
					
						
							|  |  |  |  	int ready_fd; | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  	struct ast_channel *rchan; | 
					
						
							|  |  |  |  	int res = -1; | 
					
						
							| 
									
										
										
										
											2008-05-14 16:53:23 +00:00
										 |  |  | 	int test_available_fd = -1; | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	int hangup_info_sent = 0; | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |    | 
					
						
							|  |  |  |  	FILE *eivr_commands = NULL; | 
					
						
							|  |  |  |  	FILE *eivr_errors = NULL; | 
					
						
							|  |  |  |  	FILE *eivr_events = NULL; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n"); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		goto exit; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 		ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n"); | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		goto exit; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */ | 
					
						
							|  |  |  |  		if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			goto exit; | 
					
						
							|  |  |  |  		} | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-05-14 16:53:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	test_available_fd = open("/dev/null", O_RDONLY); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |   | 
					
						
							|  |  |  |  	setvbuf(eivr_events, NULL, _IONBF, 0); | 
					
						
							|  |  |  |  	setvbuf(eivr_commands, NULL, _IONBF, 0); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  	if (eivr_errors) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 		setvbuf(eivr_errors, NULL, _IONBF, 0); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |  	while (1) { | 
					
						
							|  |  |  |  		if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			break; | 
					
						
							|  |  |  |  		} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  		if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { | 
					
						
							|  |  |  | 			if (ast_test_flag(&flags, ignore_hangup)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 				ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				send_eivr_event(eivr_events, 'I', "HANGUP", chan); | 
					
						
							|  |  |  | 				hangup_info_sent = 1; | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_verb(3, "Got check_hangup\n"); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  				send_eivr_event(eivr_events, 'H', NULL, chan); | 
					
						
							|  |  |  | 	 			break; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  		} | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |  		ready_fd = 0; | 
					
						
							|  |  |  |  		ms = 100; | 
					
						
							|  |  |  |  		errno = 0; | 
					
						
							|  |  |  |  		exception = 0; | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  |  		rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |   | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  		if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			AST_LIST_LOCK(&u->finishlist); | 
					
						
							|  |  |  |  			while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { | 
					
						
							|  |  |  |  				send_eivr_event(eivr_events, 'F', entry->filename, chan); | 
					
						
							|  |  |  |  				ast_free(entry); | 
					
						
							|  |  |  |  			} | 
					
						
							|  |  |  |  			AST_LIST_UNLOCK(&u->finishlist); | 
					
						
							|  |  |  |  		} | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  |  		if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			/* the channel has something */ | 
					
						
							|  |  |  |  			f = ast_read(chan); | 
					
						
							|  |  |  |  			if (!f) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_verb(3, "Returned no frame\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				send_eivr_event(eivr_events, 'H', NULL, chan); | 
					
						
							|  |  |  |  				break; | 
					
						
							|  |  |  |  			} | 
					
						
							|  |  |  |  			if (f->frametype == AST_FRAME_DTMF) { | 
					
						
							| 
									
										
										
										
											2009-11-04 14:05:12 +00:00
										 |  |  |  				send_eivr_event(eivr_events, f->subclass.integer, NULL, chan); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				if (u->option_autoclear) { | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  |   					if (!u->abort_current_sound && !u->playing_silence) { | 
					
						
							|  |  |  | 						/* send interrupted file as T data */ | 
					
						
							|  |  |  |  						entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); | 
					
						
							|  |  |  |  						send_eivr_event(eivr_events, 'T', entry->filename, chan); | 
					
						
							|  |  |  | 						ast_free(entry); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |   					AST_LIST_LOCK(&u->playlist); | 
					
						
							|  |  |  |   					while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { | 
					
						
							|  |  |  |  						send_eivr_event(eivr_events, 'D', entry->filename, chan); | 
					
						
							|  |  |  |   						ast_free(entry); | 
					
						
							|  |  |  |   					} | 
					
						
							|  |  |  |   					if (!u->playing_silence) | 
					
						
							|  |  |  |   						u->abort_current_sound = 1; | 
					
						
							|  |  |  |   					AST_LIST_UNLOCK(&u->playlist); | 
					
						
							|  |  |  |   				} | 
					
						
							| 
									
										
										
										
											2009-11-04 14:05:12 +00:00
										 |  |  |  			} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_verb(3, "Got AST_CONTROL_HANGUP\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				send_eivr_event(eivr_events, 'H', NULL, chan); | 
					
						
							| 
									
										
										
										
											2008-05-22 16:29:54 +00:00
										 |  |  | 				if (f->data.uint32) { | 
					
						
							|  |  |  | 					chan->hangupcause = f->data.uint32; | 
					
						
							| 
									
										
										
										
											2008-04-24 22:16:48 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_frfree(f); | 
					
						
							|  |  |  |  				break; | 
					
						
							|  |  |  |  			} | 
					
						
							|  |  |  |  			ast_frfree(f); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  |  		} else if (ready_fd == *eivr_commands_fd) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			char input[1024]; | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  |  			if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |   				break; | 
					
						
							|  |  |  |   			} | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | 			if (!fgets(input, sizeof(input), eivr_commands)) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			ast_strip(input); | 
					
						
							|  |  |  | 			ast_verb(4, "got command '%s'\n", input); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (strlen(input) < 3) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			if (input[0] == EIVR_CMD_PARM) { | 
					
						
							| 
									
										
										
										
											2008-12-13 08:36:35 +00:00
										 |  |  | 				struct ast_str *tmp = (struct ast_str *) args; | 
					
						
							| 
									
										
										
										
											2010-01-22 16:20:43 +00:00
										 |  |  | 				send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan); | 
					
						
							|  |  |  | 			} else if (input[0] == EIVR_CMD_DTMF) { | 
					
						
							|  |  |  | 				ast_verb(4, "Sending DTMF: %s\n", &input[2]); | 
					
						
							|  |  |  | 				ast_eivr_senddtmf(chan, &input[2]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			} else if (input[0] == EIVR_CMD_ANS) { | 
					
						
							|  |  |  | 				ast_verb(3, "Answering channel if needed and starting generator\n"); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				if (chan->_state != AST_STATE_UP) { | 
					
						
							|  |  |  | 					if (ast_test_flag(&flags, run_dead)) { | 
					
						
							|  |  |  | 						ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); | 
					
						
							|  |  |  | 						send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); | 
					
						
							|  |  |  | 						continue; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 					if (ast_answer(chan)) { | 
					
						
							|  |  |  | 						ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n"); | 
					
						
							|  |  |  | 						send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); | 
					
						
							|  |  |  | 						continue; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if (!(u->gen_active)) { | 
					
						
							|  |  |  | 					if (ast_activate_generator(chan, &gen, u) < 0) { | 
					
						
							|  |  |  | 						ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); | 
					
						
							|  |  |  | 						send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						u->gen_active = 1; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_SQUE) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  | 					ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n"); | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 					send_eivr_event(eivr_events, 'Z', NULL, chan); | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  				if (!ast_fileexists(&input[2], NULL, u->chan->language)) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  					ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  					send_eivr_event(eivr_events, 'Z', &input[2], chan); | 
					
						
							|  |  |  |  				} else { | 
					
						
							|  |  |  |  					AST_LIST_LOCK(&u->playlist); | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  | 	 				if (!u->abort_current_sound && !u->playing_silence) { | 
					
						
							|  |  |  | 						/* send interrupted file as T data */ | 
					
						
							|  |  |  |  						entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); | 
					
						
							|  |  |  |  						send_eivr_event(eivr_events, 'T', entry->filename, chan); | 
					
						
							|  |  |  | 						ast_free(entry); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  					while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { | 
					
						
							|  |  |  |  						send_eivr_event(eivr_events, 'D', entry->filename, chan); | 
					
						
							|  |  |  |  						ast_free(entry); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2010-01-18 17:41:44 +00:00
										 |  |  | 	 				if (!u->playing_silence) { | 
					
						
							|  |  |  |  						u->abort_current_sound = 1; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  |  					entry = make_entry(&input[2]); | 
					
						
							|  |  |  |  					if (entry) { | 
					
						
							|  |  |  |  						AST_LIST_INSERT_TAIL(&u->playlist, entry, list); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 	 				AST_LIST_UNLOCK(&u->playlist); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_APND) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { | 
					
						
							|  |  |  | 					ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); | 
					
						
							|  |  |  | 					send_eivr_event(eivr_events, 'Z', NULL, chan); | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  				if (!ast_fileexists(&input[2], NULL, u->chan->language)) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  					ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2009-11-20 17:28:01 +00:00
										 |  |  |  					send_eivr_event(eivr_events, 'Z', &input[2], chan); | 
					
						
							|  |  |  |  				} else { | 
					
						
							|  |  |  | 	 				entry = make_entry(&input[2]); | 
					
						
							|  |  |  |  					if (entry) { | 
					
						
							|  |  |  |  						AST_LIST_LOCK(&u->playlist); | 
					
						
							|  |  |  |  						AST_LIST_INSERT_TAIL(&u->playlist, entry, list); | 
					
						
							|  |  |  |  						AST_LIST_UNLOCK(&u->playlist); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				} | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_GET) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				char response[2048]; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 				ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); | 
					
						
							|  |  |  |  				send_eivr_event(eivr_events, 'G', response, chan); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_SVAR) { | 
					
						
							|  |  |  | 				ast_verb(4, "Setting Variables in channel: %s\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_eivr_setvariable(chan, &input[2]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_LOG) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_XIT) { | 
					
						
							|  |  |  |  				ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); | 
					
						
							|  |  |  | 				ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				res = 0; | 
					
						
							|  |  |  |  				break; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  | 			} else if (input[0] == EIVR_CMD_EXIT) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); | 
					
						
							|  |  |  |  				send_eivr_event(eivr_events, 'E', NULL, chan); | 
					
						
							|  |  |  |  				res = 0; | 
					
						
							|  |  |  |  				break; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_HGUP) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); | 
					
						
							|  |  |  |  				send_eivr_event(eivr_events, 'H', NULL, chan); | 
					
						
							|  |  |  |  				break; | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			} else if (input[0] == EIVR_CMD_OPT) { | 
					
						
							| 
									
										
										
										
											2008-05-22 05:10:01 +00:00
										 |  |  | 				if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { | 
					
						
							|  |  |  | 					ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); | 
					
						
							|  |  |  | 					send_eivr_event(eivr_events, 'Z', NULL, chan); | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				if (!strcasecmp(&input[2], "autoclear")) | 
					
						
							|  |  |  |  					u->option_autoclear = 1; | 
					
						
							|  |  |  |  				else if (!strcasecmp(&input[2], "noautoclear")) | 
					
						
							|  |  |  |  					u->option_autoclear = 0; | 
					
						
							|  |  |  |  				else | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  					ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  |  		} else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			char input[1024]; | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |  			if (exception || feof(eivr_errors)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  				break; | 
					
						
							|  |  |  |  			} | 
					
						
							|  |  |  |  			if (fgets(input, sizeof(input), eivr_errors)) { | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  				ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input)); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			} | 
					
						
							|  |  |  |  		} else if ((ready_fd < 0) && ms) {  | 
					
						
							|  |  |  |  			if (errno == 0 || errno == EINTR) | 
					
						
							|  |  |  |  				continue; | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2009-11-19 14:53:58 +00:00
										 |  |  |  			ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno)); | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  			break; | 
					
						
							|  |  |  |  		} | 
					
						
							|  |  |  |  	} | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	exit: | 
					
						
							| 
									
										
										
										
											2008-05-14 16:53:23 +00:00
										 |  |  | 	if (test_available_fd > -1) { | 
					
						
							|  |  |  | 		close(test_available_fd); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 	if (eivr_events) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |  		fclose(eivr_events); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 		*eivr_events_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (eivr_commands) { | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 		fclose(eivr_commands); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 		*eivr_commands_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (eivr_errors) { | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | 		fclose(eivr_errors); | 
					
						
							| 
									
										
										
										
											2009-12-02 22:17:22 +00:00
										 |  |  | 		*eivr_errors_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  |   	return res; | 
					
						
							| 
									
										
										
										
											2008-03-13 18:59:04 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-02-13 21:04:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int unload_module(void) | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-07-16 13:35:20 +00:00
										 |  |  | 	return ast_unregister_application(app); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int load_module(void) | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-06-07 17:29:44 +00:00
										 |  |  | 	return ast_register_application_xml(app, app_exec); | 
					
						
							| 
									
										
										
										
											2005-08-10 23:24:39 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application"); |