Asterisk - The Open Source Telephony Project  21.4.1
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"
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 {
242  OPT_ARG_COMEBACK,
243  OPT_ARG_TIMEOUT,
244  OPT_ARG_MUSICONHOLD,
245  OPT_ARG_ARRAY_SIZE /* Always the last element of the enum */
246 };
247 
248 enum park_flags {
249  MUXFLAG_RINGING = (1 << 0),
250  MUXFLAG_RANDOMIZE = (1 << 1),
251  MUXFLAG_NOANNOUNCE = (1 << 2),
252  MUXFLAG_COMEBACK_OVERRIDE = (1 << 3),
253  MUXFLAG_TIMEOUT_OVERRIDE = (1 << 4),
254  MUXFLAG_MUSICONHOLD = (1 << 5),
255 };
256 
257 AST_APP_OPTIONS(park_opts, {
258  AST_APP_OPTION('r', MUXFLAG_RINGING),
259  AST_APP_OPTION('R', MUXFLAG_RANDOMIZE),
260  AST_APP_OPTION('s', MUXFLAG_NOANNOUNCE),
261  AST_APP_OPTION_ARG('c', MUXFLAG_COMEBACK_OVERRIDE, OPT_ARG_COMEBACK),
262  AST_APP_OPTION_ARG('t', MUXFLAG_TIMEOUT_OVERRIDE, OPT_ARG_TIMEOUT),
263  AST_APP_OPTION_ARG('m', MUXFLAG_MUSICONHOLD, OPT_ARG_MUSICONHOLD),
264 });
265 
266 static 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 
281 static 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);
289  AST_APP_ARG(options);
290  AST_APP_ARG(other); /* Any remaining unused arguments */
291  );
292 
293  parse = ast_strdupa(data);
294  AST_STANDARD_APP_ARGS(args, parse);
295 
296  if (args.options) {
297  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
298  ast_app_parse_options(park_opts, &flags, opts, args.options);
299  if (ast_test_flag(&flags, MUXFLAG_TIMEOUT_OVERRIDE)) {
300  if (apply_option_timeout(time_limit, opts[OPT_ARG_TIMEOUT])) {
301  return -1;
302  }
303  }
304 
305  if (ast_test_flag(&flags, MUXFLAG_COMEBACK_OVERRIDE)) {
306  *comeback_override = ast_strdup(opts[OPT_ARG_COMEBACK]);
307  }
308 
309  if (ast_test_flag(&flags, MUXFLAG_NOANNOUNCE)) {
310  if (disable_announce) {
311  *disable_announce = 1;
312  }
313  }
314 
315  if (ast_test_flag(&flags, MUXFLAG_MUSICONHOLD)) {
316  *musicclass = ast_strdup(opts[OPT_ARG_MUSICONHOLD]);
317  }
318 
319  if (ast_test_flag(&flags, MUXFLAG_RINGING)) {
320  *use_ringing = 1;
321  }
322 
323  if (ast_test_flag(&flags, MUXFLAG_RANDOMIZE)) {
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 
347 static void park_common_datastore_destroy(void *data)
348 {
349  struct park_common_datastore *datastore = data;
350  park_common_datastore_free(datastore);
351 }
352 
353 static const struct ast_datastore_info park_common_info = {
354  .type = "park entry data",
355  .destroy = park_common_datastore_destroy,
356 };
357 
358 static void wipe_park_common_datastore(struct ast_channel *chan)
359 {
360  struct ast_datastore *datastore;
361 
362  ast_channel_lock(chan);
363  datastore = ast_channel_datastore_find(chan, &park_common_info, NULL);
364  if (datastore) {
365  ast_channel_datastore_remove(chan, datastore);
366  ast_datastore_free(datastore);
367  }
368  ast_channel_unlock(chan);
369 }
370 
371 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)
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 
379  wipe_park_common_datastore(parkee);
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 
409  if (!ast_strlen_zero(parker_dial_string)) {
410  ast_channel_name_to_dial_string(parker_dial_string);
411  ast_verb(4, "Setting Parker dial string to %s from %s value\n",
412  parker_dial_string,
413  attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
414  park_datastore->parker_dial_string = ast_strdup(parker_dial_string);
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) {
426  park_datastore->comeback_override = ast_strdup(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) {
465  park_common_datastore_free(data_copy);
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) {
474  data_copy->comeback_override = ast_strdup(data->comeback_override);
475  if (!data_copy->comeback_override) {
476  park_common_datastore_free(data_copy);
477  return NULL;
478  }
479  }
480 
481  if (data->parker_dial_string) {
483  if (!data_copy->parker_dial_string) {
484  park_common_datastore_free(data_copy);
485  return NULL;
486  }
487  }
488 
489  return data_copy;
490 }
491 
492 static 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 
538 struct 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 
545 struct 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 
565 static 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);
612  if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
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 
624 static 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);
642  AST_STANDARD_APP_ARGS(args, parse);
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);
654  lot_name = ast_strdupa(find_channel_parking_lot_name(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 */
705  parked_call_retrieve_enable_features(chan, lot, AST_FEATURE_FLAG_BYCALLER);
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 
725  char *parkee_uuid;
726  char *dial_string;
727  char *announce_string;
728 };
729 
730 static void park_announce_subscription_data_destroy(void *data)
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 
739 static struct park_announce_subscription_data *park_announce_subscription_data_create(const char *parkee_uuid,
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))) {
752  park_announce_subscription_data_destroy(pa_data);
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  */
765 static 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);
816  ast_channel_cleanup(chan);
817 }
818 
819 static 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 
849  ast_variables_destroy(oh.vars);
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 
874 announce_cleanup:
875  ao2_cleanup(cap_slin);
876 }
877 
878 static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
879 {
880  struct park_announce_subscription_data *pa_data = data;
881  char *dial_string = pa_data->dial_string;
882 
883  struct ast_parked_call_payload *payload = stasis_message_data(message);
884 
885  if (stasis_subscription_final_message(sub, message)) {
886  park_announce_subscription_data_destroy(data);
887  return;
888  }
889 
890  if (ast_parked_call_type() != stasis_message_type(message)) {
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 
911 static 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);
925  AST_APP_ARG(options);
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);
937  AST_STANDARD_APP_ARGS(args, parse);
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 
974  if (!(parking_subscription = stasis_subscribe_pool(ast_parking_topic(), park_announce_update_cb, pa_data))) {
975  /* Failed to create subscription */
976  park_announce_subscription_data_destroy(pa_data);
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. */
988  stasis_unsubscribe(parking_subscription);
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);
998  if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
999  res = 0;
1000  }
1001  ast_channel_unlock(chan);
1002 
1003  ast_bridge_features_cleanup(&chan_features);
1004 
1005  return res;
1006 }
1007 
1009 {
1010  if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
1011  return -1;
1012  }
1013 
1014  if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
1015  return -1;
1016  }
1017 
1018  if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
1019  return -1;
1020  }
1021 
1022  return 0;
1023 }
1024 
1026 {
1028  ast_unregister_application(PARKED_CALL_APPLICATION);
1029  ast_unregister_application(PARK_AND_ANNOUNCE_APPLICATION);
1030 }
const char * type
Definition: datastore.h:32
struct ast_variable * next
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:3653
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
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:1060
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:1262
const ast_string_field name
Structure that contains features information.
void unload_parking_applications(void)
Unregister parking applications.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
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:3620
Structure for variables, used for configurations and for channel variables.
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:1077
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:8253
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
const ast_string_field uniqueid
A parked call message payload.
Definition: parking.h:59
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
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
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
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
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.
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.
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
Configuration File Parser.
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.
ast_mutex_t lock
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:619
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 ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
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
struct ast_channel_snapshot_caller * caller
int load_parking_applications(void)
Register parking applications.
Configuration option-handling.
Structure that contains information about a bridge.
Definition: bridge.h:349
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:6839
#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
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
#define PARK_APPLICATION
The default parking application that Asterisk expects.
Definition: parking.h:35
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
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
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
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:1878
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
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:5976
Basic bridge subclass API.
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:1621
void * data
Definition: datastore.h:66
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
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
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.
const ast_string_field number
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
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
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
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
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:608
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's bridge. If it doesn'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.
#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
static struct stasis_forward * parking_subscription
Our subscription for parking.
Definition: cdr.c:422
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application'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:2394
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
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.