9fceb08498e9904f2e783fc9421f0ebf7ac63e70
[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 /*** MODULEINFO
36         <support_level>core</support_level>
37  ***/
38
39 #include "asterisk.h"
40
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/app.h"
48
49 static const char app_originate[] = "Originate";
50
51 /*** DOCUMENTATION
52         <application name="Originate" language="en_US">
53                 <synopsis>
54                         Originate a call.
55                 </synopsis>
56                 <syntax>
57                         <parameter name="tech_data" required="true">
58                                 <para>Channel technology and data for creating the outbound channel.
59                       For example, SIP/1234.</para>
60                         </parameter>
61                         <parameter name="type" required="true">
62                                 <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>
63                         </parameter>
64                         <parameter name="arg1" required="true">
65                                 <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>
66                         </parameter>
67                         <parameter name="arg2" required="false">
68                                 <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>
69                         </parameter>
70                         <parameter name="arg3" required="false">
71                                 <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>
72                         </parameter>
73                         <parameter name="timeout" required="false">
74                                 <para>Timeout in seconds. Default is 30 seconds.</para>
75                         </parameter>
76                 </syntax>
77                 <description>
78                 <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>
79
80                 <para>This application sets the following channel variable before exiting:</para>
81                 <variablelist>
82                         <variable name="ORIGINATE_STATUS">
83                                 <para>This indicates the result of the call origination.</para>
84                                 <value name="FAILED"/>
85                                 <value name="SUCCESS"/>
86                                 <value name="BUSY"/>
87                                 <value name="CONGESTION"/>
88                                 <value name="HANGUP"/>
89                                 <value name="RINGING"/>
90                                 <value name="UNKNOWN">
91                                 In practice, you should never see this value.  Please report it to the issue tracker if you ever see it.
92                                 </value>
93                         </variable>
94                 </variablelist>
95                 </description>
96         </application>
97  ***/
98
99 static int originate_exec(struct ast_channel *chan, const char *data)
100 {
101         AST_DECLARE_APP_ARGS(args,
102                 AST_APP_ARG(tech_data);
103                 AST_APP_ARG(type);
104                 AST_APP_ARG(arg1);
105                 AST_APP_ARG(arg2);
106                 AST_APP_ARG(arg3);
107                 AST_APP_ARG(timeout);
108         );
109         char *parse;
110         char *chantech, *chandata;
111         int res = -1;
112         int outgoing_status = 0;
113         unsigned int timeout = 30;
114         static const char default_exten[] = "s";
115         struct ast_format tmpfmt;
116         struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
117
118         ast_autoservice_start(chan);
119         if (!cap_slin) {
120                 goto return_cleanup;
121         }
122         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
123         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR12, 0));
124         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0));
125         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR24, 0));
126         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR32, 0));
127         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR44, 0));
128         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR48, 0));
129         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR96, 0));
130         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR192, 0));
131
132         if (ast_strlen_zero(data)) {
133                 ast_log(LOG_ERROR, "Originate() requires arguments\n");
134                 goto return_cleanup;
135         }
136
137         parse = ast_strdupa(data);
138
139         AST_STANDARD_APP_ARGS(args, parse);
140
141         if (args.argc < 3) {
142                 ast_log(LOG_ERROR, "Incorrect number of arguments\n");
143                 goto return_cleanup;
144         }
145
146         if (!ast_strlen_zero(args.timeout)) {
147                 if(sscanf(args.timeout, "%u", &timeout) != 1) {
148                         ast_log(LOG_NOTICE, "Invalid timeout: '%s'. Setting timeout to 30 seconds\n", args.timeout);
149                         timeout = 30;
150                 }
151         }
152
153         chandata = ast_strdupa(args.tech_data);
154         chantech = strsep(&chandata, "/");
155
156         if (ast_strlen_zero(chandata) || ast_strlen_zero(chantech)) {
157                 ast_log(LOG_ERROR, "Channel Tech/Data invalid: '%s'\n", args.tech_data);
158                 goto return_cleanup;
159         }
160
161         if (!strcasecmp(args.type, "exten")) {
162                 int priority = 1; /* Initialized in case priority not specified */
163                 const char *exten = args.arg2;
164
165                 if (args.argc == 5) {
166                         /* Context/Exten/Priority all specified */
167                         if (sscanf(args.arg3, "%30d", &priority) != 1) {
168                                 ast_log(LOG_ERROR, "Invalid priority: '%s'\n", args.arg3);
169                                 goto return_cleanup;
170                         }
171                 } else if (args.argc == 3) {
172                         /* Exten not specified */
173                         exten = default_exten;
174                 }
175
176                 ast_debug(1, "Originating call to '%s/%s' and connecting them to extension %s,%s,%d\n",
177                                 chantech, chandata, args.arg1, exten, priority);
178
179                 ast_pbx_outgoing_exten(chantech, cap_slin, chandata,
180                                 timeout * 1000, args.arg1, exten, priority, &outgoing_status, 1, NULL,
181                                 NULL, NULL, NULL, NULL, 0, NULL);
182         } else if (!strcasecmp(args.type, "app")) {
183                 ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
184                                 chantech, chandata, args.arg1, S_OR(args.arg2, ""));
185
186                 ast_pbx_outgoing_app(chantech, cap_slin, chandata,
187                                 timeout * 1000, args.arg1, args.arg2, &outgoing_status, 1, NULL,
188                                 NULL, NULL, NULL, NULL, NULL);
189         } else {
190                 ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n",
191                                 args.type);
192                 goto return_cleanup;
193         }
194
195         res = 0;
196
197 return_cleanup:
198         if (res) {
199                 pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "FAILED");
200         } else {
201                 switch (outgoing_status) {
202                 case 0:
203                 case AST_CONTROL_ANSWER:
204                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "SUCCESS");
205                         break;
206                 case AST_CONTROL_BUSY:
207                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "BUSY");
208                         break;
209                 case AST_CONTROL_CONGESTION:
210                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "CONGESTION");
211                         break;
212                 case AST_CONTROL_HANGUP:
213                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "HANGUP");
214                         break;
215                 case AST_CONTROL_RINGING:
216                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "RINGING");
217                         break;
218                 default:
219                         ast_log(LOG_WARNING, "Unknown originate status result of '%d'\n",
220                                         outgoing_status);
221                         pbx_builtin_setvar_helper(chan, "ORIGINATE_STATUS", "UNKNOWN");
222                         break;
223                 }
224         }
225         cap_slin = ast_format_cap_destroy(cap_slin);
226         ast_autoservice_stop(chan);
227
228         return res;
229 }
230
231 static int unload_module(void)
232 {
233         return ast_unregister_application(app_originate);
234 }
235
236 static int load_module(void)
237 {
238         int res;
239
240         res = ast_register_application_xml(app_originate, originate_exec);
241
242         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
243 }
244
245 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Originate call");