Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
43 <version>12.0.0</version>
44 </since>
45 <synopsis>
46 Park yourself.
47 </synopsis>
48 <syntax>
49 <parameter name="parking_lot_name">
50 <para>Specify in which parking lot to park a call.</para>
51 <para>The parking lot used is selected in the following order:</para>
52 <para>1) parking_lot_name option to this application</para>
53 <para>2) <variable>PARKINGLOT</variable> variable</para>
54 <para>3) <literal>CHANNEL(parkinglot)</literal> function
55 (Possibly preset by the channel driver.)</para>
56 <para>4) Default parking lot.</para>
57 </parameter>
58 <parameter name="options">
59 <para>A list of options for this parked call.</para>
60 <optionlist>
61 <option name="r">
62 <para>Send ringing instead of MOH to the parked call.</para>
63 </option>
64 <option name="R">
65 <para>Randomize the selection of a parking space.</para>
66 </option>
67 <option name="s">
68 <para>Silence announcement of the parking space number.</para>
69 </option>
70 <option name="c" argsep=",">
71 <argument name="context" required="false" />
72 <argument name="extension" required="false" />
73 <argument name="priority" required="true" />
74 <para>If the parking times out, go to this place in the dialplan
75 instead of where the parking lot defines the call should go.
76 </para>
77 </option>
78 <option name="t">
79 <argument name="duration" required="true" />
80 <para>Use a timeout of <literal>duration</literal> seconds instead
81 of the timeout specified by the parking lot.</para>
82 </option>
83 </optionlist>
84 </parameter>
85 </syntax>
86 <description>
87 <para>Used to park yourself (typically in combination with an attended
88 transfer to know the parking space).</para>
89 <para>If you set the <variable>PARKINGEXTEN</variable> variable to a
90 parking space extension in the parking lot, Park() will attempt to park the
91 call on that extension. If the extension is already in use then execution
92 will continue at the next priority.
93 </para>
94 <para>If the <literal>parkeddynamic</literal> option is enabled in
95 <filename>res_parking.conf</filename> the following variables can be
96 used to dynamically create new parking lots. When using dynamic parking
97 lots, be aware of the conditions as explained in the notes section
98 below.
99 </para>
100 <para>The <variable>PARKINGDYNAMIC</variable> variable specifies the
101 parking lot to use as a template to create a dynamic parking lot. It
102 is an error to specify a non-existent parking lot for the template.
103 If not set then the default parking lot is used as the template.
104 </para>
105 <para>The <variable>PARKINGDYNCONTEXT</variable> variable specifies the
106 dialplan context to use for the newly created dynamic parking lot. If
107 not set then the context from the parking lot template is used. The
108 context is created if it does not already exist and the new parking lot
109 needs to create extensions.
110 </para>
111 <para>The <variable>PARKINGDYNEXTEN</variable> variable specifies the
112 <literal>parkext</literal> to use for the newly created dynamic
113 parking lot. If not set then the <literal>parkext</literal> is used from
114 the parking lot template. If the template does not specify a
115 <literal>parkext</literal> then no extensions are created for the newly
116 created parking lot. The dynamic parking lot cannot be created if it
117 needs to create extensions that overlap existing parking lot extensions.
118 The only exception to this is for the <literal>parkext</literal>
119 extension and only if neither of the overlaping parking lot's
120 <literal>parkext</literal> is exclusive.
121 </para>
122 <para>The <variable>PARKINGDYNPOS</variable> variable specifies the
123 parking positions to use for the newly created dynamic parking lot. If
124 not set then the <literal>parkpos</literal> from the parking lot template
125 is used.
126 </para>
127 <note>
128 <para>This application must be used as the first extension priority
129 to be recognized as a parking access extension for blind transfers.
130 Blind transfers and the DTMF one-touch parking feature need this
131 distinction to operate properly. The parking access extension in
132 this case is treated like a dialplan hint.
133 </para>
134 </note>
135 </description>
136 <see-also>
137 <ref type="application">ParkedCall</ref>
138 </see-also>
139 </application>
140
141 <application name="ParkedCall" language="en_US">
142 <since>
143 <version>12.0.0</version>
144 </since>
145 <synopsis>
146 Retrieve a parked call.
147 </synopsis>
148 <syntax>
149 <parameter name="parking_lot_name">
150 <para>Specify from which parking lot to retrieve a parked call.</para>
151 <para>The parking lot used is selected in the following order:</para>
152 <para>1) parking_lot_name option</para>
153 <para>2) <variable>PARKINGLOT</variable> variable</para>
154 <para>3) <literal>CHANNEL(parkinglot)</literal> function
155 (Possibly preset by the channel driver.)</para>
156 <para>4) Default parking lot.</para>
157 </parameter>
158 <parameter name="parking_space">
159 <para>Parking space to retrieve a parked call from.
160 If not provided then the first available parked call in the
161 parking lot will be retrieved.</para>
162 </parameter>
163 </syntax>
164 <description>
165 <para>Used to retrieve a parked call from a parking lot.</para>
166 <note>
167 <para>If a parking lot's parkext option is set, then Parking lots
168 will automatically create and manage dialplan extensions in
169 the parking lot context. If that is the case then you will not
170 need to manage parking extensions yourself, just include the
171 parking context of the parking lot.</para>
172 </note>
173 </description>
174 <see-also>
175 <ref type="application">Park</ref>
176 </see-also>
177 </application>
178
179 <application name="ParkAndAnnounce" language="en_US">
180 <since>
181 <version>12.0.0</version>
182 </since>
183 <synopsis>
184 Park and Announce.
185 </synopsis>
186 <syntax>
187 <parameter name="parking_lot_name">
188 <para>Specify in which parking lot to park a call.</para>
189 <para>The parking lot used is selected in the following order:</para>
190 <para>1) parking_lot_name option to this application</para>
191 <para>2) <variable>PARKINGLOT</variable> variable</para>
192 <para>3) <literal>CHANNEL(parkinglot)</literal> function
193 (Possibly preset by the channel driver.)</para>
194 <para>4) Default parking lot.</para>
195 </parameter>
196 <parameter name="options">
197 <para>A list of options for this parked call.</para>
198 <optionlist>
199 <option name="r">
200 <para>Send ringing instead of MOH to the parked call.</para>
201 </option>
202 <option name="R">
203 <para>Randomize the selection of a parking space.</para>
204 </option>
205 <option name="c" argsep=",">
206 <argument name="context" required="false" />
207 <argument name="extension" required="false" />
208 <argument name="priority" required="true" />
209 <para>If the parking times out, go to this place in the dialplan
210 instead of where the parking lot defines the call should go.
211 </para>
212 </option>
213 <option name="t">
214 <argument name="duration" required="true" />
215 <para>Use a timeout of <literal>duration</literal> seconds instead
216 of the timeout specified by the parking lot.</para>
217 </option>
218 </optionlist>
219 </parameter>
220 <parameter name="announce_template" required="true" argsep=":">
221 <argument name="announce" required="true">
222 <para>Colon-separated list of files to announce. The word
223 <literal>PARKED</literal> will be replaced by a say_digits of the extension in which
224 the call is parked.</para>
225 </argument>
226 <argument name="announce1" multiple="true" />
227 </parameter>
228 <parameter name="dial" required="true">
229 <para>The app_dial style resource to call to make the
230 announcement. Console/dsp calls the console.</para>
231 </parameter>
232 </syntax>
233 <description>
234 <para>Park a call into the parkinglot and announce the call to another channel.</para>
235 <para>The variable <variable>PARKEDAT</variable> will contain the parking extension
236 into which the call was placed. Use with the Local channel to allow the dialplan to make
237 use of this information.</para>
238 </description>
239 <see-also>
240 <ref type="application">Park</ref>
241 <ref type="application">ParkedCall</ref>
242 </see-also>
243 </application>
244 ***/
245
246#define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
247
248/* Park a call */
249
254 OPT_ARG_ARRAY_SIZE /* Always the last element of the enum */
256
258 MUXFLAG_RINGING = (1 << 0),
264};
265
273});
274
275static int apply_option_timeout (int *var, char *timeout_arg)
276{
277 if (ast_strlen_zero(timeout_arg)) {
278 ast_log(LOG_ERROR, "No duration value provided for the timeout ('t') option.\n");
279 return -1;
280 }
281
282 if (sscanf(timeout_arg, "%d", var) != 1 || *var < 0) {
283 ast_log(LOG_ERROR, "Duration value provided for timeout ('t') option must be 0 or greater.\n");
284 return -1;
285 }
286
287 return 0;
288}
289
290static int park_app_parse_data(const char *data, int *disable_announce, int *use_ringing, int *randomize, int *time_limit,
291 char **comeback_override, char **lot_name, char **musicclass)
292{
293 char *parse;
294 struct ast_flags flags = { 0 };
295
297 AST_APP_ARG(lot_name);
299 AST_APP_ARG(other); /* Any remaining unused arguments */
300 );
301
302 parse = ast_strdupa(data);
304
305 if (args.options) {
306 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
307 ast_app_parse_options(park_opts, &flags, opts, args.options);
309 if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
310 return -1;
311 }
312 }
313
315 *comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
316 }
317
319 if (disable_announce) {
320 *disable_announce = 1;
321 }
322 }
323
325 *musicclass = ast_strdup(opts[OPT_ARG_MUSICONHOLD]);
326 }
327
329 *use_ringing = 1;
330 }
331
333 *randomize = 1;
334 }
335 }
336
337 if (!ast_strlen_zero(args.lot_name)) {
338 *lot_name = ast_strdup(args.lot_name);
339 }
340
341 return 0;
342}
343
345{
346 if (!datastore) {
347 return;
348 }
349
350 ast_free(datastore->parker_uuid);
351 ast_free(datastore->parker_dial_string);
352 ast_free(datastore->comeback_override);
353 ast_free(datastore);
354}
355
356static void park_common_datastore_destroy(void *data)
357{
358 struct park_common_datastore *datastore = data;
360}
361
363 .type = "park entry data",
365};
366
368{
369 struct ast_datastore *datastore;
370
371 ast_channel_lock(chan);
373 if (datastore) {
374 ast_channel_datastore_remove(chan, datastore);
375 ast_datastore_free(datastore);
376 }
377 ast_channel_unlock(chan);
378}
379
380static 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)
381{
382 struct ast_datastore *datastore = NULL;
383 struct park_common_datastore *park_datastore;
384 const char *attended_transfer;
385 const char *blind_transfer;
386 char *parker_dial_string = NULL;
387
389
390 if (!(datastore = ast_datastore_alloc(&park_common_info, NULL))) {
391 return -1;
392 }
393
394 if (!(park_datastore = ast_calloc(1, sizeof(*park_datastore)))) {
395 ast_datastore_free(datastore);
396 return -1;
397 }
398 datastore->data = park_datastore;
399
400 park_datastore->parker_uuid = ast_strdup(parker_uuid);
401 if (!park_datastore->parker_uuid) {
402 ast_datastore_free(datastore);
403 return -1;
404 }
405
406 ast_channel_lock(parkee);
407 attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER");
408 blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER");
409 if (!ast_strlen_zero(attended_transfer)) {
410 parker_dial_string = ast_strdupa(attended_transfer);
411 } else if (!ast_strlen_zero(blind_transfer)) {
412 parker_dial_string = ast_strdupa(blind_transfer);
413 /* Ensure that attended_transfer is NULL and not an empty string. */
414 attended_transfer = NULL;
415 }
416 ast_channel_unlock(parkee);
417
420 ast_verb(4, "Setting Parker dial string to %s from %s value\n",
422 attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
424 if (!park_datastore->parker_dial_string) {
425 ast_datastore_free(datastore);
426 return -1;
427 }
428 }
429
430 park_datastore->randomize = randomize;
431 park_datastore->time_limit = time_limit;
432 park_datastore->silence_announce = silence_announce;
433
434 if (comeback_override) {
436 if (!park_datastore->comeback_override) {
437 ast_datastore_free(datastore);
438 return -1;
439 }
440 }
441
442
443 ast_channel_lock(parkee);
444 ast_channel_datastore_add(parkee, datastore);
445 ast_channel_unlock(parkee);
446
447 return 0;
448}
449
451{
452 struct ast_datastore *datastore;
453 struct park_common_datastore *data;
454 struct park_common_datastore *data_copy;
455
456 SCOPED_CHANNELLOCK(lock, parkee);
457
458 if (!(datastore = ast_channel_datastore_find(parkee, &park_common_info, NULL))) {
459 return NULL;
460 }
461
462 data = datastore->data;
463
464 /* This data should always be populated if this datastore was appended to the channel */
465 ast_assert(data != NULL);
466
467 data_copy = ast_calloc(1, sizeof(*data_copy));
468 if (!data_copy) {
469 return NULL;
470 }
471
472 data_copy->parker_uuid = ast_strdup(data->parker_uuid);
473 if (!data_copy->parker_uuid) {
475 return NULL;
476 }
477
478 data_copy->randomize = data->randomize;
479 data_copy->time_limit = data->time_limit;
480 data_copy->silence_announce = data->silence_announce;
481
482 if (data->comeback_override) {
484 if (!data_copy->comeback_override) {
486 return NULL;
487 }
488 }
489
490 if (data->parker_dial_string) {
492 if (!data_copy->parker_dial_string) {
494 return NULL;
495 }
496 }
497
498 return data_copy;
499}
500
501static struct ast_bridge *park_common_setup2(struct ast_channel *parkee, struct ast_channel *parker,
502 const char *lot_name, const char *comeback_override, const char *musicclass,
503 int use_ringing, int randomize, int time_limit, int silence_announcements)
504{
505 struct ast_bridge *parking_bridge;
506 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
507
508 if (!parker) {
509 parker = parkee;
510 }
511
512 /* If the name of the parking lot isn't specified in the arguments, find it based on the channel. */
513 if (ast_strlen_zero(lot_name)) {
514 ast_channel_lock(parker);
515 lot_name = ast_strdupa(find_channel_parking_lot_name(parker));
516 ast_channel_unlock(parker);
517 }
518
519 lot = parking_lot_find_by_name(lot_name);
520 if (!lot) {
521 lot = parking_create_dynamic_lot(lot_name, parker);
522 }
523 if (!lot) {
524 ast_log(LOG_ERROR, "Could not find parking lot: '%s'\n", lot_name);
525 return NULL;
526 }
527
528 ao2_lock(lot);
529 parking_bridge = parking_lot_get_bridge(lot);
530 ao2_unlock(lot);
531
532 if (!parking_bridge) {
533 return NULL;
534 }
535
536 /* Apply relevant bridge roles and such to the parking channel */
537 parking_channel_set_roles(parkee, lot, use_ringing);
538 /* If requested, override the MOH class */
539 if (!ast_strlen_zero(musicclass)) {
540 ast_channel_set_bridge_role_option(parkee, "holding_participant", "moh_class", musicclass);
541 }
542 setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
543 silence_announcements);
544 return parking_bridge;
545}
546
547struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
548 const char *lot_name, const char *comeback_override,
549 int use_ringing, int randomize, int time_limit, int silence_announcements)
550{
551 return park_common_setup2(parkee, parker, lot_name, comeback_override, NULL, use_ringing, randomize, time_limit, silence_announcements);
552}
553
554struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
555 int *silence_announcements)
556{
557 int use_ringing = 0;
558 int randomize = 0;
559 int time_limit = -1;
560
561 RAII_VAR(char *, comeback_override, NULL, ast_free);
562 RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
563 RAII_VAR(char *, musicclass, NULL, ast_free);
564
565 if (app_data) {
566 park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg, &musicclass);
567 }
568
569 return park_common_setup2(parkee, parker, lot_name_app_arg, comeback_override, musicclass, use_ringing,
570 randomize, time_limit, silence_announcements ? *silence_announcements : 0);
571
572}
573
574static int park_app_exec(struct ast_channel *chan, const char *data)
575{
576 RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
577
578 struct ast_bridge_features chan_features;
579 int res = 0;
580 int silence_announcements = 0;
581 int blind_transfer;
582
583 /* Answer the channel if needed */
584 if (ast_channel_state(chan) != AST_STATE_UP) {
585 ast_answer(chan);
586 }
587
588 ast_channel_lock(chan);
589 blind_transfer = !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"));
590 ast_channel_unlock(chan);
591
592 /* Handle the common parking setup stuff */
593 if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
594 if (!silence_announcements && !blind_transfer) {
595 ast_stream_and_wait(chan, "pbx-parkingfailed", "");
596 }
598 return 0;
599 }
600
601 /* Initialize bridge features for the channel. */
602 res = ast_bridge_features_init(&chan_features);
603 /* Now for the fun part... park it! */
604 if (res || ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0)) {
605 if (!silence_announcements && !blind_transfer) {
606 ast_stream_and_wait(chan, "pbx-parkingfailed", "");
607 }
608 ast_bridge_features_cleanup(&chan_features);
610 return res;
611 }
612
613 /*
614 * If the bridge was broken for a hangup that isn't real, then
615 * don't run the h extension, because the channel isn't really
616 * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
617 */
618 res = -1;
619
620 ast_channel_lock(chan);
622 res = 0;
623 }
624 ast_channel_unlock(chan);
625
626 ast_bridge_features_cleanup(&chan_features);
627
628 return res;
629}
630
631/* Retrieve a parked call */
632
633static int parked_call_app_exec(struct ast_channel *chan, const char *data)
634{
635 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
636 RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
637 struct ast_bridge *retrieval_bridge;
638 int res;
639 int target_space = -1;
640 struct ast_bridge_features chan_features;
641 char *parse;
642 const char *lot_name;
643
645 AST_APP_ARG(lot_name);
646 AST_APP_ARG(parking_space);
647 AST_APP_ARG(other); /* Any remaining unused arguments */
648 );
649
650 parse = ast_strdupa(data);
652
653 /* Answer the channel if needed */
654 if (ast_channel_state(chan) != AST_STATE_UP) {
655 ast_answer(chan);
656 }
657
658 lot_name = args.lot_name;
659
660 /* If the name of the parking lot isn't in the arguments, find it based on the channel. */
661 if (ast_strlen_zero(lot_name)) {
662 ast_channel_lock(chan);
664 ast_channel_unlock(chan);
665 }
666
667 lot = parking_lot_find_by_name(lot_name);
668 if (!lot) {
669 ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
670 ast_stream_and_wait(chan, "pbx-invalidpark", "");
671 return -1;
672 }
673
674 if (!ast_strlen_zero(args.parking_space)) {
675 if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
676 ast_stream_and_wait(chan, "pbx-invalidpark", "");
677 ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
678 return -1;
679 }
680 }
681
682 /* Attempt to get the parked user from the parking lot */
683 pu = parking_lot_retrieve_parked_user(lot, target_space);
684 if (!pu) {
685 ast_stream_and_wait(chan, "pbx-invalidpark", "");
686 return -1;
687 }
688
689 /* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
690 ast_assert(pu->retriever == NULL);
691 pu->retriever = ast_channel_snapshot_create(chan);
692
693 /* Create bridge */
694 retrieval_bridge = ast_bridge_basic_new();
695 if (!retrieval_bridge) {
696 return -1;
697 }
698
699 /* Move the parkee into the new bridge */
700 if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) {
701 ast_bridge_destroy(retrieval_bridge, 0);
702 return -1;
703 }
704
705 /* Initialize our bridge features */
706 res = ast_bridge_features_init(&chan_features);
707 if (res) {
708 ast_bridge_destroy(retrieval_bridge, 0);
709 ast_bridge_features_cleanup(&chan_features);
710 return -1;
711 }
712
713 /* Set the features */
715
716 /* If the parkedplay option is set for the caller to hear, play that tone now. */
717 if (lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLER) {
718 ast_stream_and_wait(chan, lot->cfg->courtesytone, NULL);
719 }
720
721 /* Now we should try to join the new bridge ourselves... */
722 ast_bridge_join(retrieval_bridge, chan, NULL, &chan_features, NULL,
724
725 ast_bridge_features_cleanup(&chan_features);
726
727 /* Return -1 so that call does not continue in the dialplan. This is to make
728 * behavior consistent with Asterisk versions prior to 12.
729 */
730 return -1;
731}
732
737};
738
740{
741 struct park_announce_subscription_data *pa_data = data;
742 ast_free(pa_data->parkee_uuid);
743 ast_free(pa_data->dial_string);
744 ast_free(pa_data->announce_string);
745 ast_free(pa_data);
746}
747
749 const char *dial_string,
750 const char *announce_string)
751{
752 struct park_announce_subscription_data *pa_data;
753
754 if (!(pa_data = ast_calloc(1, sizeof(*pa_data)))) {
755 return NULL;
756 }
757
758 if (!(pa_data->parkee_uuid = ast_strdup(parkee_uuid))
759 || !(pa_data->dial_string = ast_strdup(dial_string))
760 || !(pa_data->announce_string = ast_strdup(announce_string))) {
762 return NULL;
763 }
764
765 return pa_data;
766}
767
768/*! \internal
769 * \brief Gathers inheritable channel variables from a channel by name.
770 *
771 * \param oh outgoing helper struct we are bestowing inheritable variables to
772 * \param channel_id name or uniqueID of the channel to inherit variables from
773 */
774static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
775{
776 struct ast_channel *chan = ast_channel_get_by_name(channel_id);
777 struct ast_var_t *current;
778 struct ast_variable *newvar;
779 const char *varname;
780 int vartype;
781
782
783 if (!chan) {
784 /* Already gone */
785 return;
786 }
787
788 ast_channel_lock(chan);
789
790 AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
791 varname = ast_var_full_name(current);
792 if (!varname) {
793 continue;
794 }
795
796 vartype = 0;
797 if (varname[0] == '_') {
798 vartype = 1;
799 if (varname[1] == '_') {
800 vartype = 2;
801 }
802 }
803
804 switch (vartype) {
805 case 1:
806 newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
807 break;
808 case 2:
809 newvar = ast_variable_new(varname, ast_var_value(current), "");
810 break;
811 default:
812 continue;
813 }
814 if (newvar) {
815 ast_debug(1, "Inheriting variable %s from %s.\n",
816 newvar->name, ast_channel_name(chan));
817 if (oh->vars) {
818 newvar->next = oh->vars;
819 oh->vars = newvar;
820 }
821 }
822 }
823
824 ast_channel_unlock(chan);
826}
827
828static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
829{
830 struct ast_channel *dchan;
831 struct outgoing_helper oh = { 0, };
832 int outstate;
834 char buf[13];
835 char *dial_tech;
836 char *cur_announce;
837
838 dial_tech = strsep(&dial_string, "/");
839 ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
840
841 if (!cap_slin) {
842 ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
843 goto announce_cleanup;
844 }
846
847 snprintf(buf, sizeof(buf), "%d", parkingspace);
848 oh.vars = ast_variable_new("_PARKEDAT", buf, "");
849
850 inherit_channel_vars_from_id(&oh, parkee_snapshot->base->uniqueid);
851
852 dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
853 &outstate,
854 parkee_snapshot->caller->number,
855 parkee_snapshot->caller->name,
856 &oh);
857
859 if (!dchan) {
860 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
861 goto announce_cleanup;
862 }
863
864 ast_verb(4, "Announce Template: %s\n", announce_string);
865
866 for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
867 ast_verb(4, "Announce:%s\n", cur_announce);
868 if (!strcmp(cur_announce, "PARKED")) {
869 ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
870 } else {
871 int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
872 if (!dres) {
873 dres = ast_waitstream(dchan, "");
874 } else {
875 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
876 }
877 }
878 }
879
880 ast_stopstream(dchan);
881 ast_hangup(dchan);
882
883announce_cleanup:
884 ao2_cleanup(cap_slin);
885}
886
888{
889 struct park_announce_subscription_data *pa_data = data;
890 char *dial_string = pa_data->dial_string;
891
893
896 return;
897 }
898
900 return;
901 }
902
903 if (payload->event_type != PARKED_CALL) {
904 /* We are only concerned with calls parked */
905 return;
906 }
907
908 if (strcmp(payload->parkee->base->uniqueid, pa_data->parkee_uuid)) {
909 /* We are only concerned with the parkee we are subscribed for. */
910 return;
911 }
912
913 if (!ast_strlen_zero(dial_string)) {
914 announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
915 }
916
917 *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
918}
919
920static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
921{
922 struct ast_bridge_features chan_features;
923 char *parse;
924 int res;
925 int silence_announcements = 1;
926
928 struct park_announce_subscription_data *pa_data;
929
930 RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
931
933 AST_APP_ARG(lot_name);
935 AST_APP_ARG(announce_template);
936 AST_APP_ARG(dial);
937 AST_APP_ARG(others);/* Any remaining unused arguments */
938 );
939
940 if (ast_strlen_zero(data)) {
941 ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
942 return -1;
943 }
944
945 parse = ast_strdupa(data);
947
948 if (ast_strlen_zero(args.announce_template)) {
949 /* improperly configured arguments for the application */
950 ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
951 return -1;
952 }
953
954 if (ast_strlen_zero(args.dial)) {
955 /* improperly configured arguments */
956 ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
957 return -1;
958 }
959
960 if (!strchr(args.dial, '/')) {
961 ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
962 return -1;
963 }
964
965 /* Handle the common parking setup stuff */
966 if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
967 return 0;
968 }
969
970 /* Initialize bridge features for the channel. */
971 res = ast_bridge_features_init(&chan_features);
972 if (res) {
973 ast_bridge_features_cleanup(&chan_features);
974 return -1;
975 }
976
977 /* subscribe to the parking message so that we can announce once it is parked */
978 pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
979 if (!pa_data) {
980 return -1;
981 }
982
984 /* Failed to create subscription */
986 return -1;
987 }
988
992
993 /* Now for the fun part... park it! */
994 ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
995
996 /* Toss the subscription since we aren't bridged at this point. */
998
999 /*
1000 * If the bridge was broken for a hangup that isn't real, then
1001 * don't run the h extension, because the channel isn't really
1002 * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
1003 */
1004 res = -1;
1005
1006 ast_channel_lock(chan);
1008 res = 0;
1009 }
1010 ast_channel_unlock(chan);
1011
1012 ast_bridge_features_cleanup(&chan_features);
1013
1014 return res;
1015}
1016
1018{
1020 return -1;
1021 }
1022
1024 return -1;
1025 }
1026
1028 return -1;
1029 }
1030
1031 return 0;
1032}
1033
1035{
1039}
ast_mutex_t lock
Definition: app_sla.c:337
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
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:1690
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:1009
@ AST_BRIDGE_JOIN_PASS_REFERENCE
Definition: bridge.h:539
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:2529
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:3689
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3722
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:476
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:2414
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2423
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2570
struct varshead * ast_channel_varshead(struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2970
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:5993
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:6861
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:3017
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1481
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2834
#define ast_channel_unlock(chan)
Definition: channel.h:2971
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:2428
@ AST_SOFTHANGUP_ASYNCGOTO
Definition: channel.h:1146
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:1301
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:1886
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1848
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:3066
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:623
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:640
@ 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:1120
const char * find_channel_parking_lot_name(struct ast_channel *chan)
Find parking lot name from channel.
Definition: res_parking.c:668
struct parking_lot * parking_lot_find_by_name(const char *lot_name)
Find a parking lot based on its name.
Definition: res_parking.c:662
#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:8279
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:1050
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:1104
#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:1201
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:998
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:353
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:1132
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