Asterisk - The Open Source Telephony Project GIT-master-7921072
parking_applications.c
Go to the documentation of this file.
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#include "res_parking.h"
29#include "asterisk/config.h"
31#include "asterisk/utils.h"
32#include "asterisk/astobj2.h"
33#include "asterisk/features.h"
34#include "asterisk/module.h"
35#include "asterisk/app.h"
36#include "asterisk/say.h"
39
40/*** DOCUMENTATION
41 <application name="Park" language="en_US">
42 <synopsis>
43 Park yourself.
44 </synopsis>
45 <syntax>
46 <parameter name="parking_lot_name">
47 <para>Specify in which parking lot to park a call.</para>
48 <para>The parking lot used is selected in the following order:</para>
49 <para>1) parking_lot_name option to this application</para>
50 <para>2) <variable>PARKINGLOT</variable> variable</para>
51 <para>3) <literal>CHANNEL(parkinglot)</literal> function
52 (Possibly preset by the channel driver.)</para>
53 <para>4) Default parking lot.</para>
54 </parameter>
55 <parameter name="options">
56 <para>A list of options for this parked call.</para>
57 <optionlist>
58 <option name="r">
59 <para>Send ringing instead of MOH to the parked call.</para>
60 </option>
61 <option name="R">
62 <para>Randomize the selection of a parking space.</para>
63 </option>
64 <option name="s">
65 <para>Silence announcement of the parking space number.</para>
66 </option>
67 <option name="c" argsep=",">
68 <argument name="context" required="false" />
69 <argument name="extension" required="false" />
70 <argument name="priority" required="true" />
71 <para>If the parking times out, go to this place in the dialplan
72 instead of where the parking lot defines the call should go.
73 </para>
74 </option>
75 <option name="t">
76 <argument name="duration" required="true" />
77 <para>Use a timeout of <literal>duration</literal> seconds instead
78 of the timeout specified by the parking lot.</para>
79 </option>
80 </optionlist>
81 </parameter>
82 </syntax>
83 <description>
84 <para>Used to park yourself (typically in combination with an attended
85 transfer to know the parking space).</para>
86 <para>If you set the <variable>PARKINGEXTEN</variable> variable to a
87 parking space extension in the parking lot, Park() will attempt to park the
88 call on that extension. If the extension is already in use then execution
89 will continue at the next priority.
90 </para>
91 <para>If the <literal>parkeddynamic</literal> option is enabled in
92 <filename>res_parking.conf</filename> the following variables can be
93 used to dynamically create new parking lots. When using dynamic parking
94 lots, be aware of the conditions as explained in the notes section
95 below.
96 </para>
97 <para>The <variable>PARKINGDYNAMIC</variable> variable specifies the
98 parking lot to use as a template to create a dynamic parking lot. It
99 is an error to specify a non-existent parking lot for the template.
100 If not set then the default parking lot is used as the template.
101 </para>
102 <para>The <variable>PARKINGDYNCONTEXT</variable> variable specifies the
103 dialplan context to use for the newly created dynamic parking lot. If
104 not set then the context from the parking lot template is used. The
105 context is created if it does not already exist and the new parking lot
106 needs to create extensions.
107 </para>
108 <para>The <variable>PARKINGDYNEXTEN</variable> variable specifies the
109 <literal>parkext</literal> to use for the newly created dynamic
110 parking lot. If not set then the <literal>parkext</literal> is used from
111 the parking lot template. If the template does not specify a
112 <literal>parkext</literal> then no extensions are created for the newly
113 created parking lot. The dynamic parking lot cannot be created if it
114 needs to create extensions that overlap existing parking lot extensions.
115 The only exception to this is for the <literal>parkext</literal>
116 extension and only if neither of the overlaping parking lot's
117 <literal>parkext</literal> is exclusive.
118 </para>
119 <para>The <variable>PARKINGDYNPOS</variable> variable specifies the
120 parking positions to use for the newly created dynamic parking lot. If
121 not set then the <literal>parkpos</literal> from the parking lot template
122 is used.
123 </para>
124 <note>
125 <para>This application must be used as the first extension priority
126 to be recognized as a parking access extension for blind transfers.
127 Blind transfers and the DTMF one-touch parking feature need this
128 distinction to operate properly. The parking access extension in
129 this case is treated like a dialplan hint.
130 </para>
131 </note>
132 </description>
133 <see-also>
134 <ref type="application">ParkedCall</ref>
135 </see-also>
136 </application>
137
138 <application name="ParkedCall" language="en_US">
139 <synopsis>
140 Retrieve a parked call.
141 </synopsis>
142 <syntax>
143 <parameter name="parking_lot_name">
144 <para>Specify from which parking lot to retrieve a parked call.</para>
145 <para>The parking lot used is selected in the following order:</para>
146 <para>1) parking_lot_name option</para>
147 <para>2) <variable>PARKINGLOT</variable> variable</para>
148 <para>3) <literal>CHANNEL(parkinglot)</literal> function
149 (Possibly preset by the channel driver.)</para>
150 <para>4) Default parking lot.</para>
151 </parameter>
152 <parameter name="parking_space">
153 <para>Parking space to retrieve a parked call from.
154 If not provided then the first available parked call in the
155 parking lot will be retrieved.</para>
156 </parameter>
157 </syntax>
158 <description>
159 <para>Used to retrieve a parked call from a parking lot.</para>
160 <note>
161 <para>If a parking lot's parkext option is set, then Parking lots
162 will automatically create and manage dialplan extensions in
163 the parking lot context. If that is the case then you will not
164 need to manage parking extensions yourself, just include the
165 parking context of the parking lot.</para>
166 </note>
167 </description>
168 <see-also>
169 <ref type="application">Park</ref>
170 </see-also>
171 </application>
172
173 <application name="ParkAndAnnounce" language="en_US">
174 <synopsis>
175 Park and Announce.
176 </synopsis>
177 <syntax>
178 <parameter name="parking_lot_name">
179 <para>Specify in which parking lot to park a call.</para>
180 <para>The parking lot used is selected in the following order:</para>
181 <para>1) parking_lot_name option to this application</para>
182 <para>2) <variable>PARKINGLOT</variable> variable</para>
183 <para>3) <literal>CHANNEL(parkinglot)</literal> function
184 (Possibly preset by the channel driver.)</para>
185 <para>4) Default parking lot.</para>
186 </parameter>
187 <parameter name="options">
188 <para>A list of options for this parked call.</para>
189 <optionlist>
190 <option name="r">
191 <para>Send ringing instead of MOH to the parked call.</para>
192 </option>
193 <option name="R">
194 <para>Randomize the selection of a parking space.</para>
195 </option>
196 <option name="c" argsep=",">
197 <argument name="context" required="false" />
198 <argument name="extension" required="false" />
199 <argument name="priority" required="true" />
200 <para>If the parking times out, go to this place in the dialplan
201 instead of where the parking lot defines the call should go.
202 </para>
203 </option>
204 <option name="t">
205 <argument name="duration" required="true" />
206 <para>Use a timeout of <literal>duration</literal> seconds instead
207 of the timeout specified by the parking lot.</para>
208 </option>
209 </optionlist>
210 </parameter>
211 <parameter name="announce_template" required="true" argsep=":">
212 <argument name="announce" required="true">
213 <para>Colon-separated list of files to announce. The word
214 <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
215 the call is parked.</para>
216 </argument>
217 <argument name="announce1" multiple="true" />
218 </parameter>
219 <parameter name="dial" required="true">
220 <para>The app_dial style resource to call to make the
221 announcement. Console/dsp calls the console.</para>
222 </parameter>
223 </syntax>
224 <description>
225 <para>Park a call into the parkinglot and announce the call to another channel.</para>
226 <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
227 into which the call was placed. Use with the Local channel to allow the dialplan to make
228 use of this information.</para>
229 </description>
230 <see-also>
231 <ref type="application">Park</ref>
232 <ref type="application">ParkedCall</ref>
233 </see-also>
234 </application>
235 ***/
236
237#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
238
239/* Park a call */
240
245 OPT_ARG_ARRAY_SIZE /* Always the last element of the enum */
247
249 MUXFLAG_RINGING = (1 << 0),
255};
256
264});
265
266static int apply_option_timeout (int *var, char *timeout_arg)
267{
268 if (ast_strlen_zero(timeout_arg)) {
269 ast_log(LOG_ERROR, "No duration value provided for the timeout ('t') option.\n");
270 return -1;
271 }
272
273 if (sscanf(timeout_arg, "%d", var) != 1 || *var < 0) {
274 ast_log(LOG_ERROR, "Duration value provided for timeout ('t') option must be 0 or greater.\n");
275 return -1;
276 }
277
278 return 0;
279}
280
281static int park_app_parse_data(const char *data, int *disable_announce, int *use_ringing, int *randomize, int *time_limit,
282 char **comeback_override, char **lot_name, char **musicclass)
283{
284 char *parse;
285 struct ast_flags flags = { 0 };
286
288 AST_APP_ARG(lot_name);
290 AST_APP_ARG(other); /* Any remaining unused arguments */
291 );
292
293 parse = ast_strdupa(data);
295
296 if (args.options) {
297 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
298 ast_app_parse_options(park_opts, &flags, opts, args.options);
300 if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
301 return -1;
302 }
303 }
304
306 *comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
307 }
308
310 if (disable_announce) {
311 *disable_announce = 1;
312 }
313 }
314
316 *musicclass = ast_strdup(opts[OPT_ARG_MUSICONHOLD]);
317 }
318
320 *use_ringing = 1;
321 }
322
324 *randomize = 1;
325 }
326 }
327
328 if (!ast_strlen_zero(args.lot_name)) {
329 *lot_name = ast_strdup(args.lot_name);
330 }
331
332 return 0;
333}
334
336{
337 if (!datastore) {
338 return;
339 }
340
341 ast_free(datastore->parker_uuid);
342 ast_free(datastore->parker_dial_string);
343 ast_free(datastore->comeback_override);
344 ast_free(datastore);
345}
346
347static void park_common_datastore_destroy(void *data)
348{
349 struct park_common_datastore *datastore = data;
351}
352
354 .type = "park entry data",
356};
357
359{
360 struct ast_datastore *datastore;
361
362 ast_channel_lock(chan);
364 if (datastore) {
365 ast_channel_datastore_remove(chan, datastore);
366 ast_datastore_free(datastore);
367 }
368 ast_channel_unlock(chan);
369}
370
371static 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)
372{
373 struct ast_datastore *datastore = NULL;
374 struct park_common_datastore *park_datastore;
375 const char *attended_transfer;
376 const char *blind_transfer;
377 char *parker_dial_string = NULL;
378
380
381 if (!(datastore = ast_datastore_alloc(&park_common_info, NULL))) {
382 return -1;
383 }
384
385 if (!(park_datastore = ast_calloc(1, sizeof(*park_datastore)))) {
386 ast_datastore_free(datastore);
387 return -1;
388 }
389 datastore->data = park_datastore;
390
391 park_datastore->parker_uuid = ast_strdup(parker_uuid);
392 if (!park_datastore->parker_uuid) {
393 ast_datastore_free(datastore);
394 return -1;
395 }
396
397 ast_channel_lock(parkee);
398 attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER");
399 blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER");
400 if (!ast_strlen_zero(attended_transfer)) {
401 parker_dial_string = ast_strdupa(attended_transfer);
402 } else if (!ast_strlen_zero(blind_transfer)) {
403 parker_dial_string = ast_strdupa(blind_transfer);
404 /* Ensure that attended_transfer is NULL and not an empty string. */
405 attended_transfer = NULL;
406 }
407 ast_channel_unlock(parkee);
408
411 ast_verb(4, "Setting Parker dial string to %s from %s value\n",
413 attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
415 if (!park_datastore->parker_dial_string) {
416 ast_datastore_free(datastore);
417 return -1;
418 }
419 }
420
421 park_datastore->randomize = randomize;
422 park_datastore->time_limit = time_limit;
423 park_datastore->silence_announce = silence_announce;
424
425 if (comeback_override) {
427 if (!park_datastore->comeback_override) {
428 ast_datastore_free(datastore);
429 return -1;
430 }
431 }
432
433
434 ast_channel_lock(parkee);
435 ast_channel_datastore_add(parkee, datastore);
436 ast_channel_unlock(parkee);
437
438 return 0;
439}
440
442{
443 struct ast_datastore *datastore;
444 struct park_common_datastore *data;
445 struct park_common_datastore *data_copy;
446
447 SCOPED_CHANNELLOCK(lock, parkee);
448
449 if (!(datastore = ast_channel_datastore_find(parkee, &park_common_info, NULL))) {
450 return NULL;
451 }
452
453 data = datastore->data;
454
455 /* This data should always be populated if this datastore was appended to the channel */
456 ast_assert(data != NULL);
457
458 data_copy = ast_calloc(1, sizeof(*data_copy));
459 if (!data_copy) {
460 return NULL;
461 }
462
463 data_copy->parker_uuid = ast_strdup(data->parker_uuid);
464 if (!data_copy->parker_uuid) {
466 return NULL;
467 }
468
469 data_copy->randomize = data->randomize;
470 data_copy->time_limit = data->time_limit;
471 data_copy->silence_announce = data->silence_announce;
472
473 if (data->comeback_override) {
475 if (!data_copy->comeback_override) {
477 return NULL;
478 }
479 }
480
481 if (data->parker_dial_string) {
483 if (!data_copy->parker_dial_string) {
485 return NULL;
486 }
487 }
488
489 return data_copy;
490}
491
492static struct ast_bridge *park_common_setup2(struct ast_channel *parkee, struct ast_channel *parker,
493 const char *lot_name, const char *comeback_override, const char *musicclass,
494 int use_ringing, int randomize, int time_limit, int silence_announcements)
495{
496 struct ast_bridge *parking_bridge;
497 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
498
499 if (!parker) {
500 parker = parkee;
501 }
502
503 /* If the name of the parking lot isn't specified in the arguments, find it based on the channel. */
504 if (ast_strlen_zero(lot_name)) {
505 ast_channel_lock(parker);
506 lot_name = ast_strdupa(find_channel_parking_lot_name(parker));
507 ast_channel_unlock(parker);
508 }
509
510 lot = parking_lot_find_by_name(lot_name);
511 if (!lot) {
512 lot = parking_create_dynamic_lot(lot_name, parker);
513 }
514 if (!lot) {
515 ast_log(LOG_ERROR, "Could not find parking lot: '%s'\n", lot_name);
516 return NULL;
517 }
518
519 ao2_lock(lot);
520 parking_bridge = parking_lot_get_bridge(lot);
521 ao2_unlock(lot);
522
523 if (!parking_bridge) {
524 return NULL;
525 }
526
527 /* Apply relevant bridge roles and such to the parking channel */
528 parking_channel_set_roles(parkee, lot, use_ringing);
529 /* If requested, override the MOH class */
530 if (!ast_strlen_zero(musicclass)) {
531 ast_channel_set_bridge_role_option(parkee, "holding_participant", "moh_class", musicclass);
532 }
533 setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
534 silence_announcements);
535 return parking_bridge;
536}
537
538struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
539 const char *lot_name, const char *comeback_override,
540 int use_ringing, int randomize, int time_limit, int silence_announcements)
541{
542 return park_common_setup2(parkee, parker, lot_name, comeback_override, NULL, use_ringing, randomize, time_limit, silence_announcements);
543}
544
545struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
546 int *silence_announcements)
547{
548 int use_ringing = 0;
549 int randomize = 0;
550 int time_limit = -1;
551
552 RAII_VAR(char *, comeback_override, NULL, ast_free);
553 RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
554 RAII_VAR(char *, musicclass, NULL, ast_free);
555
556 if (app_data) {
557 park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg, &musicclass);
558 }
559
560 return park_common_setup2(parkee, parker, lot_name_app_arg, comeback_override, musicclass, use_ringing,
561 randomize, time_limit, silence_announcements ? *silence_announcements : 0);
562
563}
564
565static int park_app_exec(struct ast_channel *chan, const char *data)
566{
567 RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
568
569 struct ast_bridge_features chan_features;
570 int res = 0;
571 int silence_announcements = 0;
572 int blind_transfer;
573
574 /* Answer the channel if needed */
575 if (ast_channel_state(chan) != AST_STATE_UP) {
576 ast_answer(chan);
577 }
578
579 ast_channel_lock(chan);
580 blind_transfer = !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"));
581 ast_channel_unlock(chan);
582
583 /* Handle the common parking setup stuff */
584 if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
585 if (!silence_announcements && !blind_transfer) {
586 ast_stream_and_wait(chan, "pbx-parkingfailed", "");
587 }
589 return 0;
590 }
591
592 /* Initialize bridge features for the channel. */
593 res = ast_bridge_features_init(&chan_features);
594 /* Now for the fun part... park it! */
595 if (res || ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0)) {
596 if (!silence_announcements && !blind_transfer) {
597 ast_stream_and_wait(chan, "pbx-parkingfailed", "");
598 }
599 ast_bridge_features_cleanup(&chan_features);
601 return res;
602 }
603
604 /*
605 * If the bridge was broken for a hangup that isn't real, then
606 * don't run the h extension, because the channel isn't really
607 * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
608 */
609 res = -1;
610
611 ast_channel_lock(chan);
613 res = 0;
614 }
615 ast_channel_unlock(chan);
616
617 ast_bridge_features_cleanup(&chan_features);
618
619 return res;
620}
621
622/* Retrieve a parked call */
623
624static int parked_call_app_exec(struct ast_channel *chan, const char *data)
625{
626 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
627 RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
628 struct ast_bridge *retrieval_bridge;
629 int res;
630 int target_space = -1;
631 struct ast_bridge_features chan_features;
632 char *parse;
633 const char *lot_name;
634
636 AST_APP_ARG(lot_name);
637 AST_APP_ARG(parking_space);
638 AST_APP_ARG(other); /* Any remaining unused arguments */
639 );
640
641 parse = ast_strdupa(data);
643
644 /* Answer the channel if needed */
645 if (ast_channel_state(chan) != AST_STATE_UP) {
646 ast_answer(chan);
647 }
648
649 lot_name = args.lot_name;
650
651 /* If the name of the parking lot isn't in the arguments, find it based on the channel. */
652 if (ast_strlen_zero(lot_name)) {
653 ast_channel_lock(chan);
655 ast_channel_unlock(chan);
656 }
657
658 lot = parking_lot_find_by_name(lot_name);
659 if (!lot) {
660 ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
661 ast_stream_and_wait(chan, "pbx-invalidpark", "");
662 return -1;
663 }
664
665 if (!ast_strlen_zero(args.parking_space)) {
666 if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
667 ast_stream_and_wait(chan, "pbx-invalidpark", "");
668 ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
669 return -1;
670 }
671 }
672
673 /* Attempt to get the parked user from the parking lot */
674 pu = parking_lot_retrieve_parked_user(lot, target_space);
675 if (!pu) {
676 ast_stream_and_wait(chan, "pbx-invalidpark", "");
677 return -1;
678 }
679
680 /* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
681 ast_assert(pu->retriever == NULL);
682 pu->retriever = ast_channel_snapshot_create(chan);
683
684 /* Create bridge */
685 retrieval_bridge = ast_bridge_basic_new();
686 if (!retrieval_bridge) {
687 return -1;
688 }
689
690 /* Move the parkee into the new bridge */
691 if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) {
692 ast_bridge_destroy(retrieval_bridge, 0);
693 return -1;
694 }
695
696 /* Initialize our bridge features */
697 res = ast_bridge_features_init(&chan_features);
698 if (res) {
699 ast_bridge_destroy(retrieval_bridge, 0);
700 ast_bridge_features_cleanup(&chan_features);
701 return -1;
702 }
703
704 /* Set the features */
706
707 /* If the parkedplay option is set for the caller to hear, play that tone now. */
708 if (lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLER) {
709 ast_stream_and_wait(chan, lot->cfg->courtesytone, NULL);
710 }
711
712 /* Now we should try to join the new bridge ourselves... */
713 ast_bridge_join(retrieval_bridge, chan, NULL, &chan_features, NULL,
715
716 ast_bridge_features_cleanup(&chan_features);
717
718 /* Return -1 so that call does not continue in the dialplan. This is to make
719 * behavior consistent with Asterisk versions prior to 12.
720 */
721 return -1;
722}
723
728};
729
731{
732 struct park_announce_subscription_data *pa_data = data;
733 ast_free(pa_data->parkee_uuid);
734 ast_free(pa_data->dial_string);
735 ast_free(pa_data->announce_string);
736 ast_free(pa_data);
737}
738
740 const char *dial_string,
741 const char *announce_string)
742{
743 struct park_announce_subscription_data *pa_data;
744
745 if (!(pa_data = ast_calloc(1, sizeof(*pa_data)))) {
746 return NULL;
747 }
748
749 if (!(pa_data->parkee_uuid = ast_strdup(parkee_uuid))
750 || !(pa_data->dial_string = ast_strdup(dial_string))
751 || !(pa_data->announce_string = ast_strdup(announce_string))) {
753 return NULL;
754 }
755
756 return pa_data;
757}
758
759/*! \internal
760 * \brief Gathers inheritable channel variables from a channel by name.
761 *
762 * \param oh outgoing helper struct we are bestowing inheritable variables to
763 * \param channel_id name or uniqueID of the channel to inherit variables from
764 */
765static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
766{
767 struct ast_channel *chan = ast_channel_get_by_name(channel_id);
768 struct ast_var_t *current;
769 struct ast_variable *newvar;
770 const char *varname;
771 int vartype;
772
773
774 if (!chan) {
775 /* Already gone */
776 return;
777 }
778
779 ast_channel_lock(chan);
780
781 AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
782 varname = ast_var_full_name(current);
783 if (!varname) {
784 continue;
785 }
786
787 vartype = 0;
788 if (varname[0] == '_') {
789 vartype = 1;
790 if (varname[1] == '_') {
791 vartype = 2;
792 }
793 }
794
795 switch (vartype) {
796 case 1:
797 newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
798 break;
799 case 2:
800 newvar = ast_variable_new(varname, ast_var_value(current), "");
801 break;
802 default:
803 continue;
804 }
805 if (newvar) {
806 ast_debug(1, "Inheriting variable %s from %s.\n",
807 newvar->name, ast_channel_name(chan));
808 if (oh->vars) {
809 newvar->next = oh->vars;
810 oh->vars = newvar;
811 }
812 }
813 }
814
815 ast_channel_unlock(chan);
817}
818
819static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
820{
821 struct ast_channel *dchan;
822 struct outgoing_helper oh = { 0, };
823 int outstate;
825 char buf[13];
826 char *dial_tech;
827 char *cur_announce;
828
829 dial_tech = strsep(&dial_string, "/");
830 ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
831
832 if (!cap_slin) {
833 ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
834 goto announce_cleanup;
835 }
837
838 snprintf(buf, sizeof(buf), "%d", parkingspace);
839 oh.vars = ast_variable_new("_PARKEDAT", buf, "");
840
841 inherit_channel_vars_from_id(&oh, parkee_snapshot->base->uniqueid);
842
843 dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
844 &outstate,
845 parkee_snapshot->caller->number,
846 parkee_snapshot->caller->name,
847 &oh);
848
850 if (!dchan) {
851 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
852 goto announce_cleanup;
853 }
854
855 ast_verb(4, "Announce Template: %s\n", announce_string);
856
857 for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
858 ast_verb(4, "Announce:%s\n", cur_announce);
859 if (!strcmp(cur_announce, "PARKED")) {
860 ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
861 } else {
862 int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
863 if (!dres) {
864 dres = ast_waitstream(dchan, "");
865 } else {
866 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
867 }
868 }
869 }
870
871 ast_stopstream(dchan);
872 ast_hangup(dchan);
873
874announce_cleanup:
875 ao2_cleanup(cap_slin);
876}
877
879{
880 struct park_announce_subscription_data *pa_data = data;
881 char *dial_string = pa_data->dial_string;
882
884
887 return;
888 }
889
891 return;
892 }
893
894 if (payload->event_type != PARKED_CALL) {
895 /* We are only concerned with calls parked */
896 return;
897 }
898
899 if (strcmp(payload->parkee->base->uniqueid, pa_data->parkee_uuid)) {
900 /* We are only concerned with the parkee we are subscribed for. */
901 return;
902 }
903
904 if (!ast_strlen_zero(dial_string)) {
905 announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
906 }
907
908 *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
909}
910
911static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
912{
913 struct ast_bridge_features chan_features;
914 char *parse;
915 int res;
916 int silence_announcements = 1;
917
919 struct park_announce_subscription_data *pa_data;
920
921 RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
922
924 AST_APP_ARG(lot_name);
926 AST_APP_ARG(announce_template);
927 AST_APP_ARG(dial);
928 AST_APP_ARG(others);/* Any remaining unused arguments */
929 );
930
931 if (ast_strlen_zero(data)) {
932 ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
933 return -1;
934 }
935
936 parse = ast_strdupa(data);
938
939 if (ast_strlen_zero(args.announce_template)) {
940 /* improperly configured arguments for the application */
941 ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
942 return -1;
943 }
944
945 if (ast_strlen_zero(args.dial)) {
946 /* improperly configured arguments */
947 ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
948 return -1;
949 }
950
951 if (!strchr(args.dial, '/')) {
952 ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
953 return -1;
954 }
955
956 /* Handle the common parking setup stuff */
957 if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
958 return 0;
959 }
960
961 /* Initialize bridge features for the channel. */
962 res = ast_bridge_features_init(&chan_features);
963 if (res) {
964 ast_bridge_features_cleanup(&chan_features);
965 return -1;
966 }
967
968 /* subscribe to the parking message so that we can announce once it is parked */
969 pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
970 if (!pa_data) {
971 return -1;
972 }
973
975 /* Failed to create subscription */
977 return -1;
978 }
979
983
984 /* Now for the fun part... park it! */
985 ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
986
987 /* Toss the subscription since we aren't bridged at this point. */
989
990 /*
991 * If the bridge was broken for a hangup that isn't real, then
992 * don't run the h extension, because the channel isn't really
993 * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
994 */
995 res = -1;
996
997 ast_channel_lock(chan);
999 res = 0;
1000 }
1001 ast_channel_unlock(chan);
1002
1003 ast_bridge_features_cleanup(&chan_features);
1004
1005 return res;
1006}
1007
1009{
1011 return -1;
1012 }
1013
1015 return -1;
1016 }
1017
1019 return -1;
1020 }
1021
1022 return 0;
1023}
1024
1026{
1030}
ast_mutex_t lock
Definition: app_sla.c:331
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1621
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
@ AST_BRIDGE_JOIN_PASS_REFERENCE
Definition: bridge.h:535
int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery)
Move a channel from one bridge to another.
Definition: bridge.c:2460
Basic bridge subclass API.
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3620
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3653
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
Set a role option on a channel.
Definition: bridge_roles.c:375
static struct stasis_forward * parking_subscription
Our subscription for parking.
Definition: cdr.c:422
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
struct varshead * ast_channel_varshead(struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2922
struct ast_channel * __ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
Request a channel of a given type, with data as optional information used by the low level module and...
Definition: channel.c:5975
const char * ast_channel_uniqueid(const struct ast_channel *chan)
void ast_channel_name_to_dial_string(char *channel_name)
Removes the trailing identifiers from a channel name string.
Definition: channel.c:6838
@ AST_SOFTHANGUP_ASYNCGOTO
Definition: channel.h:1126
const char * ast_channel_language(const struct ast_channel *chan)
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
#define ast_channel_unlock(chan)
Definition: channel.h:2923
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
const char * ast_var_full_name(const struct ast_var_t *var)
Definition: chanvars.c:75
const char * ast_var_value(const struct ast_var_t *var)
Definition: chanvars.c:80
Configuration option-handling.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Call Parking and Pickup API Includes code and algorithms from the Zapata library.
@ AST_FEATURE_FLAG_BYCALLER
Definition: features.h:38
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1877
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1839
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
struct stasis_topic * ast_parking_topic(void)
accessor for the parking stasis topic
Definition: parking.c:67
struct stasis_message_type * ast_parked_call_type(void)
accessor for the parked call stasis message type
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
struct ast_channel_snapshot * ast_channel_snapshot_create(struct ast_channel *chan)
Generate a snapshot of the channel state. This is an ao2 object, so ao2_cleanup() to deallocate.
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
char * strsep(char **str, const char *delims)
Configuration File Parser.
#define ast_variable_new(name, value, filename)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:619
size_t current
Definition: main/cli.c:113
Asterisk module definitions.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
@ PARKED_CALL
Definition: parking.h:47
#define PARK_APPLICATION
The default parking application that Asterisk expects.
Definition: parking.h:35
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)
void park_common_datastore_free(struct park_common_datastore *datastore)
Free a park common datastore struct.
static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
struct ast_bridge * park_common_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *lot_name, const char *comeback_override, int use_ringing, int randomize, int time_limit, int silence_announcements)
Setup a parked call on a parking bridge without needing to parse appdata.
void unload_parking_applications(void)
Unregister parking applications.
static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
int load_parking_applications(void)
Register parking applications.
static void park_announce_subscription_data_destroy(void *data)
static void park_common_datastore_destroy(void *data)
static int park_app_exec(struct ast_channel *chan, const char *data)
@ OPT_ARG_TIMEOUT
@ OPT_ARG_MUSICONHOLD
@ OPT_ARG_COMEBACK
@ OPT_ARG_ARRAY_SIZE
static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
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, char **musicclass)
static struct park_announce_subscription_data * park_announce_subscription_data_create(const char *parkee_uuid, const char *dial_string, const char *announce_string)
static void wipe_park_common_datastore(struct ast_channel *chan)
struct park_common_datastore * get_park_common_datastore_copy(struct ast_channel *parkee)
Get a copy of the park_common_datastore from a channel that is being parked.
struct ast_bridge * park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data, int *silence_announcements)
Function to prepare a channel for parking by determining which parking bridge should be used,...
static const struct ast_datastore_info park_common_info
static const struct ast_app_option park_opts[128]
static int parked_call_app_exec(struct ast_channel *chan, const char *data)
@ MUXFLAG_RINGING
@ MUXFLAG_MUSICONHOLD
@ MUXFLAG_COMEBACK_OVERRIDE
@ MUXFLAG_TIMEOUT_OVERRIDE
@ MUXFLAG_RANDOMIZE
@ MUXFLAG_NOANNOUNCE
#define PARK_AND_ANNOUNCE_APPLICATION
static struct ast_bridge * park_common_setup2(struct ast_channel *parkee, struct ast_channel *parker, const char *lot_name, const char *comeback_override, const char *musicclass, int use_ringing, int randomize, int time_limit, int silence_announcements)
static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
static int apply_option_timeout(int *var, char *timeout_arg)
void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode)
Apply features based on the parking lot feature options.
int parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing)
Set necessary bridge roles on a channel that is about to enter a parking lot.
struct parked_user * parking_lot_retrieve_parked_user(struct parking_lot *lot, int target)
Determine if there is a parked user in a parking space and pull it from the parking lot if there is.
struct ast_bridge * parking_lot_get_bridge(struct parking_lot *lot)
Get a reference to a parking lot's bridge. If it doesn't exist, create it and get a reference.
void publish_parked_call_failure(struct ast_channel *parkee)
Publish a stasis parked call message for the channel indicating failure to park.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct stasis_forward * sub
Definition: res_corosync.c:240
Call Parking Resource Internal API.
#define PARKED_CALL_APPLICATION
Definition: res_parking.h:38
struct parking_lot * parking_create_dynamic_lot(const char *name, struct ast_channel *chan)
Create a dynamic parking lot.
Definition: res_parking.c:1060
const char * find_channel_parking_lot_name(struct ast_channel *chan)
Find parking lot name from channel.
Definition: res_parking.c:608
struct parking_lot * parking_lot_find_by_name(const char *lot_name)
Find a parking lot based on its name.
Definition: res_parking.c:602
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
says digits
Definition: channel.c:8252
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition: stasis.h:297
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1023
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1077
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:680
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
Definition: stasis.c:1174
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Structure that contains features information.
Structure that contains information about a bridge.
Definition: bridge.h:349
const ast_string_field uniqueid
const ast_string_field number
const ast_string_field name
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_base * base
struct ast_channel_snapshot_caller * caller
Main Channel structure associated with a channel.
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
A parked call message payload.
Definition: parking.h:59
unsigned int parkingspace
Definition: parking.h:65
struct ast_channel_snapshot * parkee
Definition: parking.h:60
enum ast_parked_call_event_type event_type
Definition: parking.h:62
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
struct ast_variable * vars
Definition: channel.h:1112
const char * args
static struct test_options options
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739