Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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"
37 #include "asterisk/bridge_basic.h"
38 #include "asterisk/format_cache.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 
241 enum park_args {
244  OPT_ARG_ARRAY_SIZE /* Always the last element of the enum */
245 };
246 
248  MUXFLAG_RINGING = (1 << 0),
249  MUXFLAG_RANDOMIZE = (1 << 1),
250  MUXFLAG_NOANNOUNCE = (1 << 2),
253 };
254 
261 });
262 
263 static int apply_option_timeout (int *var, char *timeout_arg)
264 {
265  if (ast_strlen_zero(timeout_arg)) {
266  ast_log(LOG_ERROR, "No duration value provided for the timeout ('t') option.\n");
267  return -1;
268  }
269 
270  if (sscanf(timeout_arg, "%d", var) != 1 || *var < 0) {
271  ast_log(LOG_ERROR, "Duration value provided for timeout ('t') option must be 0 or greater.\n");
272  return -1;
273  }
274 
275  return 0;
276 }
277 
278 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)
279 {
280  char *parse;
281  struct ast_flags flags = { 0 };
282 
284  AST_APP_ARG(lot_name);
286  AST_APP_ARG(other); /* Any remaining unused arguments */
287  );
288 
289  parse = ast_strdupa(data);
290  AST_STANDARD_APP_ARGS(args, parse);
291 
292  if (args.options) {
293  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
294  ast_app_parse_options(park_opts, &flags, opts, args.options);
296  if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
297  return -1;
298  }
299  }
300 
302  *comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
303  }
304 
305  if (ast_test_flag(&flags, MUXFLAG_NOANNOUNCE)) {
306  if (disable_announce) {
307  *disable_announce = 1;
308  }
309  }
310 
311  if (ast_test_flag(&flags, MUXFLAG_RINGING)) {
312  *use_ringing = 1;
313  }
314 
315  if (ast_test_flag(&flags, MUXFLAG_RANDOMIZE)) {
316  *randomize = 1;
317  }
318  }
319 
320  if (!ast_strlen_zero(args.lot_name)) {
321  *lot_name = ast_strdup(args.lot_name);
322  }
323 
324  return 0;
325 }
326 
328 {
329  if (!datastore) {
330  return;
331  }
332 
333  ast_free(datastore->parker_uuid);
334  ast_free(datastore->parker_dial_string);
335  ast_free(datastore->comeback_override);
336  ast_free(datastore);
337 }
338 
339 static void park_common_datastore_destroy(void *data)
340 {
341  struct park_common_datastore *datastore = data;
342  park_common_datastore_free(datastore);
343 }
344 
345 static const struct ast_datastore_info park_common_info = {
346  .type = "park entry data",
348 };
349 
350 static void wipe_park_common_datastore(struct ast_channel *chan)
351 {
352  struct ast_datastore *datastore;
353 
354  ast_channel_lock(chan);
355  datastore = ast_channel_datastore_find(chan, &park_common_info, NULL);
356  if (datastore) {
357  ast_channel_datastore_remove(chan, datastore);
358  ast_datastore_free(datastore);
359  }
360  ast_channel_unlock(chan);
361 }
362 
363 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)
364 {
365  struct ast_datastore *datastore = NULL;
366  struct park_common_datastore *park_datastore;
367  const char *attended_transfer;
368  const char *blind_transfer;
369  char *parker_dial_string = NULL;
370 
372 
373  if (!(datastore = ast_datastore_alloc(&park_common_info, NULL))) {
374  return -1;
375  }
376 
377  if (!(park_datastore = ast_calloc(1, sizeof(*park_datastore)))) {
378  ast_datastore_free(datastore);
379  return -1;
380  }
381  datastore->data = park_datastore;
382 
383  park_datastore->parker_uuid = ast_strdup(parker_uuid);
384  if (!park_datastore->parker_uuid) {
385  ast_datastore_free(datastore);
386  return -1;
387  }
388 
389  ast_channel_lock(parkee);
390  attended_transfer = pbx_builtin_getvar_helper(parkee, "ATTENDEDTRANSFER");
391  blind_transfer = pbx_builtin_getvar_helper(parkee, "BLINDTRANSFER");
392  if (!ast_strlen_zero(attended_transfer)) {
393  parker_dial_string = ast_strdupa(attended_transfer);
394  } else if (!ast_strlen_zero(blind_transfer)) {
395  parker_dial_string = ast_strdupa(blind_transfer);
396  /* Ensure that attended_transfer is NULL and not an empty string. */
397  attended_transfer = NULL;
398  }
399  ast_channel_unlock(parkee);
400 
401  if (!ast_strlen_zero(parker_dial_string)) {
402  ast_channel_name_to_dial_string(parker_dial_string);
403  ast_verb(4, "Setting Parker dial string to %s from %s value\n",
404  parker_dial_string,
405  attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
406  park_datastore->parker_dial_string = ast_strdup(parker_dial_string);
407  if (!park_datastore->parker_dial_string) {
408  ast_datastore_free(datastore);
409  return -1;
410  }
411  }
412 
413  park_datastore->randomize = randomize;
414  park_datastore->time_limit = time_limit;
415  park_datastore->silence_announce = silence_announce;
416 
417  if (comeback_override) {
418  park_datastore->comeback_override = ast_strdup(comeback_override);
419  if (!park_datastore->comeback_override) {
420  ast_datastore_free(datastore);
421  return -1;
422  }
423  }
424 
425 
426  ast_channel_lock(parkee);
427  ast_channel_datastore_add(parkee, datastore);
428  ast_channel_unlock(parkee);
429 
430  return 0;
431 }
432 
434 {
435  struct ast_datastore *datastore;
436  struct park_common_datastore *data;
437  struct park_common_datastore *data_copy;
438 
439  SCOPED_CHANNELLOCK(lock, parkee);
440 
441  if (!(datastore = ast_channel_datastore_find(parkee, &park_common_info, NULL))) {
442  return NULL;
443  }
444 
445  data = datastore->data;
446 
447  /* This data should always be populated if this datastore was appended to the channel */
448  ast_assert(data != NULL);
449 
450  data_copy = ast_calloc(1, sizeof(*data_copy));
451  if (!data_copy) {
452  return NULL;
453  }
454 
455  data_copy->parker_uuid = ast_strdup(data->parker_uuid);
456  if (!data_copy->parker_uuid) {
457  park_common_datastore_free(data_copy);
458  return NULL;
459  }
460 
461  data_copy->randomize = data->randomize;
462  data_copy->time_limit = data->time_limit;
463  data_copy->silence_announce = data->silence_announce;
464 
465  if (data->comeback_override) {
466  data_copy->comeback_override = ast_strdup(data->comeback_override);
467  if (!data_copy->comeback_override) {
468  park_common_datastore_free(data_copy);
469  return NULL;
470  }
471  }
472 
473  if (data->parker_dial_string) {
475  if (!data_copy->parker_dial_string) {
476  park_common_datastore_free(data_copy);
477  return NULL;
478  }
479  }
480 
481  return data_copy;
482 }
483 
484 struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_channel *parker,
485  const char *lot_name, const char *comeback_override,
486  int use_ringing, int randomize, int time_limit, int silence_announcements)
487 {
488  struct ast_bridge *parking_bridge;
489  RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
490 
491  if (!parker) {
492  parker = parkee;
493  }
494 
495  /* If the name of the parking lot isn't specified in the arguments, find it based on the channel. */
496  if (ast_strlen_zero(lot_name)) {
497  ast_channel_lock(parker);
498  lot_name = ast_strdupa(find_channel_parking_lot_name(parker));
499  ast_channel_unlock(parker);
500  }
501 
502  lot = parking_lot_find_by_name(lot_name);
503  if (!lot) {
504  lot = parking_create_dynamic_lot(lot_name, parker);
505  }
506  if (!lot) {
507  ast_log(LOG_ERROR, "Could not find parking lot: '%s'\n", lot_name);
508  return NULL;
509  }
510 
511  ao2_lock(lot);
512  parking_bridge = parking_lot_get_bridge(lot);
513  ao2_unlock(lot);
514 
515  if (!parking_bridge) {
516  return NULL;
517  }
518 
519  /* Apply relevant bridge roles and such to the parking channel */
520  parking_channel_set_roles(parkee, lot, use_ringing);
521  setup_park_common_datastore(parkee, ast_channel_uniqueid(parker), comeback_override, randomize, time_limit,
522  silence_announcements);
523  return parking_bridge;
524 }
525 
526 struct ast_bridge *park_application_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *app_data,
527  int *silence_announcements)
528 {
529  int use_ringing = 0;
530  int randomize = 0;
531  int time_limit = -1;
532 
533  RAII_VAR(char *, comeback_override, NULL, ast_free);
534  RAII_VAR(char *, lot_name_app_arg, NULL, ast_free);
535 
536  if (app_data) {
537  park_app_parse_data(app_data, silence_announcements, &use_ringing, &randomize, &time_limit, &comeback_override, &lot_name_app_arg);
538  }
539 
540  return park_common_setup(parkee, parker, lot_name_app_arg, comeback_override, use_ringing,
541  randomize, time_limit, silence_announcements ? *silence_announcements : 0);
542 
543 }
544 
545 static int park_app_exec(struct ast_channel *chan, const char *data)
546 {
547  RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
548 
549  struct ast_bridge_features chan_features;
550  int res;
551  int silence_announcements = 0;
552  int blind_transfer;
553 
554  /* Answer the channel if needed */
555  if (ast_channel_state(chan) != AST_STATE_UP) {
556  ast_answer(chan);
557  }
558 
559  ast_channel_lock(chan);
560  blind_transfer = !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"));
561  ast_channel_unlock(chan);
562 
563  /* Handle the common parking setup stuff */
564  if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
565  if (!silence_announcements && !blind_transfer) {
566  ast_stream_and_wait(chan, "pbx-parkingfailed", "");
567  }
569  return 0;
570  }
571 
572  /* Initialize bridge features for the channel. */
573  res = ast_bridge_features_init(&chan_features);
574  if (res) {
575  ast_bridge_features_cleanup(&chan_features);
577  return -1;
578  }
579 
580  /* Now for the fun part... park it! */
581  ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
582 
583  /*
584  * If the bridge was broken for a hangup that isn't real, then
585  * don't run the h extension, because the channel isn't really
586  * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
587  */
588  res = -1;
589 
590  ast_channel_lock(chan);
592  res = 0;
593  }
594  ast_channel_unlock(chan);
595 
596  ast_bridge_features_cleanup(&chan_features);
597 
598  return res;
599 }
600 
601 /* Retrieve a parked call */
602 
603 static int parked_call_app_exec(struct ast_channel *chan, const char *data)
604 {
605  RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
606  RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup); /* Parked user being retrieved */
607  struct ast_bridge *retrieval_bridge;
608  int res;
609  int target_space = -1;
610  struct ast_bridge_features chan_features;
611  char *parse;
612  const char *lot_name;
613 
615  AST_APP_ARG(lot_name);
616  AST_APP_ARG(parking_space);
617  AST_APP_ARG(other); /* Any remaining unused arguments */
618  );
619 
620  parse = ast_strdupa(data);
621  AST_STANDARD_APP_ARGS(args, parse);
622 
623  /* Answer the channel if needed */
624  if (ast_channel_state(chan) != AST_STATE_UP) {
625  ast_answer(chan);
626  }
627 
628  lot_name = args.lot_name;
629 
630  /* If the name of the parking lot isn't in the arguments, find it based on the channel. */
631  if (ast_strlen_zero(lot_name)) {
632  ast_channel_lock(chan);
633  lot_name = ast_strdupa(find_channel_parking_lot_name(chan));
634  ast_channel_unlock(chan);
635  }
636 
637  lot = parking_lot_find_by_name(lot_name);
638  if (!lot) {
639  ast_log(LOG_ERROR, "Could not find the requested parking lot\n");
640  ast_stream_and_wait(chan, "pbx-invalidpark", "");
641  return -1;
642  }
643 
644  if (!ast_strlen_zero(args.parking_space)) {
645  if (sscanf(args.parking_space, "%d", &target_space) != 1 || target_space < 0) {
646  ast_stream_and_wait(chan, "pbx-invalidpark", "");
647  ast_log(LOG_ERROR, "value '%s' for parking_space argument is invalid. Must be an integer greater than 0.\n", args.parking_space);
648  return -1;
649  }
650  }
651 
652  /* Attempt to get the parked user from the parking lot */
653  pu = parking_lot_retrieve_parked_user(lot, target_space);
654  if (!pu) {
655  ast_stream_and_wait(chan, "pbx-invalidpark", "");
656  return -1;
657  }
658 
659  /* The parked call needs to know who is retrieving it before we move it out of the parking bridge */
660  ast_assert(pu->retriever == NULL);
661  pu->retriever = ast_channel_snapshot_create(chan);
662 
663  /* Create bridge */
664  retrieval_bridge = ast_bridge_basic_new();
665  if (!retrieval_bridge) {
666  return -1;
667  }
668 
669  /* Move the parkee into the new bridge */
670  if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) {
671  ast_bridge_destroy(retrieval_bridge, 0);
672  return -1;
673  }
674 
675  /* Initialize our bridge features */
676  res = ast_bridge_features_init(&chan_features);
677  if (res) {
678  ast_bridge_destroy(retrieval_bridge, 0);
679  ast_bridge_features_cleanup(&chan_features);
680  return -1;
681  }
682 
683  /* Set the features */
685 
686  /* If the parkedplay option is set for the caller to hear, play that tone now. */
687  if (lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLER) {
688  ast_stream_and_wait(chan, lot->cfg->courtesytone, NULL);
689  }
690 
691  /* Now we should try to join the new bridge ourselves... */
692  ast_bridge_join(retrieval_bridge, chan, NULL, &chan_features, NULL,
694 
695  ast_bridge_features_cleanup(&chan_features);
696 
697  /* Return -1 so that call does not continue in the dialplan. This is to make
698  * behavior consistent with Asterisk versions prior to 12.
699  */
700  return -1;
701 }
702 
704  char *parkee_uuid;
705  char *dial_string;
707 };
708 
710 {
711  struct park_announce_subscription_data *pa_data = data;
712  ast_free(pa_data->parkee_uuid);
713  ast_free(pa_data->dial_string);
714  ast_free(pa_data->announce_string);
715  ast_free(pa_data);
716 }
717 
719  const char *dial_string,
720  const char *announce_string)
721 {
722  struct park_announce_subscription_data *pa_data;
723 
724  if (!(pa_data = ast_calloc(1, sizeof(*pa_data)))) {
725  return NULL;
726  }
727 
728  if (!(pa_data->parkee_uuid = ast_strdup(parkee_uuid))
729  || !(pa_data->dial_string = ast_strdup(dial_string))
730  || !(pa_data->announce_string = ast_strdup(announce_string))) {
732  return NULL;
733  }
734 
735  return pa_data;
736 }
737 
738 /*! \internal
739  * \brief Gathers inheritable channel variables from a channel by name.
740  *
741  * \param oh outgoing helper struct we are bestowing inheritable variables to
742  * \param channel_id name or uniqueID of the channel to inherit variables from
743  *
744  * \return Nothing
745  */
746 static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
747 {
748  struct ast_channel *chan = ast_channel_get_by_name(channel_id);
749  struct ast_var_t *current;
750  struct ast_variable *newvar;
751  const char *varname;
752  int vartype;
753 
754 
755  if (!chan) {
756  /* Already gone */
757  return;
758  }
759 
760  ast_channel_lock(chan);
761 
762  AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
763  varname = ast_var_full_name(current);
764  if (!varname) {
765  continue;
766  }
767 
768  vartype = 0;
769  if (varname[0] == '_') {
770  vartype = 1;
771  if (varname[1] == '_') {
772  vartype = 2;
773  }
774  }
775 
776  switch (vartype) {
777  case 1:
778  newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
779  break;
780  case 2:
781  newvar = ast_variable_new(varname, ast_var_value(current), "");
782  break;
783  default:
784  continue;
785  }
786  if (newvar) {
787  ast_debug(1, "Inheriting variable %s from %s.\n",
788  newvar->name, ast_channel_name(chan));
789  if (oh->vars) {
790  newvar->next = oh->vars;
791  oh->vars = newvar;
792  }
793  }
794  }
795 
796  ast_channel_unlock(chan);
797  ast_channel_cleanup(chan);
798 }
799 
800 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
801 {
802  struct ast_channel *dchan;
803  struct outgoing_helper oh = { 0, };
804  int outstate;
806  char buf[13];
807  char *dial_tech;
808  char *cur_announce;
809 
810  dial_tech = strsep(&dial_string, "/");
811  ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
812 
813  if (!cap_slin) {
814  ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
815  goto announce_cleanup;
816  }
818 
819  snprintf(buf, sizeof(buf), "%d", parkingspace);
820  oh.vars = ast_variable_new("_PARKEDAT", buf, "");
821 
822  inherit_channel_vars_from_id(&oh, parkee_snapshot->base->uniqueid);
823 
824  dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
825  &outstate,
826  parkee_snapshot->caller->number,
827  parkee_snapshot->caller->name,
828  &oh);
829 
831  if (!dchan) {
832  ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
833  goto announce_cleanup;
834  }
835 
836  ast_verb(4, "Announce Template: %s\n", announce_string);
837 
838  for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
839  ast_verb(4, "Announce:%s\n", cur_announce);
840  if (!strcmp(cur_announce, "PARKED")) {
841  ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
842  } else {
843  int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
844  if (!dres) {
845  dres = ast_waitstream(dchan, "");
846  } else {
847  ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
848  }
849  }
850  }
851 
852  ast_stopstream(dchan);
853  ast_hangup(dchan);
854 
855 announce_cleanup:
856  ao2_cleanup(cap_slin);
857 }
858 
859 static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
860 {
861  struct park_announce_subscription_data *pa_data = data;
862  char *dial_string = pa_data->dial_string;
863 
864  struct ast_parked_call_payload *payload = stasis_message_data(message);
865 
866  if (stasis_subscription_final_message(sub, message)) {
868  return;
869  }
870 
871  if (ast_parked_call_type() != stasis_message_type(message)) {
872  return;
873  }
874 
875  if (payload->event_type != PARKED_CALL) {
876  /* We are only concerned with calls parked */
877  return;
878  }
879 
880  if (strcmp(payload->parkee->base->uniqueid, pa_data->parkee_uuid)) {
881  /* We are only concerned with the parkee we are subscribed for. */
882  return;
883  }
884 
885  if (!ast_strlen_zero(dial_string)) {
886  announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
887  }
888 
889  *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
890 }
891 
892 static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
893 {
894  struct ast_bridge_features chan_features;
895  char *parse;
896  int res;
897  int silence_announcements = 1;
898 
900  struct park_announce_subscription_data *pa_data;
901 
902  RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
903 
905  AST_APP_ARG(lot_name);
907  AST_APP_ARG(announce_template);
908  AST_APP_ARG(dial);
909  AST_APP_ARG(others);/* Any remaining unused arguments */
910  );
911 
912  if (ast_strlen_zero(data)) {
913  ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
914  return -1;
915  }
916 
917  parse = ast_strdupa(data);
918  AST_STANDARD_APP_ARGS(args, parse);
919 
920  if (ast_strlen_zero(args.announce_template)) {
921  /* improperly configured arguments for the application */
922  ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
923  return -1;
924  }
925 
926  if (ast_strlen_zero(args.dial)) {
927  /* improperly configured arguments */
928  ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
929  return -1;
930  }
931 
932  if (!strchr(args.dial, '/')) {
933  ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
934  return -1;
935  }
936 
937  /* Handle the common parking setup stuff */
938  if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
939  return 0;
940  }
941 
942  /* Initialize bridge features for the channel. */
943  res = ast_bridge_features_init(&chan_features);
944  if (res) {
945  ast_bridge_features_cleanup(&chan_features);
946  return -1;
947  }
948 
949  /* subscribe to the parking message so that we can announce once it is parked */
950  pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
951  if (!pa_data) {
952  return -1;
953  }
954 
955  if (!(parking_subscription = stasis_subscribe_pool(ast_parking_topic(), park_announce_update_cb, pa_data))) {
956  /* Failed to create subscription */
958  return -1;
959  }
960 
964 
965  /* Now for the fun part... park it! */
966  ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
967 
968  /* Toss the subscription since we aren't bridged at this point. */
969  stasis_unsubscribe(parking_subscription);
970 
971  /*
972  * If the bridge was broken for a hangup that isn't real, then
973  * don't run the h extension, because the channel isn't really
974  * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
975  */
976  res = -1;
977 
978  ast_channel_lock(chan);
980  res = 0;
981  }
982  ast_channel_unlock(chan);
983 
984  ast_bridge_features_cleanup(&chan_features);
985 
986  return res;
987 }
988 
990 {
992  return -1;
993  }
994 
996  return -1;
997  }
998 
1000  return -1;
1001  }
1002 
1003  return 0;
1004 }
1005 
1007 {
1011 }
static int apply_option_timeout(int *var, char *timeout_arg)
const char * type
Definition: datastore.h:32
struct ast_variable * next
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3720
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)
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1250
struct ast_channel_snapshot_base * base
Asterisk main include file. File version handling, generic pbx functions.
struct parking_lot * parking_create_dynamic_lot(const char *name, struct ast_channel *chan)
Create a dynamic parking lot.
Definition: res_parking.c:1059
void park_common_datastore_free(struct park_common_datastore *datastore)
Free a park common datastore struct.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
const ast_string_field name
Structure that contains features information.
void unload_parking_applications(void)
Unregister parking applications.
#define ast_test_flag(p, flag)
Definition: utils.h:63
const char * ast_var_value(const struct ast_var_t *var)
Definition: chanvars.c:80
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
#define LOG_WARNING
Definition: logger.h:274
struct stasis_message_type * ast_parked_call_type(void)
accessor for the parked call stasis message type
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3687
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
Structure representing a snapshot of channel state.
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.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
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:1079
Structure for a data store type.
Definition: datastore.h:31
ast_channel_state
ast_channel states
Definition: channelstate.h:35
int ast_say_digits(struct ast_channel *chan, int num, const char *ints, const char *lang)
says digits
Definition: channel.c:8349
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:970
const ast_string_field uniqueid
#define ast_assert(a)
Definition: utils.h:695
#define ao2_unlock(a)
Definition: astobj2.h:730
A parked call message payload.
Definition: parking.h:59
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
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:2404
struct varshead * ast_channel_varshead(struct ast_channel *chan)
const char * args
#define NULL
Definition: resample.c:96
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
enum ast_parked_call_event_type event_type
Definition: parking.h:62
#define ast_verb(level,...)
Definition: logger.h:463
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_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...
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_variable * vars
Definition: channel.h:1113
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Utility functions.
#define PARK_AND_ANNOUNCE_APPLICATION
#define ast_strlen_zero(foo)
Definition: strings.h:52
void publish_parked_call_failure(struct ast_channel *parkee)
Publish a stasis parked call message for the channel indicating failure to park.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
Configuration File Parser.
static const struct ast_datastore_info park_common_info
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static struct park_announce_subscription_data * park_announce_subscription_data_create(const char *parkee_uuid, const char *dial_string, const char *announce_string)
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.
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
struct ast_channel_snapshot * parkee
Definition: parking.h:60
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...
#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:911
ast_mutex_t lock
Definition: app_meetme.c:1091
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2992
#define ao2_lock(a)
Definition: astobj2.h:718
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_format_cap_append(cap, format, framing)
Definition: format_cap.h:103
#define ast_variable_new(name, value, filename)
#define ast_format_cap_alloc(flags)
Definition: format_cap.h:52
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:2906
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct ast_channel_snapshot_caller * caller
static int parked_call_app_exec(struct ast_channel *chan, const char *data)
int load_parking_applications(void)
Register parking applications.
Configuration option-handling.
Structure that contains information about a bridge.
Definition: bridge.h:357
#define LOG_ERROR
Definition: logger.h:285
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
void ast_channel_name_to_dial_string(char *channel_name)
Removes the trailing identifiers from a channel name string.
Definition: channel.c:6934
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
unsigned int parkingspace
Definition: parking.h:65
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:682
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
#define PARK_APPLICATION
The default parking application that Asterisk expects.
Definition: parking.h:35
const char * ast_var_full_name(const struct ast_var_t *var)
Definition: chanvars.c:75
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
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:1176
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define PARKED_CALL_APPLICATION
Definition: res_parking.h:38
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:1814
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2548
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:6066
Basic bridge subclass API.
static void park_announce_subscription_data_destroy(void *data)
Structure used to handle boolean flags.
Definition: utils.h:199
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:1667
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)
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
void * data
Definition: datastore.h:70
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
struct stasis_topic * ast_parking_topic(void)
accessor for the parking stasis topic
Definition: parking.c:67
char * strsep(char **str, const char *delims)
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:2508
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.
static void park_common_datastore_destroy(void *data)
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const ast_string_field number
const char * ast_channel_name(const struct ast_channel *chan)
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1776
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
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:1025
struct stasis_forward * sub
Definition: res_corosync.c:240
const char * ast_channel_language(const struct ast_channel *chan)
static void wipe_park_common_datastore(struct ast_channel *chan)
static int park_app_exec(struct ast_channel *chan, const char *data)
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
struct parking_lot * parking_lot_find_by_name(const char *lot_name)
Find a parking lot based on its name.
Definition: res_parking.c:601
Call Parking Resource Internal API.
const char * find_channel_parking_lot_name(struct ast_channel *chan)
Find parking lot name from channel.
Definition: res_parking.c:607
static struct test_options options
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
struct ast_bridge * parking_lot_get_bridge(struct parking_lot *lot)
Get a reference to a parking lot&#39;s bridge. If it doesn&#39;t exist, create it and get a reference...
Say numbers and dates (maybe words one day too)
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
static const struct ast_app_option park_opts[128]
static struct stasis_forward * parking_subscription
Our subscription for parking.
Definition: cdr.c:380
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2399
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:187
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
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.
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.
static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)