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