Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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 {
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);
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 
306  if (disable_announce) {
307  *disable_announce = 1;
308  }
309  }
310 
312  *use_ringing = 1;
313  }
314 
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 
403  ast_verb(4, "Setting Parker dial string to %s from %s value\n",
405  attended_transfer ? "ATTENDEDTRANSFER" : "BLINDTRANSFER");
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) {
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);
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 static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
745 {
746  struct ast_channel *chan = ast_channel_get_by_name(channel_id);
747  struct ast_var_t *current;
748  struct ast_variable *newvar;
749  const char *varname;
750  int vartype;
751 
752 
753  if (!chan) {
754  /* Already gone */
755  return;
756  }
757 
758  ast_channel_lock(chan);
759 
760  AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
761  varname = ast_var_full_name(current);
762  if (!varname) {
763  continue;
764  }
765 
766  vartype = 0;
767  if (varname[0] == '_') {
768  vartype = 1;
769  if (varname[1] == '_') {
770  vartype = 2;
771  }
772  }
773 
774  switch (vartype) {
775  case 1:
776  newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
777  break;
778  case 2:
779  newvar = ast_variable_new(varname, ast_var_value(current), "");
780  break;
781  default:
782  continue;
783  }
784  if (newvar) {
785  ast_debug(1, "Inheriting variable %s from %s.\n",
786  newvar->name, ast_channel_name(chan));
787  if (oh->vars) {
788  newvar->next = oh->vars;
789  oh->vars = newvar;
790  }
791  }
792  }
793 
794  ast_channel_unlock(chan);
795  ast_channel_cleanup(chan);
796 }
797 
798 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
799 {
800  struct ast_channel *dchan;
801  struct outgoing_helper oh = { 0, };
802  int outstate;
804  char buf[13];
805  char *dial_tech;
806  char *cur_announce;
807 
808  dial_tech = strsep(&dial_string, "/");
809  ast_verb(3, "Dial Tech,String: (%s,%s)\n", dial_tech, dial_string);
810 
811  if (!cap_slin) {
812  ast_log(LOG_WARNING, "PARK: Failed to announce park.\n");
813  goto announce_cleanup;
814  }
816 
817  snprintf(buf, sizeof(buf), "%d", parkingspace);
818  oh.vars = ast_variable_new("_PARKEDAT", buf, "");
819 
820  inherit_channel_vars_from_id(&oh, parkee_snapshot->base->uniqueid);
821 
822  dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
823  &outstate,
824  parkee_snapshot->caller->number,
825  parkee_snapshot->caller->name,
826  &oh);
827 
829  if (!dchan) {
830  ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
831  goto announce_cleanup;
832  }
833 
834  ast_verb(4, "Announce Template: %s\n", announce_string);
835 
836  for (cur_announce = strsep(&announce_string, ":"); cur_announce; cur_announce = strsep(&announce_string, ":")) {
837  ast_verb(4, "Announce:%s\n", cur_announce);
838  if (!strcmp(cur_announce, "PARKED")) {
839  ast_say_digits(dchan, parkingspace, "", ast_channel_language(dchan));
840  } else {
841  int dres = ast_streamfile(dchan, cur_announce, ast_channel_language(dchan));
842  if (!dres) {
843  dres = ast_waitstream(dchan, "");
844  } else {
845  ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", cur_announce, ast_channel_name(dchan));
846  }
847  }
848  }
849 
850  ast_stopstream(dchan);
851  ast_hangup(dchan);
852 
853 announce_cleanup:
854  ao2_cleanup(cap_slin);
855 }
856 
857 static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
858 {
859  struct park_announce_subscription_data *pa_data = data;
860  char *dial_string = pa_data->dial_string;
861 
863 
866  return;
867  }
868 
870  return;
871  }
872 
873  if (payload->event_type != PARKED_CALL) {
874  /* We are only concerned with calls parked */
875  return;
876  }
877 
878  if (strcmp(payload->parkee->base->uniqueid, pa_data->parkee_uuid)) {
879  /* We are only concerned with the parkee we are subscribed for. */
880  return;
881  }
882 
883  if (!ast_strlen_zero(dial_string)) {
884  announce_to_dial(dial_string, pa_data->announce_string, payload->parkingspace, payload->parkee);
885  }
886 
887  *dial_string = '\0'; /* If we observe this dial string on a second pass, we don't want to do anything with it. */
888 }
889 
890 static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
891 {
892  struct ast_bridge_features chan_features;
893  char *parse;
894  int res;
895  int silence_announcements = 1;
896 
898  struct park_announce_subscription_data *pa_data;
899 
900  RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
901 
903  AST_APP_ARG(lot_name);
905  AST_APP_ARG(announce_template);
906  AST_APP_ARG(dial);
907  AST_APP_ARG(others);/* Any remaining unused arguments */
908  );
909 
910  if (ast_strlen_zero(data)) {
911  ast_log(LOG_ERROR, "ParkAndAnnounce has required arguments. No arguments were provided.\n");
912  return -1;
913  }
914 
915  parse = ast_strdupa(data);
917 
918  if (ast_strlen_zero(args.announce_template)) {
919  /* improperly configured arguments for the application */
920  ast_log(LOG_ERROR, "ParkAndAnnounce requires the announce_template argument.\n");
921  return -1;
922  }
923 
924  if (ast_strlen_zero(args.dial)) {
925  /* improperly configured arguments */
926  ast_log(LOG_ERROR, "ParkAndAnnounce requires the dial argument.\n");
927  return -1;
928  }
929 
930  if (!strchr(args.dial, '/')) {
931  ast_log(LOG_ERROR, "ParkAndAnnounce dial string '%s' is improperly formed.\n", args.dial);
932  return -1;
933  }
934 
935  /* Handle the common parking setup stuff */
936  if (!(parking_bridge = park_application_setup(chan, NULL, data, &silence_announcements))) {
937  return 0;
938  }
939 
940  /* Initialize bridge features for the channel. */
941  res = ast_bridge_features_init(&chan_features);
942  if (res) {
943  ast_bridge_features_cleanup(&chan_features);
944  return -1;
945  }
946 
947  /* subscribe to the parking message so that we can announce once it is parked */
948  pa_data = park_announce_subscription_data_create(ast_channel_uniqueid(chan), args.dial, args.announce_template);
949  if (!pa_data) {
950  return -1;
951  }
952 
954  /* Failed to create subscription */
956  return -1;
957  }
958 
962 
963  /* Now for the fun part... park it! */
964  ast_bridge_join(parking_bridge, chan, NULL, &chan_features, NULL, 0);
965 
966  /* Toss the subscription since we aren't bridged at this point. */
968 
969  /*
970  * If the bridge was broken for a hangup that isn't real, then
971  * don't run the h extension, because the channel isn't really
972  * hung up. This should only happen with AST_SOFTHANGUP_ASYNCGOTO.
973  */
974  res = -1;
975 
976  ast_channel_lock(chan);
978  res = 0;
979  }
980  ast_channel_unlock(chan);
981 
982  ast_bridge_features_cleanup(&chan_features);
983 
984  return res;
985 }
986 
988 {
990  return -1;
991  }
992 
994  return -1;
995  }
996 
998  return -1;
999  }
1000 
1001  return 0;
1002 }
1003 
1005 {
1009 }
ast_mutex_t lock
Definition: app_meetme.c:1093
#define var
Definition: ast_expr2f.c:614
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
int ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, struct ast_bridge_tech_optimizations *tech_args, enum ast_bridge_join_flags flags)
Join a channel to a bridge (blocking)
Definition: bridge.c:1621
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
@ AST_BRIDGE_JOIN_PASS_REFERENCE
Definition: bridge.h:535
int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery)
Move a channel from one bridge to another.
Definition: bridge.c:2460
Basic bridge subclass API.
struct ast_bridge * ast_bridge_basic_new(void)
Create a new basic class bridge.
int ast_bridge_features_init(struct ast_bridge_features *features)
Initialize bridge features structure.
Definition: bridge.c:3620
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
Clean up the contents of a bridge features structure.
Definition: bridge.c:3653
static struct stasis_forward * parking_subscription
Our subscription for parking.
Definition: cdr.c:396
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1844
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2384
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2393
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2542
const char * ast_channel_uniqueid(const struct ast_channel *chan)
@ AST_SOFTHANGUP_ASYNCGOTO
Definition: channel.h:1126
#define ast_channel_lock(chan)
Definition: channel.h:2922
struct ast_channel * __ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int timeout, int *reason, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
Request a channel of a given type, with data as optional information used by the low level module and...
Definition: channel.c:6054
const char * ast_channel_name(const struct ast_channel *chan)
const char * ast_channel_language(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:6917
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:2398
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1448
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
struct varshead * ast_channel_varshead(struct ast_channel *chan)
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2806
#define ast_channel_unlock(chan)
Definition: channel.h:2923
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:1291
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:1855
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1817
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_message_type * ast_parked_call_type(void)
accessor for the parked call stasis message type
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.
struct stasis_topic * ast_parking_topic(void)
accessor for the parking stasis topic
Definition: parking.c:67
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
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:3126
char * strsep(char **str, const char *delims)
Configuration File Parser.
#define ast_variable_new(name, value, filename)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
size_t current
Definition: main/cli.c:113
Asterisk module definitions.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
@ PARKED_CALL
Definition: parking.h:47
#define PARK_APPLICATION
The default parking application that Asterisk expects.
Definition: parking.h:35
static int setup_park_common_datastore(struct ast_channel *parkee, const char *parker_uuid, const char *comeback_override, int randomize, int time_limit, int silence_announce)
void park_common_datastore_free(struct park_common_datastore *datastore)
Free a park common datastore struct.
static void park_announce_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
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.
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.
static void park_announce_subscription_data_destroy(void *data)
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 park_common_datastore_destroy(void *data)
static int park_app_exec(struct ast_channel *chan, const char *data)
@ OPT_ARG_TIMEOUT
@ OPT_ARG_COMEBACK
@ OPT_ARG_ARRAY_SIZE
static int park_and_announce_app_exec(struct ast_channel *chan, const char *data)
static void wipe_park_common_datastore(struct ast_channel *chan)
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)
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,...
@ MUXFLAG_RINGING
@ MUXFLAG_COMEBACK_OVERRIDE
@ MUXFLAG_TIMEOUT_OVERRIDE
@ MUXFLAG_RANDOMIZE
@ MUXFLAG_NOANNOUNCE
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.
#define PARK_AND_ANNOUNCE_APPLICATION
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)
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 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.
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.
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.
struct parking_lot * parking_create_dynamic_lot(const char *name, struct ast_channel *chan)
Create a dynamic parking lot.
Definition: res_parking.c:1060
struct parking_lot * parking_lot_find_by_name(const char *lot_name)
Find a parking lot based on its name.
Definition: res_parking.c:602
#define PARKED_CALL_APPLICATION
Definition: res_parking.h:38
const char * find_channel_parking_lot_name(struct ast_channel *chan)
Find parking lot name from channel.
Definition: res_parking.c:608
#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:8336
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
@ 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:1025
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
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:680
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
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Structure that contains features information.
Structure that contains information about a bridge.
Definition: bridge.h:349
const ast_string_field uniqueid
const ast_string_field number
const ast_string_field name
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_base * base
struct ast_channel_snapshot_caller * caller
Main Channel structure associated with a channel.
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
A parked call message payload.
Definition: parking.h:59
unsigned int parkingspace
Definition: parking.h:65
struct ast_channel_snapshot * parkee
Definition: parking.h:60
enum ast_parked_call_event_type event_type
Definition: parking.h:62
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
struct ast_variable * vars
Definition: channel.h:1112
const char * args
static struct test_options options
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:936
#define ast_assert(a)
Definition: utils.h:734