Skip to content
Snippets Groups Projects
app_stack.c 37.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	saved_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
    
    
    	ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
    		saved_context, saved_exten, saved_priority);
    
    	ast_channel_unlock(chan);
    	res = gosub_exec(chan, sub_args);
    	ast_debug(4, "%s exited with status %d\n", app_gosub, res);
    	ast_channel_lock(chan);
    	if (!res) {
    		struct ast_datastore *stack_store;
    
    		/* Mark the return location as special. */
    		stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
    		if (!stack_store) {
    			/* Should never happen! */
    			ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
    			res = -1;
    		} else {
    			struct gosub_stack_list *oldlist;
    			struct gosub_stack_frame *cur;
    
    			oldlist = stack_store->data;
    			cur = AST_LIST_FIRST(oldlist);
    			cur->is_special = 1;
    		}
    	}
    	if (!res) {
    		int found = 0;	/* set if we find at least one match */
    
    		/*
    		 * Run gosub body autoloop.
    		 *
    		 * Note that this loop is inverted from the normal execution
    		 * loop because we just executed the Gosub application as the
    		 * first extension of the autoloop.
    		 */
    		do {
    			/* Check for hangup. */
    			if (ast_check_hangup(chan)) {
    				if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
    					ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
    						ast_channel_name(chan));
    					break;
    				}
    				if (!ignore_hangup) {
    					break;
    				}
    			}
    
    			/* Next dialplan priority. */
    			ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
    
    			ast_channel_unlock(chan);
    			res = ast_spawn_extension(chan, ast_channel_context(chan),
    				ast_channel_exten(chan), ast_channel_priority(chan),
    				S_COR(ast_channel_caller(chan)->id.number.valid,
    					ast_channel_caller(chan)->id.number.str, NULL),
    				&found, 1);
    			ast_channel_lock(chan);
    		} while (!res);
    		if (found && res) {
    			/* Something bad happened, or a hangup has been requested. */
    			ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
    				ast_channel_context(chan), ast_channel_exten(chan),
    				ast_channel_priority(chan), res, ast_channel_name(chan));
    			ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
    				ast_channel_context(chan), ast_channel_exten(chan),
    				ast_channel_priority(chan), ast_channel_name(chan));
    		}
    
    		/* Did the routine return? */
    		if (ast_channel_priority(chan) == saved_priority
    			&& !strcmp(ast_channel_context(chan), saved_context)
    			&& !strcmp(ast_channel_exten(chan), saved_exten)) {
    			ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
    				ast_channel_name(chan), app_gosub, sub_args,
    				S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
    		} else {
    			ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
    				ast_channel_name(chan), app_gosub, sub_args);
    			balance_stack(chan);
    			pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
    		}
    
    		/* We executed the requested subroutine to the best of our ability. */
    		res = 0;
    	}
    
    	ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
    		ast_channel_context(chan), ast_channel_exten(chan),
    		ast_channel_priority(chan));
    
    	/* Restore dialplan location */
    	if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
    		ast_channel_context_set(chan, saved_context);
    		ast_channel_exten_set(chan, saved_exten);
    		ast_channel_priority_set(chan, saved_priority);
    	}
    
    	/* Restore autoloop flag */
    	ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
    
    
    	/* Restore subroutine flag */
    	ast_set2_flag(ast_channel_flags(chan), saved_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
    
    
    	/* Restore non-hangup softhangup flags. */
    	if (saved_hangup_flags) {
    		ast_softhangup_nolock(chan, saved_hangup_flags);
    	}
    
    	ast_channel_unlock(chan);
    
    	return res;
    }
    
    
    static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
    
    	int old_priority;
    	const char *old_context;
    	const char *old_extension;
    
    	char *gosub_args;
    
    	if (argc < 4 || argc > 5) {
    		return RESULT_SHOWUSAGE;
    	}
    
    	ast_debug(1, "Gosub called with %d arguments: 0:%s 1:%s 2:%s 3:%s 4:%s\n", argc, argv[0], argv[1], argv[2], argv[3], argc == 5 ? argv[4] : "");
    
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
    
    		priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
    
    			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
    
    		if (priority < 0) {
    
    			ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
    
    			ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
    
    	} else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
    
    		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
    
    		ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
    
    		if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
    
    		if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
    
    	if (!gosub_args) {
    		ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
    		return RESULT_FAILURE;
    	}
    
    	ast_channel_lock(chan);
    
    	ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
    
    	/* Save autoloop flag */
    	old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
    	ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
    
    
    	/* Save subroutine flag */
    	old_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
    
    
    	/* Save previous location, since we're going to change it */
    	old_context = ast_strdupa(ast_channel_context(chan));
    	old_extension = ast_strdupa(ast_channel_exten(chan));
    	old_priority = ast_channel_priority(chan);
    
    	ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
    		old_context, old_extension, old_priority);
    	ast_channel_unlock(chan);
    
    	res = gosub_exec(chan, gosub_args);
    	if (!res) {
    		struct ast_datastore *stack_store;
    
    		/* Mark the return location as special. */
    		ast_channel_lock(chan);
    		stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
    		if (!stack_store) {
    			/* Should never happen! */
    			ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
    			res = -1;
    		} else {
    			struct gosub_stack_list *oldlist;
    
    			struct gosub_stack_frame *cur;
    
    			oldlist = stack_store->data;
    			cur = AST_LIST_FIRST(oldlist);
    
    			cur->is_special = 1;
    		}
    		ast_channel_unlock(chan);
    	}
    	if (!res) {
    		struct ast_pbx *pbx;
    		struct ast_pbx_args args;
    		int abnormal_exit;
    
    		memset(&args, 0, sizeof(args));
    		args.no_hangup_chan = 1;
    
    		ast_channel_lock(chan);
    
    		/* Next dialplan priority. */
    		ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
    
    		/* Suppress warning about PBX already existing */
    		pbx = ast_channel_pbx(chan);
    		ast_channel_pbx_set(chan, NULL);
    		ast_channel_unlock(chan);
    
    		ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
    		ast_pbx_run_args(chan, &args);
    
    		ast_channel_lock(chan);
    		ast_free(ast_channel_pbx(chan));
    		ast_channel_pbx_set(chan, pbx);
    
    		/* Did the routine return? */
    		if (ast_channel_priority(chan) == old_priority
    			&& !strcmp(ast_channel_context(chan), old_context)
    			&& !strcmp(ast_channel_exten(chan), old_extension)) {
    			ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
    				ast_channel_name(chan), app_gosub, gosub_args,
    				S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
    			abnormal_exit = 0;
    
    			ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit.  Popping routine return locations.\n",
    				ast_channel_name(chan), app_gosub, gosub_args);
    			balance_stack(chan);
    			pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
    			abnormal_exit = 1;
    
    		ast_channel_unlock(chan);
    
    		ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
    			abnormal_exit ? " (abnormal exit)" : "");
    
    		ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
    
    
    	ast_channel_lock(chan);
    	ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
    		ast_channel_context(chan), ast_channel_exten(chan),
    		ast_channel_priority(chan));
    
    
    	ast_channel_context_set(chan, old_context);
    	ast_channel_exten_set(chan, old_extension);
    
    	ast_channel_priority_set(chan, old_priority);
    
    	/* Restore autoloop flag */
    	ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
    
    
    	/* Restore subroutine flag */
    	ast_set2_flag(ast_channel_flags(chan), old_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
    
    static struct agi_command gosub_agi_command =
    
    	{ { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
    
    static int unload_module(void)
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    {
    
    	ast_agi_unregister(&gosub_agi_command);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	ast_unregister_application(app_return);
    	ast_unregister_application(app_pop);
    	ast_unregister_application(app_gosubif);
    	ast_unregister_application(app_gosub);
    
    	ast_custom_function_unregister(&local_function);
    
    	ast_custom_function_unregister(&peek_function);
    
    	ast_custom_function_unregister(&stackpeek_function);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	return 0;
    }
    
    
    static int load_module(void)
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    {
    
    	/* Setup the stack application callback functions. */
    	static struct ast_app_stack_funcs funcs = {
    		.run_sub = gosub_run,
    		.expand_sub_args = expand_gosub_args,
    	};
    
    	ast_agi_register(ast_module_info->self, &gosub_agi_command);
    
    	ast_register_application_xml(app_pop, pop_exec);
    	ast_register_application_xml(app_return, return_exec);
    	ast_register_application_xml(app_gosubif, gosubif_exec);
    	ast_register_application_xml(app_gosub, gosub_exec);
    
    	ast_custom_function_register(&local_function);
    
    	ast_custom_function_register(&peek_function);
    
    	ast_custom_function_register(&stackpeek_function);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    	funcs.module = ast_module_info->self,
    	ast_install_stack_functions(&funcs);
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	return 0;
    }
    
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
    
    	.support_level = AST_MODULE_SUPPORT_CORE,
    	.load = load_module,
    	.unload = unload_module,
    	.load_pri = AST_MODPRI_APP_DEPEND,
    
    	.optional_modules = "res_agi",