5bb8b659465dc3164c727c47480a0681ed2e546f
[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 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/features.h"
41 #include "asterisk/say.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/utils.h"
44 #include "asterisk/app.h"
45
46 /*** DOCUMENTATION
47         <application name="ParkAndAnnounce" language="en_US">
48                 <synopsis>
49                         Park and Announce.
50                 </synopsis>
51                 <syntax>
52                         <parameter name="announce_template" required="true" argsep=":">
53                                 <argument name="announce" required="true">
54                                         <para>Colon-separated list of files to announce. The word
55                                         <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
56                                         the call is parked.</para>
57                                 </argument>
58                                 <argument name="announce1" multiple="true" />
59                         </parameter>
60                         <parameter name="timeout" required="true">
61                                 <para>Time in seconds before the call returns into the return
62                                 context.</para>
63                         </parameter>
64                         <parameter name="dial" required="true">
65                                 <para>The app_dial style resource to call to make the
66                                 announcement. Console/dsp calls the console.</para>
67                         </parameter>
68                         <parameter name="return_context">
69                                 <para>The goto-style label to jump the call back into after
70                                 timeout. Default <literal>priority+1</literal>.</para>
71                         </parameter>
72                 </syntax>
73                 <description>
74                         <para>Park a call into the parkinglot and announce the call to another channel.</para>
75                         <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
76                         into which the call was placed.  Use with the Local channel to allow the dialplan to make
77                         use of this information.</para>
78                 </description>
79                 <see-also>
80                         <ref type="application">Park</ref>
81                         <ref type="application">ParkedCall</ref>
82                 </see-also>
83         </application>
84  ***/
85
86 static char *app = "ParkAndAnnounce";
87
88 static int parkandannounce_exec(struct ast_channel *chan, const char *data)
89 {
90         int res = -1;
91         int lot, timeout = 0, dres;
92         char *dialtech, *tmp[100], buf[13];
93         int looptemp, i;
94         char *s;
95
96         struct ast_channel *dchan;
97         struct outgoing_helper oh = { 0, };
98         int outstate;
99         AST_DECLARE_APP_ARGS(args,
100                 AST_APP_ARG(template);
101                 AST_APP_ARG(timeout);
102                 AST_APP_ARG(dial);
103                 AST_APP_ARG(return_context);
104         );
105         if (ast_strlen_zero(data)) {
106                 ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
107                 return -1;
108         }
109   
110         s = ast_strdupa(data);
111         AST_STANDARD_APP_ARGS(args, s);
112
113         if (args.timeout)
114                 timeout = atoi(args.timeout) * 1000;
115
116         if (ast_strlen_zero(args.dial)) {
117                 ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or DAHDI/g1/5551212\n");
118                 return -1;
119         }
120
121         dialtech = strsep(&args.dial, "/");
122         ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
123
124         if (!ast_strlen_zero(args.return_context)) {
125                 ast_clear_flag(chan, AST_FLAG_IN_AUTOLOOP);
126                 ast_parseable_goto(chan, args.return_context);
127         }
128
129         ast_verb(3, "Return Context: (%s,%s,%d) ID: %s\n", chan->context, chan->exten,
130                 chan->priority,
131                 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""));
132         if (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
133                 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
134                 ast_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
135         }
136
137         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
138         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
139
140         res = ast_masq_park_call(chan, NULL, timeout, &lot);
141         if (res == -1)
142                 return res;
143
144         ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, args.return_context);
145
146         /* Now place the call to the extension */
147
148         snprintf(buf, sizeof(buf), "%d", lot);
149         oh.parent_channel = chan;
150         oh.vars = ast_variable_new("_PARKEDAT", buf, "");
151         dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, chan, args.dial, 30000,
152                 &outstate,
153                 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
154                 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
155                 &oh);
156         if (dchan) {
157                 if (dchan->_state == AST_STATE_UP) {
158                         ast_verb(4, "Channel %s was answered.\n", dchan->name);
159                 } else {
160                         ast_verb(4, "Channel %s was never answered.\n", dchan->name);
161                         ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
162                         ast_hangup(dchan);
163                         return -1;
164                 }
165         } else {
166                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
167                 return -1; 
168         }
169
170         ast_stopstream(dchan);
171
172         /* now we have the call placed and are ready to play stuff to it */
173
174         ast_verb(4, "Announce Template:%s\n", args.template);
175
176         for (looptemp = 0; looptemp < ARRAY_LEN(tmp); looptemp++) {
177                 if ((tmp[looptemp] = strsep(&args.template, ":")) != NULL)
178                         continue;
179                 else
180                         break;
181         }
182
183         for (i = 0; i < looptemp; i++) {
184                 ast_verb(4, "Announce:%s\n", tmp[i]);
185                 if (!strcmp(tmp[i], "PARKED")) {
186                         ast_say_digits(dchan, lot, "", dchan->language);
187                 } else {
188                         dres = ast_streamfile(dchan, tmp[i], dchan->language);
189                         if (!dres) {
190                                 dres = ast_waitstream(dchan, "");
191                         } else {
192                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
193                                 dres = 0;
194                         }
195                 }
196         }
197
198         ast_stopstream(dchan);  
199         ast_hangup(dchan);
200         
201         return res;
202 }
203
204 static int unload_module(void)
205 {
206         return ast_unregister_application(app);
207 }
208
209 static int load_module(void)
210 {
211         /* return ast_register_application(app, park_exec); */
212         return ast_register_application_xml(app, parkandannounce_exec);
213 }
214
215 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");