Don't do SIP contact/route DNS if we're not using the result.
[asterisk/asterisk.git] / apps / app_parkandannounce.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Author: Ben Miller <bgmiller@dccinc.com>
9  *    With TONS of help from Mark!
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  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief ParkAndAnnounce application for Asterisk
25  *
26  * \author Ben Miller <bgmiller@dccinc.com>
27  * \arg With TONS of help from Mark!
28  *
29  * \ingroup applications
30  */
31
32 /*** MODULEINFO
33         <support_level>core</support_level>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/features.h"
45 #include "asterisk/say.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/utils.h"
48 #include "asterisk/app.h"
49
50 /*** DOCUMENTATION
51         <application name="ParkAndAnnounce" language="en_US">
52                 <synopsis>
53                         Park and Announce.
54                 </synopsis>
55                 <syntax>
56                         <parameter name="announce_template" required="true" argsep=":">
57                                 <argument name="announce" required="true">
58                                         <para>Colon-separated list of files to announce. The word
59                                         <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
60                                         the call is parked.</para>
61                                 </argument>
62                                 <argument name="announce1" multiple="true" />
63                         </parameter>
64                         <parameter name="timeout" required="true">
65                                 <para>Time in seconds before the call returns into the return
66                                 context.</para>
67                         </parameter>
68                         <parameter name="dial" required="true">
69                                 <para>The app_dial style resource to call to make the
70                                 announcement. Console/dsp calls the console.</para>
71                         </parameter>
72                         <parameter name="return_context">
73                                 <para>The goto-style label to jump the call back into after
74                                 timeout. Default <literal>priority+1</literal>.</para>
75                         </parameter>
76                 </syntax>
77                 <description>
78                         <para>Park a call into the parkinglot and announce the call to another channel.</para>
79                         <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
80                         into which the call was placed.  Use with the Local channel to allow the dialplan to make
81                         use of this information.</para>
82                 </description>
83                 <see-also>
84                         <ref type="application">Park</ref>
85                         <ref type="application">ParkedCall</ref>
86                 </see-also>
87         </application>
88  ***/
89
90 static char *app = "ParkAndAnnounce";
91
92 static int parkandannounce_exec(struct ast_channel *chan, const char *data)
93 {
94         int res = -1;
95         int lot, timeout = 0, dres;
96         char *dialtech, *tmp[100], buf[13];
97         int looptemp, i;
98         char *s;
99         struct ast_party_id caller_id;
100
101         struct ast_channel *dchan;
102         struct outgoing_helper oh = { 0, };
103         int outstate;
104         struct ast_format tmpfmt;
105         struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
106
107         AST_DECLARE_APP_ARGS(args,
108                 AST_APP_ARG(template);
109                 AST_APP_ARG(timeout);
110                 AST_APP_ARG(dial);
111                 AST_APP_ARG(return_context);
112         );
113         if (ast_strlen_zero(data)) {
114                 ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce_template,timeout,dial,[return_context])\n");
115                 res = -1;
116                 goto parkcleanup;
117         }
118         if (!cap_slin) {
119                 res = -1;
120                 goto parkcleanup;
121         }
122         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
123
124         s = ast_strdupa(data);
125         AST_STANDARD_APP_ARGS(args, s);
126
127         if (args.timeout)
128                 timeout = atoi(args.timeout) * 1000;
129
130         if (ast_strlen_zero(args.dial)) {
131                 ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or DAHDI/g1/5551212\n");
132                 res = -1;
133                 goto parkcleanup;
134         }
135
136         dialtech = strsep(&args.dial, "/");
137         ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
138
139         if (!ast_strlen_zero(args.return_context)) {
140                 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
141                 ast_parseable_goto(chan, args.return_context);
142         }
143
144         ast_verb(3, "Return Context: (%s,%s,%d) ID: %s\n", ast_channel_context(chan), ast_channel_exten(chan),
145                 ast_channel_priority(chan),
146                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""));
147         if (!ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
148                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
149                 ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
150         }
151
152         /* Save the CallerID because the masquerade turns chan into a ZOMBIE. */
153         ast_party_id_init(&caller_id);
154         ast_channel_lock(chan);
155         ast_party_id_copy(&caller_id, &ast_channel_caller(chan)->id);
156         ast_channel_unlock(chan);
157
158         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
159         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
160
161         res = ast_masq_park_call(chan, NULL, timeout, &lot);
162         if (res) {
163                 /* Parking failed. */
164                 ast_party_id_free(&caller_id);
165                 res = -1;
166                 goto parkcleanup;
167         }
168
169         ast_verb(3, "Call parked in space: %d, timeout: %d, return-context: %s\n",
170                 lot, timeout, args.return_context ? args.return_context : "");
171
172         /* Now place the call to the extension */
173
174         snprintf(buf, sizeof(buf), "%d", lot);
175         oh.parent_channel = chan;
176         oh.vars = ast_variable_new("_PARKEDAT", buf, "");
177         dchan = __ast_request_and_dial(dialtech, cap_slin, chan, args.dial, 30000,
178                 &outstate,
179                 S_COR(caller_id.number.valid, caller_id.number.str, NULL),
180                 S_COR(caller_id.name.valid, caller_id.name.str, NULL),
181                 &oh);
182         ast_variables_destroy(oh.vars);
183         ast_party_id_free(&caller_id);
184         if (dchan) {
185                 if (ast_channel_state(dchan) == AST_STATE_UP) {
186                         ast_verb(4, "Channel %s was answered.\n", ast_channel_name(dchan));
187                 } else {
188                         ast_verb(4, "Channel %s was never answered.\n", ast_channel_name(dchan));
189                         ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", ast_channel_name(dchan));
190                         ast_hangup(dchan);
191                         res = -1;
192                         goto parkcleanup;
193                 }
194         } else {
195                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
196                 res = -1;
197                 goto parkcleanup;
198         }
199
200         ast_stopstream(dchan);
201
202         /* now we have the call placed and are ready to play stuff to it */
203
204         ast_verb(4, "Announce Template:%s\n", args.template);
205
206         for (looptemp = 0; looptemp < ARRAY_LEN(tmp); looptemp++) {
207                 if ((tmp[looptemp] = strsep(&args.template, ":")) != NULL)
208                         continue;
209                 else
210                         break;
211         }
212
213         for (i = 0; i < looptemp; i++) {
214                 ast_verb(4, "Announce:%s\n", tmp[i]);
215                 if (!strcmp(tmp[i], "PARKED")) {
216                         ast_say_digits(dchan, lot, "", ast_channel_language(dchan));
217                 } else {
218                         dres = ast_streamfile(dchan, tmp[i], ast_channel_language(dchan));
219                         if (!dres) {
220                                 dres = ast_waitstream(dchan, "");
221                         } else {
222                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], ast_channel_name(dchan));
223                         }
224                 }
225         }
226
227         ast_stopstream(dchan);  
228         ast_hangup(dchan);
229
230 parkcleanup:
231         cap_slin = ast_format_cap_destroy(cap_slin);
232
233         return res;
234 }
235
236 static int unload_module(void)
237 {
238         return ast_unregister_application(app);
239 }
240
241 static int load_module(void)
242 {
243         /* return ast_register_application(app, park_exec); */
244         return ast_register_application_xml(app, parkandannounce_exec);
245 }
246
247 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");