Bridge API: Set a cause code on a channel when it is ejected from a bridge.
[asterisk/asterisk.git] / res / parking / parking_applications.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Call Parking Applications
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/config.h"
32 #include "asterisk/config_options.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/astobj2.h"
35 #include "asterisk/features.h"
36 #include "asterisk/module.h"
37 #include "asterisk/app.h"
38 #include "asterisk/say.h"
39 #include "asterisk/bridge_basic.h"
40
41 /*** DOCUMENTATION
42         <application name="Park" language="en_US">
43                 <synopsis>
44                         Park yourself.
45                 </synopsis>
46                 <syntax>
47                         <parameter name="parking_lot_name">
48                                 <para>Specify in which parking lot to park a call.</para>
49                                 <para>The parking lot used is selected in the following order:</para>
50                                 <para>1) parking_lot_name option to this application</para>
51                                 <para>2) <variable>PARKINGLOT</variable> variable</para>
52                                 <para>3) <literal>CHANNEL(parkinglot)</literal> function
53                                 (Possibly preset by the channel driver.)</para>
54                                 <para>4) Default parking lot.</para>
55                         </parameter>
56                         <parameter name="options">
57                                 <para>A list of options for this parked call.</para>
58                                 <optionlist>
59                                         <option name="r">
60                                                 <para>Send ringing instead of MOH to the parked call.</para>
61                                         </option>
62                                         <option name="R">
63                                                 <para>Randomize the selection of a parking space.</para>
64                                         </option>
65                                         <option name="s">
66                                                 <para>Silence announcement of the parking space number.</para>
67                                         </option>
68                                         <option name="c" argsep=",">
69                                                 <argument name="context" required="false" />
70                                                 <argument name="extension" required="false" />
71                                                 <argument name="priority" required="true" />
72                                                 <para>If the parking times out, go to this place in the dialplan
73                                                         instead of where the parking lot defines the call should go.
74                                                 </para>
75                                         </option>
76                                         <option name="t">
77                                                 <argument name="duration" required="true" />
78                                                 <para>Use a timeout of <literal>duration</literal> seconds instead
79                                                         of the timeout specified by the parking lot.</para>
80                                         </option>
81                                 </optionlist>
82                         </parameter>
83                 </syntax>
84                 <description>
85                         <para>Used to park yourself (typically in combination with an attended
86                         transfer to know the parking space).</para>
87                         <para>If you set the <variable>PARKINGEXTEN</variable> variable to a
88                                 parking space extension in the parking lot, Park() will attempt to park the
89                                 call on that extension. If the extension is already in use then execution
90                                 will continue at the next priority.
91                         </para>
92                 </description>
93                 <see-also>
94                         <ref type="application">ParkedCall</ref>
95                 </see-also>
96         </application>
97
98         <application name="ParkedCall" language="en_US">
99                 <synopsis>
100                         Retrieve a parked call.
101                 </synopsis>
102                 <syntax>
103                         <parameter name="parking_lot_name">
104                                 <para>Specify from which parking lot to retrieve a parked call.</para>
105                                 <para>The parking lot used is selected in the following order:</para>
106                                 <para>1) parking_lot_name option</para>
107                                 <para>2) <variable>PARKINGLOT</variable> variable</para>
108                                 <para>3) <literal>CHANNEL(parkinglot)</literal> function
109                                 (Possibly preset by the channel driver.)</para>
110                                 <para>4) Default parking lot.</para>
111                         </parameter>
112                         <parameter name="parking_space">
113                                 <para>Parking space to retrieve a parked call from.
114                                 If not provided then the first available parked call in the
115                                 parking lot will be retrieved.</para>
116                         </parameter>
117                 </syntax>
118                 <description>
119                         <para>Used to retrieve a parked call from a parking lot.</para>
120                         <note>
121                                 <para>If a parking lot's parkext option is set, then Parking lots
122                                 will automatically create and manage dialplan extensions in
123                                 the parking lot context. If that is the case then you will not
124                                 need to manage parking extensions yourself, just include the
125                                 parking context of the parking lot.</para>
126                         </note>
127                 </description>
128                 <see-also>
129                         <ref type="application">Park</ref>
130                 </see-also>
131         </application>
132
133         <application name="ParkAndAnnounce" language="en_US">
134                 <synopsis>
135                         Park and Announce.
136                 </synopsis>
137                 <syntax>
138                         <parameter name="parking_lot_name">
139                                 <para>Specify in which parking lot to park a call.</para>
140                                 <para>The parking lot used is selected in the following order:</para>
141                                 <para>1) parking_lot_name option to this application</para>
142                                 <para>2) <variable>PARKINGLOT</variable> variable</para>
143                                 <para>3) <literal>CHANNEL(parkinglot)</literal> function
144                                 (Possibly preset by the channel driver.)</para>
145                                 <para>4) Default parking lot.</para>
146                         </parameter>
147                         <parameter name="options">
148                                 <para>A list of options for this parked call.</para>
149                                 <optionlist>
150                                         <option name="r">
151                                                 <para>Send ringing instead of MOH to the parked call.</para>
152                                         </option>
153                                         <option name="R">
154                                                 <para>Randomize the selection of a parking space.</para>
155                                         </option>
156                                         <option name="c" argsep=",">
157                                                 <argument name="context" required="false" />
158                                                 <argument name="extension" required="false" />
159                                                 <argument name="priority" required="true" />
160                                                 <para>If the parking times out, go to this place in the dialplan
161                                                         instead of where the parking lot defines the call should go.
162                                                 </para>
163                                         </option>
164                                         <option name="t">
165                                                 <argument name="duration" required="true" />
166                                                 <para>Use a timeout of <literal>duration</literal> seconds instead
167                                                         of the timeout specified by the parking lot.</para>
168                                         </option>
169                                 </optionlist>
170                         </parameter>
171                         <parameter name="announce_template" required="true" argsep=":">
172                                 <argument name="announce" required="true">
173                                         <para>Colon-separated list of files to announce. The word
174                                         <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
175                                         the call is parked.</para>
176                                 </argument>
177                                 <argument name="announce1" multiple="true" />
178                         </parameter>
179                         <parameter name="dial" required="true">
180                                 <para>The app_dial style resource to call to make the
181                                 announcement. Console/dsp calls the console.</para>
182                         </parameter>
183                 </syntax>
184                 <description>
185                         <para>Park a call into the parkinglot and announce the call to another channel.</para>
186                         <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
187                         into which the call was placed.  Use with the Local channel to allow the dialplan to make
188                         use of this information.</para>
189                 </description>
190                 <see-also>
191                         <ref type="application">Park</ref>
192                         <ref type="application">ParkedCall</ref>
193                 </see-also>
194         </application>
195  ***/
196
197 #define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
198
199 /* Park a call */
200
201 enum park_args {
202         OPT_ARG_COMEBACK,
203         OPT_ARG_TIMEOUT,
204         OPT_ARG_ARRAY_SIZE /* Always the last element of the enum */
205 };
206
207 enum park_flags {
208         MUXFLAG_RINGING = (1 << 0),
209         MUXFLAG_RANDOMIZE = (1 << 1),
210         MUXFLAG_NOANNOUNCE = (1 << 2),
211         MUXFLAG_COMEBACK_OVERRIDE = (1 << 3),
212         MUXFLAG_TIMEOUT_OVERRIDE = (1 << 4),
213 };
214
215 AST_APP_OPTIONS(park_opts, {
216         AST_APP_OPTION('r', MUXFLAG_RINGING),
217         AST_APP_OPTION('R', MUXFLAG_RANDOMIZE),
218         AST_APP_OPTION('s', MUXFLAG_NOANNOUNCE),
219         AST_APP_OPTION_ARG('c', MUXFLAG_COMEBACK_OVERRIDE, OPT_ARG_COMEBACK),
220         AST_APP_OPTION_ARG('t', MUXFLAG_TIMEOUT_OVERRIDE, OPT_ARG_TIMEOUT),
221 });
222
223 static int apply_option_timeout (int *var, char *timeout_arg)
224 {
225         if (ast_strlen_zero(timeout_arg)) {
226                 ast_log(LOG_ERROR, "No duration value provided for the timeout ('t') option.\n");
227                 return -1;
228         }
229
230         if (sscanf(timeout_arg, "%d", var) != 1 || *var < 0) {
231                 ast_log(LOG_ERROR, "Duration value provided for timeout ('t') option must be 0 or greater.\n");
232                 return -1;
233         }
234
235         return 0;
236 }
237
238 static int park_app_parse_data(const char *data, int *disable_announce, int *use_ringing, int *randomize, int *time_limit, char **comeback_override, char **lot_name)
239 {
240         char *parse;
241         struct ast_flags flags = { 0 };
242
243         AST_DECLARE_APP_ARGS(args,
244                 AST_APP_ARG(lot_name);
245                 AST_APP_ARG(options);
246                 AST_APP_ARG(other);     /* Any remaining unused arguments */
247         );
248
249         parse = ast_strdupa(data);
250         AST_STANDARD_APP_ARGS(args, parse);
251
252         if (args.options) {
253                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
254                 ast_app_parse_options(park_opts, &flags, opts, args.options);
255                 if (ast_test_flag(&flags, MUXFLAG_TIMEOUT_OVERRIDE)) {
256                         if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
257                                 return -1;
258                         }
259                 }
260
261                 if (ast_test_flag(&flags, MUXFLAG_COMEBACK_OVERRIDE)) {
262                         *comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
263                 }
264
265                 if (ast_test_flag(&flags, MUXFLAG_NOANNOUNCE)) {
266                         if (disable_announce) {
267                                 *disable_announce = 1;
268                         }
269                 }
270
271                 if (ast_test_flag(&flags, MUXFLAG_RINGING)) {
272                         *use_ringing = 1;
273                 }
274
275                 if (ast_test_flag(&flags, MUXFLAG_RANDOMIZE)) {
276                         *randomize = 1;
277                 }
278         }
279
280         if (!ast_strlen_zero(args.lot_name)) {
281                 *lot_name = ast_strdup(args.lot_name);
282         }
283
284         return 0;
285 }
286
287 void park_common_datastore_free(struct park_common_datastore *datastore)
288 {
289         if (!datastore) {
290                 return;
291         }
292
293         ast_free(datastore->parker_uuid);
294         ast_free(datastore->parker_dial_string);
295         ast_free(datastore->comeback_override);
296         ast_free(datastore);
297 }
298
299 static void park_common_datastore_destroy(void *data)
300 {
301         struct park_common_datastore *datastore = data;
302         park_common_datastore_free(datastore);
303 }
304
305 static const struct ast_datastore_info park_common_info = {
306         .type = "park entry data",
307         .destroy = park_common_datastore_destroy,
308 };
309
310 static void wipe_park_common_datastore(struct ast_channel *chan)
311 {
312         struct ast_datastore *datastore;
313
314         ast_channel_lock(chan);
315         datastore = ast_channel_datastore_find(chan, &park_common_info, NULL);
316         if (datastore) {
317                 ast_channel_datastore_remove(chan, datastore);
318                 ast_datastore_free(datastore);
319         }
320         ast_channel_unlock(chan);
321 }
322
323 static int setup_park_common_datastore(struct ast_channel *parkee, const char *parker_uuid, const char *comeback_override, int randomize, int time_limit, int silence_announce)
324 {
325         struct ast_datastore *datastore = NULL;
326         struct park_common_datastore *park_datastore;
327         const char *attended_transfer;
328         const char *blind_transfer;
329         char *parker_dial_string = NULL;
330
331         wipe_park_common_datastore(parkee);
332
333         if (!(datastore = ast_datastore_alloc(&park_common_info, NULL))) {
334                 return -1;
335         }
336
337         if (!(park_datastore = ast_calloc(1, sizeof(*park_datastore)))) {
338                 ast_datastore_free(datastore);
339                 return -1;
340         }
341
342         if (parker_uuid) {
343                 park_datastore->parker_uuid = ast_strdup(parker_uuid);
344         }
345
346         ast_channel_lock(parkee);
347
348         attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER");
349         blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER");
350
351         if (attended_transfer || blind_transfer) {
352                 parker_dial_string = ast_strdupa(S_OR(attended_transfer, blind_transfer));
353         }
354
355         ast_channel_unlock(parkee);
356
357         if (!ast_strlen_zero(parker_dial_string)) {
358                 ast_channel_name_to_dial_string(parker_dial_string);
359                 ast_verb(5, "Setting dial string to %s from %s value", parker_dial_string, attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
360                 park_datastore->parker_dial_string = ast_strdup(parker_dial_string);
361         }
362
363         park_datastore->randomize = randomize;
364         park_datastore->time_limit = time_limit;
365         park_datastore->silence_announce = silence_announce;
366
367         if (comeback_override) {
368                 park_datastore->comeback_override = ast_strdup(comeback_override);
369         }
370
371
372         datastore->data = park_datastore;
373         ast_channel_lock(parkee);
374         ast_channel_datastore_add(parkee, datastore);
375         ast_channel_unlock(parkee);
376
377         return 0;
378 }
379
380 struct park_common_datastore *get_park_common_datastore_copy(struct ast_channel *parkee)
381 {
382         struct ast_datastore *datastore;
383         struct park_common_datastore *data;
384         struct park_common_datastore *data_copy;
385
386         SCOPED_CHANNELLOCK(lock, parkee);
387         if (!(datastore = ast_channel_datastore_find(parkee, &park_common_info, NULL))) {
388                 return NULL;
389         }
390
391         data = datastore->data;
392
393         if (!data) {
394                 /* This data should always be populated if this datastore was appended to the channel */
395                 ast_assert(0);
396         }
397
398         data_copy = ast_calloc(1, sizeof(*data_copy));
399         if (!data_copy) {
400                 return NULL;
401         }
402
403         if (!(data_copy->parker_uuid = ast_strdup(data->parker_uuid))) {
404                 park_common_datastore_free(data_copy);
405                 return NULL;
406         }
407
408         data_copy->randomize = data->randomize;
409         data_copy->time_limit = data->time_limit;
410         data_copy->silence_announce = data->silence_announce;
411
412         if (data->comeback_override) {
413                 data_copy->comeback_override = ast_strdup(data->comeback_override);
414                 if (!data_copy->comeback_override) {
415                         park_common_datastore_free(data_copy);
416                         return NULL;
417                 }
418         }
419
420         if (data->parker_dial_string) {
421                 data_copy->parker_dial_string = ast_strdup(data->parker_dial_string);
422                 if (!data_copy->parker_dial_string) {
423                         park_common_datastore_free(data_copy);
424                         return NULL;
425                 }
426         }
427
428         return data_copy;
429 }
430
431 struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
432                 const char *lot_name, const char *comeback_override,
433                 int use_ringing, int randomize, int time_limit, int silence_announcements)
434 {
435         struct ast_bridge *parking_bridge;
436         RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
437
438         if (!parker) {
439                 parker = parkee;
440         }
441
442         /* If the name of the parking lot isn't specified in the arguments, find it based on the channel. */
443         if (ast_strlen_zero(lot_name)) {
444                 ast_channel_lock(parker);
445                 lot_name = ast_strdupa(find_channel_parking_lot_name(parker));
446                 ast_channel_unlock(parker);
447         }
448
449         lot = parking_lot_find_by_name(lot_name);
450         if (!lot) {
451                 lot = parking_create_dynamic_lot(lot_name, parkee);
452         }
453
454         if (!lot) {
455                 ast_log(LOG_ERROR, "Could not find parking lot: '%s'\n", lot_name);
456                 return NULL;
457         }
458
459         ao2_lock(lot);
460         parking_bridge = parking_lot_get_bridge(lot);
461         ao2_unlock(lot);
462
463         if (!parking_bridge) {
464                 return NULL;
465         }
466
467         /* Apply relevant bridge roles and such to the parking channel */
468         parking_channel_set_roles(parkee, lot, use_ringing);
469         setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
470                 silence_announcements);
471         return parking_bridge;
472 }
473
474 struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
475                 int *silence_announcements)
476 {
477         int use_ringing = 0;
478         int randomize = 0;
479         int time_limit = -1;
480
481         RAII_VAR(char *, comeback_override, NULL, ast_free);
482         RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
483
484         if (app_data) {
485                 park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg);
486         }
487
488         return park_common_setup(parkee, parker, lot_name_app_arg, comeback_override, use_ringing,
489                 randomize, time_limit, silence_announcements ? *silence_announcements : 0);
490
491 }
492
493 static int park_app_exec(struct ast_channel *chan, const char *data)
494 {
495         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
496
497         struct ast_bridge_features chan_features;
498         int res;
499         int silence_announcements = 0;
500         const char *transferer;
501
502         /* Answer the channel if needed */
503         if (ast_channel_state(chan) != AST_STATE_UP) {
504                 ast_answer(chan);
505         }
506
507         ast_channel_lock(chan);
508         if (!(transferer = pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER"))) {
509                 transferer = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER");
510         }
511         transferer = ast_strdupa(S_OR(transferer, ""));
512         ast_channel_unlock(chan);
513
514         /* Handle the common parking setup stuff */
515         if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
516                 if (!silence_announcements && !transferer) {
517                         ast_stream_and_wait(chan, "pbx-parkingfailed", "");
518                 }
519                 return 0;
520         }
521
522         /* Initialize bridge features for the channel. */
523         res = ast_bridge_features_init(&chan_features);
524         if (res) {
525                 ast_bridge_features_cleanup(&chan_features);
526                 return -1;
527         }
528
529         /* Now for the fun part... park it! */
530         ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
531
532         /*
533          * If the bridge was broken for a hangup that isn't real, then
534          * don't run the h extension, because the channel isn't really
535          * hung up.  This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
536          */
537         res = -1;
538
539         ast_channel_lock(chan);
540         if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
541                 res = 0;
542         }
543         ast_channel_unlock(chan);
544
545         ast_bridge_features_cleanup(&chan_features);
546
547         return res;
548 }
549
550 /* Retrieve a parked call */
551
552 static int parked_call_app_exec(struct ast_channel *chan, const char *data)
553 {
554         RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
555         RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
556         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
557         struct ast_bridge *retrieval_bridge;
558         int res;
559         int target_space = -1;
560         struct ast_bridge_features chan_features;
561         char *parse;
562         char *lot_name;
563
564         AST_DECLARE_APP_ARGS(args,
565                 AST_APP_ARG(lot_name);
566                 AST_APP_ARG(parking_space);
567                 AST_APP_ARG(other);     /* Any remaining unused arguments */
568         );
569
570         parse = ast_strdupa(data);
571         AST_STANDARD_APP_ARGS(args, parse);
572
573         /* Answer the channel if needed */
574         if (ast_channel_state(chan) != AST_STATE_UP) {
575                 ast_answer(chan);
576         }
577
578         lot_name = args.lot_name;
579
580         /* If the name of the parking lot isn't in the arguments, find it based on the channel. */
581         if (ast_strlen_zero(lot_name)) {
582                 ast_channel_lock(chan);
583                 lot_name = ast_strdupa(find_channel_parking_lot_name(chan));
584                 ast_channel_unlock(chan);
585         }
586
587         lot = parking_lot_find_by_name(lot_name);
588
589         if (!lot) {
590                 ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
591                 ast_stream_and_wait(chan, "pbx-invalidpark", "");
592                 return -1;
593         }
594
595         if (!ast_strlen_zero(args.parking_space)) {
596                 if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
597                         ast_stream_and_wait(chan, "pbx-invalidpark", "");
598                         ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
599                         return -1;
600                 }
601         }
602
603         /* Attempt to get the parked user from the parking lot */
604         pu = parking_lot_retrieve_parked_user(lot, target_space);
605         if (!pu) {
606                 ast_stream_and_wait(chan, "pbx-invalidpark", "");
607                 return -1;
608         }
609
610         /* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
611         pu->retriever = ast_channel_snapshot_create(chan);
612
613         /* Create bridge */
614         retrieval_bridge = ast_bridge_basic_new();
615         if (!retrieval_bridge) {
616                 return -1;
617         }
618
619         /* Move the parkee into the new bridge */
620         if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) {
621                 ast_bridge_destroy(retrieval_bridge, 0);
622                 return -1;
623         }
624
625         /* Initialize our bridge features */
626         res = ast_bridge_features_init(&chan_features);
627         if (res) {
628                 ast_bridge_destroy(retrieval_bridge, 0);
629                 ast_bridge_features_cleanup(&chan_features);
630                 return -1;
631         }
632
633         /* Set the features */
634         parked_call_retrieve_enable_features(chan, lot, AST_FEATURE_FLAG_BYCALLER);
635
636         /* If the parkedplay option is set for the caller to hear, play that tone now. */
637         if (lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLER) {
638                 ast_stream_and_wait(chan, lot->cfg->courtesytone, NULL);
639         }
640
641         /* Now we should try to join the new bridge ourselves... */
642         ast_bridge_join(retrieval_bridge, chan, NULL, &chan_features, NULL, 1);
643
644         ast_bridge_features_cleanup(&chan_features);
645
646         return 0;
647 }
648
649 struct park_announce_subscription_data {
650         char *parkee_uuid;
651         char *dial_string;
652         char *announce_string;
653 };
654
655 static void park_announce_subscription_data_destroy(void *data)
656 {
657         struct park_announce_subscription_data *pa_data = data;
658         ast_free(pa_data->parkee_uuid);
659         ast_free(pa_data->dial_string);
660         ast_free(pa_data->announce_string);
661         ast_free(pa_data);
662 }
663
664 static struct park_announce_subscription_data *park_announce_subscription_data_create(const char *parkee_uuid,
665                 const char *dial_string,
666                 const char *announce_string)
667 {
668         struct park_announce_subscription_data *pa_data;
669
670         if (!(pa_data = ast_calloc(1, sizeof(*pa_data)))) {
671                 return NULL;
672         }
673
674         if (!(pa_data->parkee_uuid = ast_strdup(parkee_uuid))
675                 || !(pa_data->dial_string = ast_strdup(dial_string))
676                 || !(pa_data->announce_string = ast_strdup(announce_string))) {
677                 park_announce_subscription_data_destroy(pa_data);
678                 return NULL;
679         }
680
681         return pa_data;
682 }
683
684 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
685 {
686         struct ast_channel *dchan;
687         struct outgoing_helper oh = { 0, };
688         int outstate;
689         struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
690         char buf[13];
691         char *dial_tech;
692         char *cur_announce;
693         struct ast_format tmpfmt;
694
695         dial_tech = strsep(&dial_string, "/");
696         ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
697
698         if (!cap_slin) {
699                 ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
700                 goto announce_cleanup;
701         }
702         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
703
704         snprintf(buf, sizeof(buf), "%d", parkingspace);
705         oh.vars = ast_variable_new("_PARKEDAT", buf, "");
706         dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, dial_string, 30000,
707                 &outstate,
708                 parkee_snapshot->caller_number,
709                 parkee_snapshot->caller_name,
710                 &oh);
711
712         ast_variables_destroy(oh.vars);
713         if (!dchan) {
714                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
715                 goto announce_cleanup;
716         }
717
718         ast_verb(4, "Announce Template: %s\n", announce_string);
719
720         for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
721                 ast_verb(4, "Announce:%s\n", cur_announce);
722                 if (!strcmp(cur_announce, "PARKED")) {
723                         ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
724                 } else {
725                         int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
726                         if (!dres) {
727                                 dres = ast_waitstream(dchan, "");
728                         } else {
729                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
730                         }
731                 }
732         }
733
734         ast_stopstream(dchan);
735         ast_hangup(dchan);
736
737 announce_cleanup:
738         cap_slin = ast_format_cap_destroy(cap_slin);
739 }
740
741 static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
742 {
743         struct park_announce_subscription_data *pa_data = data;
744         char *dial_string = pa_data->dial_string;
745
746         struct ast_parked_call_payload *payload = stasis_message_data(message);
747
748         if (stasis_subscription_final_message(sub, message)) {
749                 park_announce_subscription_data_destroy(data);
750                 return;
751         }
752
753         if (payload->event_type != PARKED_CALL) {
754                 /* We are only concerned with calls parked */
755                 return;
756         }
757
758         if (strcmp(payload->parkee->uniqueid, pa_data->parkee_uuid)) {
759                 /* We are only concerned with the parkee we are subscribed for. */
760                 return;
761         }
762
763         if (!ast_strlen_zero(dial_string)) {
764                 announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
765         }
766
767         *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
768 }
769
770 static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
771 {
772         struct ast_bridge_features chan_features;
773         char *parse;
774         int res;
775         int silence_announcements = 1;
776
777         struct stasis_subscription *parking_subscription;
778         struct park_announce_subscription_data *pa_data;
779
780         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
781
782         AST_DECLARE_APP_ARGS(args,
783                 AST_APP_ARG(lot_name);
784                 AST_APP_ARG(options);
785                 AST_APP_ARG(announce_template);
786                 AST_APP_ARG(dial);
787                 AST_APP_ARG(others);/* Any remaining unused arguments */
788         );
789
790         if (ast_strlen_zero(data)) {
791                 ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
792                 return -1;
793         }
794
795         parse = ast_strdupa(data);
796         AST_STANDARD_APP_ARGS(args, parse);
797
798         if (ast_strlen_zero(args.announce_template)) {
799                 /* improperly configured arguments for the application */
800                 ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
801                 return -1;
802         }
803
804         if (ast_strlen_zero(args.dial)) {
805                 /* improperly configured arguments */
806                 ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
807                 return -1;
808         }
809
810         if (!strchr(args.dial, '/')) {
811                 ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
812                 return -1;
813         }
814
815         /* Handle the common parking setup stuff */
816         if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
817                 return 0;
818         }
819
820         /* Initialize bridge features for the channel. */
821         res = ast_bridge_features_init(&chan_features);
822         if (res) {
823                 ast_bridge_features_cleanup(&chan_features);
824                 return -1;
825         }
826
827         /* subscribe to the parking message so that we can announce once it is parked */
828         pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
829         if (!pa_data) {
830                 return -1;
831         }
832
833         if (!(parking_subscription = stasis_subscribe(ast_parking_topic(), park_announce_update_cb, pa_data))) {
834                 /* Failed to create subscription */
835                 park_announce_subscription_data_destroy(pa_data);
836                 return -1;
837         }
838
839         /* Now for the fun part... park it! */
840         ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
841
842         /* Toss the subscription since we aren't bridged at this point. */
843         stasis_unsubscribe(parking_subscription);
844
845         /*
846          * If the bridge was broken for a hangup that isn't real, then
847          * don't run the h extension, because the channel isn't really
848          * hung up.  This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
849          */
850         res = -1;
851
852         ast_channel_lock(chan);
853         if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
854                 res = 0;
855         }
856         ast_channel_unlock(chan);
857
858         ast_bridge_features_cleanup(&chan_features);
859
860         return res;
861 }
862
863 int load_parking_applications(void)
864 {
865         const struct ast_module_info *ast_module_info = parking_get_module_info();
866
867         if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
868                 return -1;
869         }
870
871         if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
872                 return -1;
873         }
874
875         if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
876                 return -1;
877         }
878
879         return 0;
880 }
881
882 void unload_parking_applications(void)
883 {
884         ast_unregister_application(PARK_APPLICATION);
885         ast_unregister_application(PARKED_CALL_APPLICATION);
886         ast_unregister_application(PARK_AND_ANNOUNCE_APPLICATION);
887 }