2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Jonathan Rose <jrose@digium.com>
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.
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.
21 * \brief Parking Bridge Class
23 * \author Jonathan Rose <jrose@digium.com>
27 #include "res_parking.h"
28 #include "asterisk/astobj2.h"
29 #include "asterisk/logger.h"
30 #include "asterisk/say.h"
31 #include "asterisk/term.h"
32 #include "asterisk/features.h"
33 #include "asterisk/bridge_internal.h"
35 struct ast_bridge_parking
37 struct ast_bridge base;
39 /* private stuff for parking */
40 struct parking_lot *lot;
45 * \brief ast_bridge parking class destructor
48 * \param self Bridge to operate upon.
50 * \note XXX Stub... and it might go unused.
54 static void bridge_parking_destroy(struct ast_bridge_parking *self)
56 ast_bridge_base_v_table.destroy(&self->base);
59 static void bridge_parking_dissolving(struct ast_bridge_parking *self)
62 ast_bridge_base_v_table.dissolving(&self->base);
65 static void destroy_parked_user(void *obj)
67 struct parked_user *pu = obj;
70 ao2_cleanup(pu->retriever);
71 ast_free(pu->parker_dial_string);
74 /* Only call this on a parked user that hasn't had its parker_dial_string set already */
75 static int parked_user_set_parker_dial_string(struct parked_user *pu, struct ast_channel *parker)
77 char *dial_string = ast_strdupa(ast_channel_name(parker));
79 ast_channel_name_to_dial_string(dial_string);
80 pu->parker_dial_string = ast_strdup(dial_string);
82 if (!pu->parker_dial_string) {
92 * \brief Construct a parked_user struct assigned to the specified parking lot
94 * \param lot The parking lot we are assigning the user to
95 * \param parkee The channel being parked
96 * \param parker The channel performing the park operation (may be the same channel)
97 * \param parker_dial_string Takes priority over parker for setting the parker dial string if included
98 * \param use_random_space if true, prioritize using a random parking space instead
99 * of ${PARKINGEXTEN} and/or automatic assignment from the parking lot
100 * \param time_limit If using a custom timeout, this should be supplied so that the
101 * parked_user struct can provide this information for manager events. If <0,
102 * use the parking lot limit instead.
104 * \retval NULL on failure
105 * \retval reference to the parked user
107 * \note ao2_cleanup this reference when you are done using it or you'll cause leaks.
109 static struct parked_user *generate_parked_user(struct parking_lot *lot, struct ast_channel *chan, struct ast_channel *parker, const char *parker_dial_string, int use_random_space, int time_limit)
111 struct parked_user *new_parked_user;
112 int preferred_space = -1; /* Initialize to use parking lot defaults */
114 const char *parkingexten;
116 if (lot->mode == PARKINGLOT_DISABLED) {
117 ast_log(LOG_NOTICE, "Tried to park in a parking lot that is no longer able to be parked to.\n");
121 new_parked_user = ao2_alloc(sizeof(*new_parked_user), destroy_parked_user);
122 if (!new_parked_user) {
126 if (use_random_space) {
127 preferred_space = ast_random() % (lot->cfg->parking_stop - lot->cfg->parking_start + 1);
128 preferred_space += lot->cfg->parking_start;
130 ast_channel_lock(chan);
131 if ((parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"))) {
132 parkingexten = ast_strdupa(parkingexten);
134 ast_channel_unlock(chan);
136 if (!ast_strlen_zero(parkingexten)) {
137 if (sscanf(parkingexten, "%30d", &preferred_space) != 1 || preferred_space <= 0) {
138 ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n", parkingexten);
139 ao2_ref(new_parked_user, -1);
145 /* We need to keep the lot locked between parking_lot_get_space and actually placing it in the lot. Or until we decide not to. */
148 parking_space = parking_lot_get_space(lot, preferred_space);
149 if (parking_space == -1) {
150 ast_log(LOG_NOTICE, "Failed to get parking space in lot '%s'. All full.\n", lot->name);
151 ao2_ref(new_parked_user, -1);
156 lot->next_space = ((parking_space + 1) - lot->cfg->parking_start) % (lot->cfg->parking_stop - lot->cfg->parking_start + 1) + lot->cfg->parking_start;
157 new_parked_user->chan = chan;
158 new_parked_user->parking_space = parking_space;
160 /* Have the parked user take a reference to the parking lot. This reference should be immutable and released at destruction */
161 new_parked_user->lot = lot;
164 new_parked_user->start = ast_tvnow();
165 new_parked_user->time_limit = (time_limit >= 0) ? time_limit : lot->cfg->parkingtime;
167 if (parker_dial_string) {
168 new_parked_user->parker_dial_string = ast_strdup(parker_dial_string);
170 if (parked_user_set_parker_dial_string(new_parked_user, parker)) {
171 ao2_ref(new_parked_user, -1);
177 if (!new_parked_user->parker_dial_string) {
178 ao2_ref(new_parked_user, -1);
183 /* Insert into the parking lot's parked user list. We can unlock the lot now. */
184 ao2_link(lot->parked_users, new_parked_user);
187 return new_parked_user;
190 /* TODO CEL events for parking */
194 * \brief ast_bridge parking push method.
197 * \param self Bridge to operate upon
198 * \param bridge_channel Bridge channel to push
199 * \param swap Bridge channel to swap places with if not NULL
201 * \note On entry, self is already locked
203 * \retval 0 on success
204 * \retval -1 on failure
206 static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
208 struct parked_user *pu;
209 const char *blind_transfer;
210 RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); /* XXX replace with ast_channel_cleanup when available */
211 RAII_VAR(struct park_common_datastore *, park_datastore, NULL, park_common_datastore_free);
213 ast_bridge_base_v_table.push(&self->base, bridge_channel, swap);
215 ast_assert(self->lot != NULL);
217 /* Answer the channel if needed */
218 if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
219 ast_answer(bridge_channel->chan);
225 pu = swap->bridge_pvt;
227 /* This should be impossible since the only way a channel can enter in the first place
228 * is if it has a parked user associated with it */
229 publish_parked_call_failure(bridge_channel->chan);
234 /* Give the swap channel's parked user reference to the incoming channel */
235 pu->chan = bridge_channel->chan;
236 bridge_channel->bridge_pvt = pu;
237 swap->bridge_pvt = NULL;
239 /* TODO Add a parked call swap message type to relay information about parked channel swaps */
241 if (ast_bridge_channel_has_role(swap, "holding_participant")) {
242 const char *idle_mode = ast_bridge_channel_get_role_option(swap, "holding_participant", "idle_mode");
243 if (!ast_strlen_zero(idle_mode) && !strcmp(idle_mode, "ringing")) {
250 parking_set_duration(bridge_channel->features, pu);
252 if (parking_channel_set_roles(bridge_channel->chan, self->lot, use_ringing)) {
253 ast_log(LOG_WARNING, "Failed to apply holding bridge roles to %s while joining the parking lot.\n",
254 ast_channel_name(bridge_channel->chan));
257 publish_parked_call(pu, PARKED_CALL_SWAP);
262 if (!(park_datastore = get_park_common_datastore_copy(bridge_channel->chan))) {
263 /* There was either a failure to apply the datastore when performing park common setup or else we had alloc failures while cloning. Abort. */
266 parker = ast_channel_get_by_name(park_datastore->parker_uuid);
268 /* If the parker and the parkee are the same channel pointer, then the channel entered using
269 * the park application. It's possible that the channel that transferred it is still alive (particularly
270 * when a multichannel bridge is parked), so try to get the real parker if possible. */
271 ast_channel_lock(bridge_channel->chan);
272 blind_transfer = S_OR(pbx_builtin_getvar_helper(bridge_channel->chan, "BLINDTRANSFER"),
273 ast_channel_name(bridge_channel->chan));
274 if (blind_transfer) {
275 blind_transfer = ast_strdupa(blind_transfer);
277 ast_channel_unlock(bridge_channel->chan);
279 if (parker == bridge_channel->chan) {
280 struct ast_channel *real_parker = ast_channel_get_by_name(blind_transfer);
283 parker = real_parker;
287 pu = generate_parked_user(self->lot, bridge_channel->chan, parker,
288 park_datastore->parker_dial_string, park_datastore->randomize, park_datastore->time_limit);
291 publish_parked_call_failure(bridge_channel->chan);
295 /* If a comeback_override was provided, set it for the parked user's comeback string. */
296 if (park_datastore->comeback_override) {
297 strncpy(pu->comeback, park_datastore->comeback_override, sizeof(pu->comeback));
298 pu->comeback[sizeof(pu->comeback) - 1] = '\0';
301 /* Generate ParkedCall Stasis Message */
302 publish_parked_call(pu, PARKED_CALL);
304 /* If the parkee and the parker are the same and silence_announce isn't set, play the announcement to the parkee */
305 if (!strcmp(blind_transfer, ast_channel_name(bridge_channel->chan)) && !park_datastore->silence_announce) {
307 snprintf(saynum_buf, sizeof(saynum_buf), "%u %u", 0, pu->parking_space);
308 ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL);
311 /* Apply parking duration limits */
312 parking_set_duration(bridge_channel->features, pu);
314 /* Set this to the bridge pvt so that we don't have to refind the parked user associated with this bridge channel again. */
315 bridge_channel->bridge_pvt = pu;
317 ast_verb(3, "Parking '" COLORIZE_FMT "' in '" COLORIZE_FMT "' at space %d\n",
318 COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(bridge_channel->chan)),
319 COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
322 parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);
329 * \brief ast_bridge parking pull method.
332 * \param self Bridge to operate upon.
333 * \param bridge_channel Bridge channel to pull.
335 * \note On entry, self is already locked.
339 static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
341 RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
342 ast_bridge_base_v_table.pull(&self->base, bridge_channel);
344 /* Take over the bridge channel's pu reference. It will be released when we are done. */
345 pu = bridge_channel->bridge_pvt;
346 bridge_channel->bridge_pvt = NULL;
348 /* This should only happen if the exiting channel was swapped out */
353 /* If we got here without the resolution being set, that's because the call was hung up for some reason without
354 * timing out or being picked up. There may be some forcible park removals later, but the resolution should be
355 * handled in those cases */
357 if (pu->resolution == PARK_UNSET) {
358 pu->resolution = PARK_ABANDON;
362 /* Pull can still happen after the bridge starts dissolving, so make sure we still have a lot before trying to notify metermaids. */
364 parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
367 switch (pu->resolution) {
369 /* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
370 isn't allowed to be changed when it isn't currently PARK_UNSET. */
373 /* Since the call was abandoned without additional handling, we need to issue the give up event and unpark the user. */
374 publish_parked_call(pu, PARKED_CALL_GIVEUP);
375 unpark_parked_user(pu);
378 /* PARK_FORCED is currently unused, but it is expected that it would be handled similar to PARK_ANSWERED.
379 * There is currently no event related to forced parked calls either */
382 /* If answered or forced, the channel should be pulled from the bridge as part of that process and unlinked from
383 * the parking lot afterwards. We do need to apply bridge features though and play the courtesy tone if set. */
384 publish_parked_call(pu, PARKED_CALL_UNPARKED);
385 parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);
387 if (pu->lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLEE) {
388 ast_bridge_channel_queue_playfile(bridge_channel, NULL, pu->lot->cfg->courtesytone, NULL);
392 /* Timeout is similar to abandon because it simply sets the bridge state to end and doesn't
393 * actually pull the channel. Because of that, unpark should happen in here. */
394 publish_parked_call(pu, PARKED_CALL_TIMEOUT);
395 parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);
396 unpark_parked_user(pu);
403 * \brief ast_bridge parking notify_masquerade method.
406 * \param self Bridge to operate upon.
407 * \param bridge_channel Bridge channel that was masqueraded.
409 * \note On entry, self is already locked.
410 * \note XXX Stub... and it will probably go unused.
414 static void bridge_parking_notify_masquerade(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
416 ast_bridge_base_v_table.notify_masquerade(&self->base, bridge_channel);
419 static void bridge_parking_get_merge_priority(struct ast_bridge_parking *self)
421 ast_bridge_base_v_table.get_merge_priority(&self->base);
424 struct ast_bridge_methods ast_bridge_parking_v_table = {
426 .destroy = (ast_bridge_destructor_fn) bridge_parking_destroy,
427 .dissolving = (ast_bridge_dissolving_fn) bridge_parking_dissolving,
428 .push = (ast_bridge_push_channel_fn) bridge_parking_push,
429 .pull = (ast_bridge_pull_channel_fn) bridge_parking_pull,
430 .notify_masquerade = (ast_bridge_notify_masquerade_fn) bridge_parking_notify_masquerade,
431 .get_merge_priority = (ast_bridge_merge_priority_fn) bridge_parking_get_merge_priority,
434 static struct ast_bridge *ast_bridge_parking_init(struct ast_bridge_parking *self, struct parking_lot *bridge_lot)
440 /* If no lot is defined for the bridge, then we aren't allowing the bridge to be initialized. */
446 /* It doesn't need to be a reference since the bridge only lives as long as the parking lot lives. */
447 self->lot = bridge_lot;
452 struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot)
456 bridge = bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
457 bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
458 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
459 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM, "Parking", bridge_lot->name, NULL);
460 bridge = ast_bridge_parking_init(bridge, bridge_lot);
461 bridge = bridge_register(bridge);