Because ExecIf needs to reprocess arguments, it's best if we don't remove quotes...
[asterisk/asterisk.git] / apps / app_exec.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (c) 2004 - 2005, Tilghman Lesher.  All rights reserved.
5  * Portions copyright (c) 2006, Philipp Dunkel.
6  *
7  * Tilghman Lesher <app_exec__v002@the-tilghman.com>
8  *
9  * This code is released by the author with no restrictions on usage.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  */
18
19 /*! \file
20  *
21  * \brief Exec application
22  *
23  * \author Tilghman Lesher <app_exec__v002@the-tilghman.com>
24  * \author Philipp Dunkel <philipp.dunkel@ebox.at>
25  *
26  * \ingroup applications
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/file.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/module.h"
37 #include "asterisk/app.h"
38
39 /*** DOCUMENTATION
40         <application name="Exec" language="en_US">
41                 <synopsis>
42                         Executes dialplan application.
43                 </synopsis>
44                 <syntax>
45                         <parameter name="appname" required="true" hasparams="true">
46                                 <para>Application name and arguments of the dialplan application to execute.</para>
47                                 <argument name="arguments" required="true" />
48                         </parameter>
49                 </syntax>
50                 <description>
51                         <para>Allows an arbitrary application to be invoked even when not
52                         hard coded into the dialplan.  If the underlying application
53                         terminates the dialplan, or if the application cannot be found,
54                         Exec will terminate the dialplan.</para>
55                         <para>To invoke external applications, see the application System.
56                         If you would like to catch any error instead, see TryExec.</para>
57                 </description>
58         </application>
59         <application name="TryExec" language="en_US">
60                 <synopsis>
61                         Executes dialplan application, always returning.
62                 </synopsis>
63                 <syntax>
64                         <parameter name="appname" required="true" hasparams="true">
65                                 <argument name="arguments" required="true" />
66                         </parameter>
67                 </syntax>
68                 <description>
69                         <para>Allows an arbitrary application to be invoked even when not
70                         hard coded into the dialplan. To invoke external applications
71                         see the application System.  Always returns to the dialplan.
72                         The channel variable TRYSTATUS will be set to one of:
73                         </para>
74                         <variablelist>
75                                 <variable name="TRYSTATUS">
76                                         <value name="SUCCESS">
77                                                 If the application returned zero.
78                                         </value>
79                                         <value name="FAILED">
80                                                 If the application returned non-zero.
81                                         </value>
82                                         <value name="NOAPP">
83                                                 If the application was not found or was not specified.
84                                         </value>
85                                 </variable>
86                         </variablelist>
87                 </description>
88         </application>
89         <application name="ExecIf" language="en_US">
90                 <synopsis>
91                         Executes dialplan application, conditionally.
92                 </synopsis>
93                 <syntax argsep="?">
94                         <parameter name="expression" required="true" />
95                         <parameter name="execapp" required="true" argsep=":">
96                                 <argument name="appiftrue" required="true" hasparams="true">
97                                         <argument name="args" required="true" />
98                                 </argument>
99                                 <argument name="appiffalse" required="false" hasparams="true">
100                                         <argument name="args" required="true" />
101                                 </argument>
102                         </parameter>
103                 </syntax>
104                 <description>
105                         <para>If <replaceable>expr</replaceable> is true, execute and return the
106                         result of <replaceable>appiftrue(args)</replaceable>.</para>
107                         <para>If <replaceable>expr</replaceable> is true, but <replaceable>appiftrue</replaceable> is not found,
108                         then the application will return a non-zero value.</para>
109                 </description>
110         </application>
111  ***/
112
113 /* Maximum length of any variable */
114 #define MAXRESULT 1024
115
116 /*! Note
117  *
118  * The key difference between these two apps is exit status.  In a
119  * nutshell, Exec tries to be transparent as possible, behaving
120  * in exactly the same way as if the application it calls was
121  * directly invoked from the dialplan.
122  *
123  * TryExec, on the other hand, provides a way to execute applications
124  * and catch any possible fatal error without actually fatally
125  * affecting the dialplan.
126  */
127
128 static const char app_exec[] = "Exec";
129 static const char app_tryexec[] = "TryExec";
130 static const char app_execif[] = "ExecIf";
131
132 static int exec_exec(struct ast_channel *chan, const char *data)
133 {
134         int res = 0;
135         char *s, *appname, *endargs;
136         struct ast_app *app;
137         struct ast_str *args = NULL;
138
139         if (ast_strlen_zero(data))
140                 return 0;
141
142         s = ast_strdupa(data);
143         appname = strsep(&s, "(");
144         if (s) {
145                 endargs = strrchr(s, ')');
146                 if (endargs)
147                         *endargs = '\0';
148                 if ((args = ast_str_create(16))) {
149                         ast_str_substitute_variables(&args, 0, chan, s);
150                 }
151         }
152         if (appname) {
153                 app = pbx_findapp(appname);
154                 if (app) {
155                         res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL);
156                 } else {
157                         ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
158                         res = -1;
159                 }
160         }
161
162         ast_free(args);
163         return res;
164 }
165
166 static int tryexec_exec(struct ast_channel *chan, const char *data)
167 {
168         int res = 0;
169         char *s, *appname, *endargs;
170         struct ast_app *app;
171         struct ast_str *args = NULL;
172
173         if (ast_strlen_zero(data))
174                 return 0;
175
176         s = ast_strdupa(data);
177         appname = strsep(&s, "(");
178         if (s) {
179                 endargs = strrchr(s, ')');
180                 if (endargs)
181                         *endargs = '\0';
182                 if ((args = ast_str_create(16))) {
183                         ast_str_substitute_variables(&args, 0, chan, s);
184                 }
185         }
186         if (appname) {
187                 app = pbx_findapp(appname);
188                 if (app) {
189                         res = pbx_exec(chan, app, args ? ast_str_buffer(args) : NULL);
190                         pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS");
191                 } else {
192                         ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
193                         pbx_builtin_setvar_helper(chan, "TRYSTATUS", "NOAPP");
194                 }
195         }
196
197         ast_free(args);
198         return 0;
199 }
200
201 static int execif_exec(struct ast_channel *chan, const char *data)
202 {
203         int res = 0;
204         char *truedata = NULL, *falsedata = NULL, *end, *firstcomma, *firstquestion;
205         struct ast_app *app = NULL;
206         AST_DECLARE_APP_ARGS(expr,
207                 AST_APP_ARG(expr);
208                 AST_APP_ARG(remainder);
209         );
210         AST_DECLARE_APP_ARGS(apps,
211                 AST_APP_ARG(t);
212                 AST_APP_ARG(f);
213         );
214         char *parse = ast_strdupa(data);
215
216         firstcomma = strchr(parse, ',');
217         firstquestion = strchr(parse, '?');
218
219         if ((firstcomma != NULL && firstquestion != NULL && firstcomma < firstquestion) || (firstquestion == NULL)) {
220                 /* Deprecated syntax */
221                 AST_DECLARE_APP_ARGS(depr,
222                         AST_APP_ARG(expr);
223                         AST_APP_ARG(appname);
224                         AST_APP_ARG(appargs);
225                 );
226                 AST_STANDARD_APP_ARGS(depr, parse);
227
228                 ast_log(LOG_WARNING, "Deprecated syntax found.  Please upgrade to using ExecIf(<expr>?%s(%s))\n", depr.appname, depr.appargs);
229
230                 /* Make the two syntaxes look the same */
231                 expr.expr = depr.expr;
232                 apps.t = depr.appname;
233                 apps.f = NULL;
234                 truedata = depr.appargs;
235         } else {
236                 /* Preferred syntax */
237
238                 AST_NONSTANDARD_RAW_ARGS(expr, parse, '?');
239                 if (ast_strlen_zero(expr.remainder)) {
240                         ast_log(LOG_ERROR, "Usage: ExecIf(<expr>?<appiftrue>(<args>)[:<appiffalse>(<args)])\n");
241                         return -1;
242                 }
243
244                 AST_NONSTANDARD_RAW_ARGS(apps, expr.remainder, ':');
245
246                 if (apps.t && (truedata = strchr(apps.t, '('))) {
247                         *truedata++ = '\0';
248                         if ((end = strrchr(truedata, ')'))) {
249                                 *end = '\0';
250                         }
251                 }
252
253                 if (apps.f && (falsedata = strchr(apps.f, '('))) {
254                         *falsedata++ = '\0';
255                         if ((end = strrchr(falsedata, ')'))) {
256                                 *end = '\0';
257                         }
258                 }
259         }
260
261         if (pbx_checkcondition(expr.expr)) {
262                 if (!ast_strlen_zero(apps.t) && (app = pbx_findapp(apps.t))) {
263                         res = pbx_exec(chan, app, S_OR(truedata, ""));
264                 } else {
265                         ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.t);
266                         res = -1;
267                 }
268         } else if (!ast_strlen_zero(apps.f)) {
269                 if ((app = pbx_findapp(apps.f))) {
270                         res = pbx_exec(chan, app, S_OR(falsedata, ""));
271                 } else {
272                         ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.f);
273                         res = -1;
274                 }
275         }
276
277         return res;
278 }
279
280 static int unload_module(void)
281 {
282         int res;
283
284         res = ast_unregister_application(app_exec);
285         res |= ast_unregister_application(app_tryexec);
286         res |= ast_unregister_application(app_execif);
287
288         return res;
289 }
290
291 static int load_module(void)
292 {
293         int res = ast_register_application_xml(app_exec, exec_exec);
294         res |= ast_register_application_xml(app_tryexec, tryexec_exec);
295         res |= ast_register_application_xml(app_execif, execif_exec);
296         return res;
297 }
298
299 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Executes dialplan applications");