5500e3316475a0379133c0fdb205fd1f6a0ad408
[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                 publish_parked_call_failure(chan);
520                 return 0;
521         }
522
523         /* Initialize bridge features for the channel. */
524         res = ast_bridge_features_init(&chan_features);
525         if (res) {
526                 ast_bridge_features_cleanup(&chan_features);
527                 publish_parked_call_failure(chan);
528                 return -1;
529         }
530
531         /* Now for the fun part... park it! */
532         ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
533
534         /*
535          * If the bridge was broken for a hangup that isn't real, then
536          * don't run the h extension, because the channel isn't really
537          * hung up.  This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
538          */
539         res = -1;
540
541         ast_channel_lock(chan);
542         if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
543                 res = 0;
544         }
545         ast_channel_unlock(chan);
546
547         ast_bridge_features_cleanup(&chan_features);
548
549         return res;
550 }
551
552 /* Retrieve a parked call */
553
554 static int parked_call_app_exec(struct ast_channel *chan, const char *data)
555 {
556         RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
557         RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
558         struct ast_bridge *retrieval_bridge;
559         int res;
560         int target_space = -1;
561         struct ast_bridge_features chan_features;
562         char *parse;
563         char *lot_name;
564
565         AST_DECLARE_APP_ARGS(args,
566                 AST_APP_ARG(lot_name);
567                 AST_APP_ARG(parking_space);
568                 AST_APP_ARG(other);     /* Any remaining unused arguments */
569         );
570
571         parse = ast_strdupa(data);
572         AST_STANDARD_APP_ARGS(args, parse);
573
574         /* Answer the channel if needed */
575         if (ast_channel_state(chan) != AST_STATE_UP) {
576                 ast_answer(chan);
577         }
578
579         lot_name = args.lot_name;
580
581         /* If the name of the parking lot isn't in the arguments, find it based on the channel. */
582         if (ast_strlen_zero(lot_name)) {
583                 ast_channel_lock(chan);
584                 lot_name = ast_strdupa(find_channel_parking_lot_name(chan));
585                 ast_channel_unlock(chan);
586         }
587
588         lot = parking_lot_find_by_name(lot_name);
589
590         if (!lot) {
591                 ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
592                 ast_stream_and_wait(chan, "pbx-invalidpark", "");
593                 return -1;
594         }
595
596         if (!ast_strlen_zero(args.parking_space)) {
597                 if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
598                         ast_stream_and_wait(chan, "pbx-invalidpark", "");
599                         ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
600                         return -1;
601                 }
602         }
603
604         /* Attempt to get the parked user from the parking lot */
605         pu = parking_lot_retrieve_parked_user(lot, target_space);
606         if (!pu) {
607                 ast_stream_and_wait(chan, "pbx-invalidpark", "");
608                 return -1;
609         }
610
611         /* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
612         pu->retriever = ast_channel_snapshot_create(chan);
613
614         /* Create bridge */
615         retrieval_bridge = ast_bridge_basic_new();
616         if (!retrieval_bridge) {
617                 return -1;
618         }
619
620         /* Move the parkee into the new bridge */
621         if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) {
622                 ast_bridge_destroy(retrieval_bridge, 0);
623                 return -1;
624         }
625
626         /* Initialize our bridge features */
627         res = ast_bridge_features_init(&chan_features);
628         if (res) {
629                 ast_bridge_destroy(retrieval_bridge, 0);
630                 ast_bridge_features_cleanup(&chan_features);
631                 return -1;
632         }
633
634         /* Set the features */
635         parked_call_retrieve_enable_features(chan, lot, AST_FEATURE_FLAG_BYCALLER);
636
637         /* If the parkedplay option is set for the caller to hear, play that tone now. */
638         if (lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLER) {
639                 ast_stream_and_wait(chan, lot->cfg->courtesytone, NULL);
640         }
641
642         /* Now we should try to join the new bridge ourselves... */
643         ast_bridge_join(retrieval_bridge, chan, NULL, &chan_features, NULL,
644                 AST_BRIDGE_JOIN_PASS_REFERENCE);
645
646         ast_bridge_features_cleanup(&chan_features);
647
648         return 0;
649 }
650
651 struct park_announce_subscription_data {
652         char *parkee_uuid;
653         char *dial_string;
654         char *announce_string;
655 };
656
657 static void park_announce_subscription_data_destroy(void *data)
658 {
659         struct park_announce_subscription_data *pa_data = data;
660         ast_free(pa_data->parkee_uuid);
661         ast_free(pa_data->dial_string);
662         ast_free(pa_data->announce_string);
663         ast_free(pa_data);
664 }
665
666 static struct park_announce_subscription_data *park_announce_subscription_data_create(const char *parkee_uuid,
667                 const char *dial_string,
668                 const char *announce_string)
669 {
670         struct park_announce_subscription_data *pa_data;
671
672         if (!(pa_data = ast_calloc(1, sizeof(*pa_data)))) {
673                 return NULL;
674         }
675
676         if (!(pa_data->parkee_uuid = ast_strdup(parkee_uuid))
677                 || !(pa_data->dial_string = ast_strdup(dial_string))
678                 || !(pa_data->announce_string = ast_strdup(announce_string))) {
679                 park_announce_subscription_data_destroy(pa_data);
680                 return NULL;
681         }
682
683         return pa_data;
684 }
685
686 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
687 {
688         struct ast_channel *dchan;
689         struct outgoing_helper oh = { 0, };
690         int outstate;
691         struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
692         char buf[13];
693         char *dial_tech;
694         char *cur_announce;
695         struct ast_format tmpfmt;
696
697         dial_tech = strsep(&dial_string, "/");
698         ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
699
700         if (!cap_slin) {
701                 ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
702                 goto announce_cleanup;
703         }
704         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
705
706         snprintf(buf, sizeof(buf), "%d", parkingspace);
707         oh.vars = ast_variable_new("_PARKEDAT", buf, "");
708         dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
709                 &outstate,
710                 parkee_snapshot->caller_number,
711                 parkee_snapshot->caller_name,
712                 &oh);
713
714         ast_variables_destroy(oh.vars);
715         if (!dchan) {
716                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
717                 goto announce_cleanup;
718         }
719
720         ast_verb(4, "Announce Template: %s\n", announce_string);
721
722         for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
723                 ast_verb(4, "Announce:%s\n", cur_announce);
724                 if (!strcmp(cur_announce, "PARKED")) {
725                         ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
726                 } else {
727                         int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
728                         if (!dres) {
729                                 dres = ast_waitstream(dchan, "");
730                         } else {
731                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
732                         }
733                 }
734         }
735
736         ast_stopstream(dchan);
737         ast_hangup(dchan);
738
739 announce_cleanup:
740         cap_slin = ast_format_cap_destroy(cap_slin);
741 }
742
743 static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
744 {
745         struct park_announce_subscription_data *pa_data = data;
746         char *dial_string = pa_data->dial_string;
747
748         struct ast_parked_call_payload *payload = stasis_message_data(message);
749
750         if (stasis_subscription_final_message(sub, message)) {
751                 park_announce_subscription_data_destroy(data);
752                 return;
753         }
754
755         if (payload->event_type != PARKED_CALL) {
756                 /* We are only concerned with calls parked */
757                 return;
758         }
759
760         if (strcmp(payload->parkee->uniqueid, pa_data->parkee_uuid)) {
761                 /* We are only concerned with the parkee we are subscribed for. */
762                 return;
763         }
764
765         if (!ast_strlen_zero(dial_string)) {
766                 announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
767         }
768
769         *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
770 }
771
772 static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
773 {
774         struct ast_bridge_features chan_features;
775         char *parse;
776         int res;
777         int silence_announcements = 1;
778
779         struct stasis_subscription *parking_subscription;
780         struct park_announce_subscription_data *pa_data;
781
782         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
783
784         AST_DECLARE_APP_ARGS(args,
785                 AST_APP_ARG(lot_name);
786                 AST_APP_ARG(options);
787                 AST_APP_ARG(announce_template);
788                 AST_APP_ARG(dial);
789                 AST_APP_ARG(others);/* Any remaining unused arguments */
790         );
791
792         if (ast_strlen_zero(data)) {
793                 ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
794                 return -1;
795         }
796
797         parse = ast_strdupa(data);
798         AST_STANDARD_APP_ARGS(args, parse);
799
800         if (ast_strlen_zero(args.announce_template)) {
801                 /* improperly configured arguments for the application */
802                 ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
803                 return -1;
804         }
805
806         if (ast_strlen_zero(args.dial)) {
807                 /* improperly configured arguments */
808                 ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
809                 return -1;
810         }
811
812         if (!strchr(args.dial, '/')) {
813                 ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
814                 return -1;
815         }
816
817         /* Handle the common parking setup stuff */
818         if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
819                 return 0;
820         }
821
822         /* Initialize bridge features for the channel. */
823         res = ast_bridge_features_init(&chan_features);
824         if (res) {
825                 ast_bridge_features_cleanup(&chan_features);
826                 return -1;
827         }
828
829         /* subscribe to the parking message so that we can announce once it is parked */
830         pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
831         if (!pa_data) {
832                 return -1;
833         }
834
835         if (!(parking_subscription = stasis_subscribe(ast_parking_topic(), park_announce_update_cb, pa_data))) {
836                 /* Failed to create subscription */
837                 park_announce_subscription_data_destroy(pa_data);
838                 return -1;
839         }
840
841         /* Now for the fun part... park it! */
842         ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
843
844         /* Toss the subscription since we aren't bridged at this point. */
845         stasis_unsubscribe(parking_subscription);
846
847         /*
848          * If the bridge was broken for a hangup that isn't real, then
849          * don't run the h extension, because the channel isn't really
850          * hung up.  This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
851          */
852         res = -1;
853
854         ast_channel_lock(chan);
855         if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
856                 res = 0;
857         }
858         ast_channel_unlock(chan);
859
860         ast_bridge_features_cleanup(&chan_features);
861
862         return res;
863 }
864
865 int load_parking_applications(void)
866 {
867         const struct ast_module_info *ast_module_info = parking_get_module_info();
868
869         if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
870                 return -1;
871         }
872
873         if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
874                 return -1;
875         }
876
877         if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
878                 return -1;
879         }
880
881         return 0;
882 }
883
884 void unload_parking_applications(void)
885 {
886         ast_unregister_application(PARK_APPLICATION);
887         ast_unregister_application(PARKED_CALL_APPLICATION);
888         ast_unregister_application(PARK_AND_ANNOUNCE_APPLICATION);
889 }