Fix handling of notification calls w/ the dialing api
[asterisk/asterisk.git] / apps / app_originate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Roberto Casas.
5  * Copyright (C) 2008, Digium, Inc.
6  *
7  * Roberto Casas <roberto.casas@diaple.com>
8  * Russell Bryant <russell@digium.com>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*!
22  * \file
23  * \brief Originate application
24  *
25  * \author Roberto Casas <roberto.casas@diaple.com>
26  * \author Russell Bryant <russell@digium.com>
27  *
28  * \ingroup applications
29  *
30  * \todo Make a way to be able to set variables (and functions) on the outbound
31  *       channel, similar to the Variable headers for the AMI Originate, and the
32  *       Set options for call files.
33  */
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/app.h"
44 #include "asterisk/dial.h"
45
46 static const char app_originate[] = "Originate";
47
48 /*** DOCUMENTATION
49         <application name="Originate" language="en_US">
50                 <synopsis>
51                         Originate a call.
52                 </synopsis>
53                 <syntax>
54                         <parameter name="tech_data" required="true">
55                                 <para>Channel technology and data for creating the outbound channel.
56                       For example, SIP/1234.</para>
57                         </parameter>
58                         <parameter name="type" required="true">
59                                 <para>This should be <literal>app</literal> or <literal>exten</literal>, depending on whether the outbound channel should be connected to an application or extension.</para>
60                         </parameter>
61                         <parameter name="arg1" required="true">
62                                 <para>If the type is <literal>app</literal>, then this is the application name.  If the type is <literal>exten</literal>, then this is the context that the channel will be sent to.</para>
63                         </parameter>
64                         <parameter name="arg2" required="false">
65                                 <para>If the type is <literal>app</literal>, then this is the data passed as arguments to the application.  If the type is <literal>exten</literal>, then this is the extension that the channel will be sent to.</para>
66                         </parameter>
67                         <parameter name="arg3" required="false">
68                                 <para>If the type is <literal>exten</literal>, then this is the priority that the channel is sent to.  If the type is <literal>app</literal>, then this parameter is ignored.</para>
69                         </parameter>
70                 </syntax>
71                 <description>
72                 <para>This application originates an outbound call and connects it to a specified extension or application.  This application will block until the outgoing call fails or gets answered.  At that point, this application will exit with the status variable set and dialplan processing will continue.</para>
73
74                 <para>This application sets the following channel variable before exiting:</para>
75                 <variablelist>
76                         <variable name="ORIGINATE_STATUS">
77                                 <para>This indicates the result of the call origination.</para>
78                                 <value name="FAILED"/>
79                                 <value name="SUCCESS"/>
80                                 <value name="BUSY"/>
81                                 <value name="CONGESTION"/>
82                                 <value name="HANGUP"/>
83                                 <value name="RINGING"/>
84                                 <value name="UNKNOWN">
85                                 In practice, you should never see this value.  Please report it to the issue tracker if you ever see it.
86                                 </value>
87                         </variable>
88                 </variablelist>
89                 </description>
90         </application>
91  ***/
92
93 static int originate_exec(struct ast_channel *chan, const char *data)
94 {
95         AST_DECLARE_APP_ARGS(args,
96                 AST_APP_ARG(tech_data);
97                 AST_APP_ARG(type);
98                 AST_APP_ARG(arg1);
99                 AST_APP_ARG(arg2);
100                 AST_APP_ARG(arg3);
101         );
102         char *parse;
103         char *chantech, *chandata;
104         int res = -1;
105         int outgoing_res = 0;
106         int outgoing_status = 0;
107         static const unsigned int timeout = 30;
108         static const char default_exten[] = "s";
109         struct ast_dial *dial = NULL;
110         struct ast_str *buf = NULL;
111         struct ast_channel *c = NULL;
112
113         ast_autoservice_start(chan);
114
115         if (ast_strlen_zero(data)) {
116                 ast_log(LOG_ERROR, "Originate() requires arguments\n");
117                 goto return_cleanup;
118         }
119
120         parse = ast_strdupa(data);
121
122         AST_STANDARD_APP_ARGS(args, parse);
123
124         if (args.argc < 3) {
125                 ast_log(LOG_ERROR, "Incorrect number of arguments\n");
126                 goto return_cleanup;
127         }
128
129         chandata = ast_strdupa(args.tech_data);
130         chantech = strsep(&chandata, "/");
131
132         if (ast_strlen_zero(chandata) || ast_strlen_zero(chantech)) {
133                 ast_log(LOG_ERROR, "Channel Tech/Data invalid: '%s'\n", args.tech_data);
134                 goto return_cleanup;
135         }
136
137         if (strstr(args.type, "async")) {
138                 if (!(dial = ast_dial_create())) {
139                         goto return_cleanup;
140                 }
141
142                 if (ast_dial_append(dial, chantech, chandata)) {
143                         goto return_cleanup;
144                 }
145
146                 if (!(buf = ast_str_create(32))) {
147                         goto return_cleanup;
148                 }
149
150                 if (!(c = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, 0, "Originate/%s-%08lx", args.arg1, ast_random()))) {
151                         ast_free(buf);
152                         goto return_cleanup;
153                 }
154
155                 c->nativeformats = AST_FORMAT_SLINEAR;
156                 ast_dial_set_global_timeout(dial, 30 * 1000);
157
158         }
159
160         if (!strncasecmp(args.type, "exten", 5)) {
161                 int priority = 1; /* Initialized in case priority not specified */
162                 const char *exten = args.arg2;
163
164                 if (args.argc == 5) {
165                         /* Context/Exten/Priority all specified */
166                         if (sscanf(args.arg3, "%30d", &priority) != 1) {
167                                 ast_log(LOG_ERROR, "Invalid priority: '%s'\n", args.arg3);
168                                 goto return_cleanup;
169                         }
170                 } else if (args.argc == 3) {
171                         /* Exten not specified */
172                         exten = default_exten;
173                 }
174
175                 ast_debug(1, "Originating call to '%s/%s' and connecting them to extension %s,%s,%d\n",
176                                 chantech, chandata, args.arg1, exten, priority);
177
178                 if (!strcasecmp(args.type, "exten-async")) {
179                         ast_str_set(&buf, 0, "Dial,Local/%s@%s", exten, args.arg1);
180                         ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, ast_str_buffer(buf));
181                         ast_dial_run(dial, NULL, 1);
182                 } else {
183                         outgoing_res = ast_pbx_outgoing_exten(chantech, AST_FORMAT_SLINEAR, chandata,
184                                         timeout * 1000, args.arg1, exten, priority, &outgoing_status, 0, NULL,
185                                         NULL, NULL, NULL, NULL);
186                 }
187         } else if (!strncasecmp(args.type, "app", 3)) {
188                 ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
189                                 chantech, chandata, args.arg1, S_OR(args.arg2, ""));
190
191                 if (!strcasecmp(args.type, "app-async")) {
192                         ast_str_set(&buf, 0, "%s,%s", args.arg1, args.arg2);
193                         ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, ast_str_buffer(buf));
194                         ast_dial_run(dial, c, 1);
195                 } else {
196                         outgoing_res = ast_pbx_outgoing_app(chantech, AST_FORMAT_SLINEAR, chandata,
197                                         timeout * 1000, args.arg1, args.arg2, &outgoing_status, 0, NULL,
198                                         NULL, NULL, NULL, NULL);
199                 }
200         } else {
201                 ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n",
202                                 args.type);
203                 goto return_cleanup;
204         }
205
206         res = 0;
207
208 return_cleanup:
209         if (res) {
210                 pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "FAILED");
211         } else {
212                 switch (outgoing_status) {
213                 case 0:
214                 case AST_CONTROL_ANSWER:
215                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "SUCCESS");
216                         break;
217                 case AST_CONTROL_BUSY:
218                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "BUSY");
219                         break;
220                 case AST_CONTROL_CONGESTION:
221                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "CONGESTION");
222                         break;
223                 case AST_CONTROL_HANGUP:
224                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "HANGUP");
225                         break;
226                 case AST_CONTROL_RINGING:
227                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "RINGING");
228                         break;
229                 default:
230                         ast_log(LOG_WARNING, "Unknown originate status result of '%d'\n",
231                                         outgoing_status);
232                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "UNKNOWN");
233                         break;
234                 }
235         }
236         if (buf) {
237                 ast_free(buf);
238         }
239         if (c) {
240                 ast_channel_release(c);
241         }
242
243         ast_autoservice_stop(chan);
244
245         return res;
246 }
247
248 static int unload_module(void)
249 {
250         return ast_unregister_application(app_originate);
251 }
252
253 static int load_module(void)
254 {
255         int res;
256
257         res = ast_register_application_xml(app_originate, originate_exec);
258
259         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
260 }
261
262 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Originate call");