c899bd2202fd0bfbfa5b7b39476de75a45d5aaf4
[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(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", chan->context, chan->exten,
145                 chan->priority,
146                 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""));
147         if (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
148                 S_COR(chan->caller.id.number.valid, chan->caller.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_channel_lock(chan);
154         ast_party_id_copy(&caller_id, &chan->caller.id);
155         ast_channel_unlock(chan);
156
157         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
158         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
159
160         res = ast_masq_park_call(chan, NULL, timeout, &lot);
161         if (res) {
162                 /* Parking failed. */
163                 ast_party_id_free(&caller_id);
164                 res = -1;
165                 goto parkcleanup;
166         }
167
168         ast_verb(3, "Call parked in space: %d, timeout: %d, return-context: %s\n",
169                 lot, timeout, args.return_context ? args.return_context : "");
170
171         /* Now place the call to the extension */
172
173         snprintf(buf, sizeof(buf), "%d", lot);
174         oh.parent_channel = chan;
175         oh.vars = ast_variable_new("_PARKEDAT", buf, "");
176         dchan = __ast_request_and_dial(dialtech, cap_slin, chan, args.dial, 30000,
177                 &outstate,
178                 S_COR(caller_id.number.valid, caller_id.number.str, NULL),
179                 S_COR(caller_id.name.valid, caller_id.name.str, NULL),
180                 &oh);
181         ast_variables_destroy(oh.vars);
182         ast_party_id_free(&caller_id);
183         if (dchan) {
184                 if (dchan->_state == AST_STATE_UP) {
185                         ast_verb(4, "Channel %s was answered.\n", dchan->name);
186                 } else {
187                         ast_verb(4, "Channel %s was never answered.\n", dchan->name);
188                         ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
189                         ast_hangup(dchan);
190                         res = -1;
191                         goto parkcleanup;
192                 }
193         } else {
194                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
195                 res = -1;
196                 goto parkcleanup;
197         }
198
199         ast_stopstream(dchan);
200
201         /* now we have the call placed and are ready to play stuff to it */
202
203         ast_verb(4, "Announce Template:%s\n", args.template);
204
205         for (looptemp = 0; looptemp < ARRAY_LEN(tmp); looptemp++) {
206                 if ((tmp[looptemp] = strsep(&args.template, ":")) != NULL)
207                         continue;
208                 else
209                         break;
210         }
211
212         for (i = 0; i < looptemp; i++) {
213                 ast_verb(4, "Announce:%s\n", tmp[i]);
214                 if (!strcmp(tmp[i], "PARKED")) {
215                         ast_say_digits(dchan, lot, "", dchan->language);
216                 } else {
217                         dres = ast_streamfile(dchan, tmp[i], dchan->language);
218                         if (!dres) {
219                                 dres = ast_waitstream(dchan, "");
220                         } else {
221                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
222                         }
223                 }
224         }
225
226         ast_stopstream(dchan);  
227         ast_hangup(dchan);
228
229 parkcleanup:
230         cap_slin = ast_format_cap_destroy(cap_slin);
231
232         return res;
233 }
234
235 static int unload_module(void)
236 {
237         return ast_unregister_application(app);
238 }
239
240 static int load_module(void)
241 {
242         /* return ast_register_application(app, park_exec); */
243         return ast_register_application_xml(app, parkandannounce_exec);
244 }
245
246 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");