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 "asterisk/logger.h"
28 #include "res_parking.h"
29 #include "asterisk/astobj2.h"
30 #include "asterisk/features.h"
31 #include "asterisk/say.h"
32 #include "asterisk/term.h"
34 struct ast_bridge_parking
36 struct ast_bridge base;
38 /* private stuff for parking */
39 struct parking_lot *lot;
44 * \brief ast_bridge parking class destructor
47 * \param self Bridge to operate upon.
49 * \note XXX Stub... and it might go unused.
53 static void bridge_parking_destroy(struct ast_bridge_parking *self)
55 ast_bridge_base_v_table.destroy(&self->base);
58 static void bridge_parking_dissolving(struct ast_bridge_parking *self)
61 ast_bridge_base_v_table.dissolving(&self->base);
64 static void destroy_parked_user(void *obj)
66 struct parked_user *pu = obj;
71 ao2_cleanup(pu->parker);
78 * \brief Construct a parked_user struct assigned to the specified parking lot
80 * \param lot The parking lot we are assigning the user to
81 * \param parkee The channel being parked
82 * \param parker The channel performing the park operation (may be the same channel)
83 * \param use_random_space if true, prioritize using a random parking space instead
84 * of ${PARKINGEXTEN} and/or automatic assignment from the parking lot
85 * \param time_limit If using a custom timeout, this should be supplied so that the
86 * parked_user struct can provide this information for manager events. If <0,
87 * use the parking lot limit instead.
89 * \retval NULL on failure
90 * \retval reference to the parked user
92 * \note ao2_cleanup this reference when you are done using it or you'll cause leaks.
94 static struct parked_user *generate_parked_user(struct parking_lot *lot, struct ast_channel *chan, struct ast_channel *parker, int use_random_space, int time_limit)
96 struct parked_user *new_parked_user;
97 int preferred_space = -1; /* Initialize to use parking lot defaults */
99 const char *parkingexten;
101 if (lot->mode == PARKINGLOT_DISABLED) {
102 ast_log(LOG_NOTICE, "Tried to park in a parking lot that is no longer able to be parked to.\n");
106 new_parked_user = ao2_alloc(sizeof(*new_parked_user), destroy_parked_user);
107 if (!new_parked_user) {
112 if (use_random_space) {
113 preferred_space = ast_random() % (lot->cfg->parking_stop - lot->cfg->parking_start + 1);
114 preferred_space += lot->cfg->parking_start;
116 ast_channel_lock(chan);
117 if ((parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"))) {
118 parkingexten = ast_strdupa(parkingexten);
120 ast_channel_unlock(chan);
122 if (!ast_strlen_zero(parkingexten)) {
123 if (sscanf(parkingexten, "%30d", &preferred_space) != 1 || preferred_space <= 0) {
124 ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n", parkingexten);
125 ao2_ref(new_parked_user, -1);
132 /* 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. */
135 parking_space = parking_lot_get_space(lot, preferred_space);
136 if (parking_space == -1) {
137 ast_log(LOG_NOTICE, "Failed to get parking space in lot '%s'. All full.\n", lot->name);
138 ao2_ref(new_parked_user, -1);
143 lot->next_space = ((parking_space + 1) - lot->cfg->parking_start) % (lot->cfg->parking_stop - lot->cfg->parking_start + 1) + lot->cfg->parking_start;
144 new_parked_user->chan = chan;
145 new_parked_user->parking_space = parking_space;
147 /* Have the parked user take a reference to the parking lot. This reference should be immutable and released at destruction */
148 new_parked_user->lot = lot;
151 new_parked_user->start = ast_tvnow();
152 new_parked_user->time_limit = (time_limit >= 0) ? time_limit : lot->cfg->parkingtime;
153 new_parked_user->parker = ast_channel_snapshot_create(parker);
154 if (!new_parked_user->parker) {
155 ao2_ref(new_parked_user, -1);
160 /* Insert into the parking lot's parked user list. We can unlock the lot now. */
161 ao2_link(lot->parked_users, new_parked_user);
164 return new_parked_user;
167 /* TODO CEL events for parking */
171 * \brief ast_bridge parking push method.
174 * \param self Bridge to operate upon
175 * \param bridge_channel Bridge channel to push
176 * \param swap Bridge channel to swap places with if not NULL
178 * \note On entry, self is already locked
180 * \retval 0 on success
181 * \retval -1 on failure
183 static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
185 struct parked_user *pu;
189 const char *blind_transfer;
190 RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup);
191 RAII_VAR(char *, parker_uuid, NULL, ast_free);
192 RAII_VAR(char *, comeback_override, NULL, ast_free);
194 ast_bridge_base_v_table.push(&self->base, bridge_channel, swap);
196 ast_assert(self->lot != NULL);
198 /* Answer the channel if needed */
199 if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
200 ast_answer(bridge_channel->chan);
205 pu = swap->bridge_pvt;
207 /* This should be impossible since the only way a channel can enter in the first place
208 * is if it has a parked user associated with it */
209 publish_parked_call_failure(bridge_channel->chan);
214 /* Give the swap channel's parked user reference to the incoming channel */
215 pu->chan = bridge_channel->chan;
216 bridge_channel->bridge_pvt = pu;
217 swap->bridge_pvt = NULL;
219 /* TODO Add a parked call swap message type to relay information about parked channel swaps */
223 parking_set_duration(bridge_channel->features, pu);
225 /* BUGBUG Adding back local channel swapping made us not hear music on hold for the channel that got swapped
226 * into the parking lot. Setting the roels back up gets around that, but we still need to deal with the ringing option
227 * to the park application here somehow.
229 parking_channel_set_roles(bridge_channel->chan, self->lot, 0);
234 get_park_common_datastore_data(bridge_channel->chan, &parker_uuid, &comeback_override, &randomize, &time_limit, &silence);
235 parker = ast_channel_get_by_name(parker_uuid);
237 /* If the parker and the parkee are the same channel pointer, then the channel entered using
238 * the park application. It's possible the the blindtransfer channel is still alive (particularly
239 * when a multichannel bridge is parked), so try to get the real parker if possible. */
240 ast_channel_lock(bridge_channel->chan);
241 blind_transfer = S_OR(pbx_builtin_getvar_helper(bridge_channel->chan, "BLINDTRANSFER"),
242 ast_channel_name(bridge_channel->chan));
243 if (blind_transfer) {
244 blind_transfer = ast_strdupa(blind_transfer);
246 ast_channel_unlock(bridge_channel->chan);
248 if (parker == bridge_channel->chan) {
249 struct ast_channel *real_parker = ast_channel_get_by_name(blind_transfer);
252 parker = real_parker;
260 pu = generate_parked_user(self->lot, bridge_channel->chan, parker, randomize, time_limit);
262 publish_parked_call_failure(bridge_channel->chan);
266 /* If a comeback_override was provided, set it for the parked user's comeback string. */
267 if (comeback_override) {
268 strncpy(pu->comeback, comeback_override, sizeof(pu->comeback));
269 pu->comeback[sizeof(pu->comeback) - 1] = '\0';
272 /* Generate ParkedCall Stasis Message */
273 publish_parked_call(pu, PARKED_CALL);
275 /* If the parkee and the parker are the same and silence_announce isn't set, play the announcement to the parkee */
276 if (!strcmp(blind_transfer, ast_channel_name(bridge_channel->chan)) && !silence) {
278 snprintf(saynum_buf, sizeof(saynum_buf), "%u %u", 0, pu->parking_space);
279 ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL);
282 /* Apply parking duration limits */
283 parking_set_duration(bridge_channel->features, pu);
285 /* Set this to the bridge pvt so that we don't have to refind the parked user associated with this bridge channel again. */
286 bridge_channel->bridge_pvt = pu;
288 ast_verb(3, "Parking '" COLORIZE_FMT "' in '" COLORIZE_FMT "' at space %d\n",
289 COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(bridge_channel->chan)),
290 COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
298 * \brief ast_bridge parking pull method.
301 * \param self Bridge to operate upon.
302 * \param bridge_channel Bridge channel to pull.
304 * \note On entry, self is already locked.
308 static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
310 RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
311 ast_bridge_base_v_table.pull(&self->base, bridge_channel);
313 /* Take over the bridge channel's pu reference. It will be released when we are done. */
314 pu = bridge_channel->bridge_pvt;
315 bridge_channel->bridge_pvt = NULL;
317 /* This should only happen if the exiting channel was swapped out */
322 /* If we got here without the resolution being set, that's because the call was hung up for some reason without
323 * timing out or being picked up. There may be some forcible park removals later, but the resolution should be
324 * handled in those cases */
326 if (pu->resolution == PARK_UNSET) {
327 pu->resolution = PARK_ABANDON;
331 switch (pu->resolution) {
333 /* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
334 isn't allowed to be changed when it isn't currently PARK_UNSET. */
337 /* Since the call was abandoned without additional handling, we need to issue the give up event and unpark the user. */
338 publish_parked_call(pu, PARKED_CALL_GIVEUP);
339 unpark_parked_user(pu);
342 /* PARK_FORCED is currently unused, but it is expected that it would be handled similar to PARK_ANSWERED.
343 * There is currently no event related to forced parked calls either */
346 /* If answered or forced, the channel should be pulled from the bridge as part of that process and unlinked from
347 * the parking lot afterwards. We do need to apply bridge features though and play the courtesy tone if set. */
348 publish_parked_call(pu, PARKED_CALL_UNPARKED);
349 parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);
351 if (pu->lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLEE) {
352 ast_bridge_channel_queue_playfile(bridge_channel, NULL, pu->lot->cfg->courtesytone, NULL);
357 /* Timeout is similar to abandon because it simply sets the bridge state to end and doesn't
358 * actually pull the channel. Because of that, unpark should happen in here. */
359 publish_parked_call(pu, PARKED_CALL_TIMEOUT);
360 unpark_parked_user(pu);
367 * \brief ast_bridge parking notify_masquerade method.
370 * \param self Bridge to operate upon.
371 * \param bridge_channel Bridge channel that was masqueraded.
373 * \note On entry, self is already locked.
374 * \note XXX Stub... and it will probably go unused.
378 static void bridge_parking_notify_masquerade(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
380 ast_bridge_base_v_table.notify_masquerade(&self->base, bridge_channel);
383 static void bridge_parking_get_merge_priority(struct ast_bridge_parking *self)
385 ast_bridge_base_v_table.get_merge_priority(&self->base);
388 struct ast_bridge_methods ast_bridge_parking_v_table = {
390 .destroy = (ast_bridge_destructor_fn) bridge_parking_destroy,
391 .dissolving = (ast_bridge_dissolving_fn) bridge_parking_dissolving,
392 .push = (ast_bridge_push_channel_fn) bridge_parking_push,
393 .pull = (ast_bridge_pull_channel_fn) bridge_parking_pull,
394 .notify_masquerade = (ast_bridge_notify_masquerade_fn) bridge_parking_notify_masquerade,
395 .get_merge_priority = (ast_bridge_merge_priority_fn) bridge_parking_get_merge_priority,
398 static struct ast_bridge *ast_bridge_parking_init(struct ast_bridge_parking *self, struct parking_lot *bridge_lot)
404 /* If no lot is defined for the bridge, then we aren't allowing the bridge to be initialized. */
410 /* It doesn't need to be a reference since the bridge only lives as long as the parking lot lives. */
411 self->lot = bridge_lot;
416 struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot)
420 bridge = ast_bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
421 bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
422 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
423 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM);
424 bridge = ast_bridge_parking_init(bridge, bridge_lot);
425 bridge = ast_bridge_register(bridge);