56e51fc9ee518ac8554d03de6ed8936933373dab
[asterisk/asterisk.git] / res / parking / parking_bridge.c
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 Parking Bridge Class
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
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"
33
34 struct ast_bridge_parking
35 {
36         struct ast_bridge base;
37
38         /* private stuff for parking */
39         struct parking_lot *lot;
40 };
41
42 /*!
43  * \internal
44  * \brief ast_bridge parking class destructor
45  * \since 12.0.0
46  *
47  * \param self Bridge to operate upon.
48  *
49  * \note XXX Stub... and it might go unused.
50  *
51  * \return Nothing
52  */
53 static void bridge_parking_destroy(struct ast_bridge_parking *self)
54 {
55         ast_bridge_base_v_table.destroy(&self->base);
56 }
57
58 static void bridge_parking_dissolving(struct ast_bridge_parking *self)
59 {
60         self->lot = NULL;
61         ast_bridge_base_v_table.dissolving(&self->base);
62 }
63
64 static void destroy_parked_user(void *obj)
65 {
66         struct parked_user *pu = obj;
67
68         ao2_cleanup(pu->lot);
69         pu->lot = NULL;
70
71         ao2_cleanup(pu->parker);
72         pu->parker = NULL;
73 }
74
75 /*!
76  * \internal
77  * \since 12
78  * \brief Construct a parked_user struct assigned to the specified parking lot
79  *
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.
88  *
89  * \retval NULL on failure
90  * \retval reference to the parked user
91  *
92  * \note ao2_cleanup this reference when you are done using it or you'll cause leaks.
93  */
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)
95 {
96         struct parked_user *new_parked_user;
97         int preferred_space = -1; /* Initialize to use parking lot defaults */
98         int parking_space;
99         const char *parkingexten;
100
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");
103                 return NULL;
104         }
105
106         new_parked_user = ao2_alloc(sizeof(*new_parked_user), destroy_parked_user);
107         if (!new_parked_user) {
108                 return NULL;
109         }
110
111
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;
115         } else {
116                 ast_channel_lock(chan);
117                 if ((parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"))) {
118                         parkingexten = ast_strdupa(parkingexten);
119                 }
120                 ast_channel_unlock(chan);
121
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);
126                                 return NULL;
127                         }
128                 }
129         }
130
131
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. */
133         ao2_lock(lot);
134
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);
139                 ao2_unlock(lot);
140                 return NULL;
141         }
142
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;
146
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;
149         ao2_ref(lot, +1);
150
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);
156                 ao2_unlock(lot);
157                 return NULL;
158         }
159
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);
162         ao2_unlock(lot);
163
164         return new_parked_user;
165 }
166
167 /* TODO CEL events for parking */
168
169 /*!
170  * \internal
171  * \brief ast_bridge parking push method.
172  * \since 12.0.0
173  *
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
177  *
178  * \note On entry, self is already locked
179  *
180  * \retval 0 on success
181  * \retval -1 on failure
182  */
183 static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
184 {
185         struct parked_user *pu;
186         int randomize = 0;
187         int time_limit = -1;
188         int silence = 0;
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);
193
194         ast_bridge_base_v_table.push(&self->base, bridge_channel, swap);
195
196         ast_assert(self->lot != NULL);
197
198         /* Answer the channel if needed */
199         if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) {
200                 ast_answer(bridge_channel->chan);
201         }
202
203         if (swap) {
204                 ao2_lock(swap);
205                 pu = swap->bridge_pvt;
206                 if (!pu) {
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);
210                         ao2_unlock(swap);
211                         return -1;
212                 }
213
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;
218
219                 /* TODO Add a parked call swap message type to relay information about parked channel swaps */
220
221                 ao2_unlock(swap);
222
223                 parking_set_duration(bridge_channel->features, pu);
224
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.
228                  */
229                 parking_channel_set_roles(bridge_channel->chan, self->lot, 0);
230
231                 return 0;
232         }
233
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);
236
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);
245         }
246         ast_channel_unlock(bridge_channel->chan);
247
248         if (parker == bridge_channel->chan) {
249                 struct ast_channel *real_parker = ast_channel_get_by_name(blind_transfer);
250                 if (real_parker) {
251                         ao2_cleanup(parker);
252                         parker = real_parker;
253                 }
254         }
255
256         if (!parker) {
257                 return -1;
258         }
259
260         pu = generate_parked_user(self->lot, bridge_channel->chan, parker, randomize, time_limit);
261         if (!pu) {
262                 publish_parked_call_failure(bridge_channel->chan);
263                 return -1;
264         }
265
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';
270         }
271
272         /* Generate ParkedCall Stasis Message */
273         publish_parked_call(pu, PARKED_CALL);
274
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) {
277                 char saynum_buf[16];
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);
280         }
281
282         /* Apply parking duration limits */
283         parking_set_duration(bridge_channel->features, pu);
284
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;
287
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),
291                 pu->parking_space);
292
293         parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);
294
295         return 0;
296 }
297
298 /*!
299  * \internal
300  * \brief ast_bridge parking pull method.
301  * \since 12.0.0
302  *
303  * \param self Bridge to operate upon.
304  * \param bridge_channel Bridge channel to pull.
305  *
306  * \note On entry, self is already locked.
307  *
308  * \return Nothing
309  */
310 static void bridge_parking_pull(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
311 {
312         RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
313         ast_bridge_base_v_table.pull(&self->base, bridge_channel);
314
315         /* Take over the bridge channel's pu reference. It will be released when we are done. */
316         pu = bridge_channel->bridge_pvt;
317         bridge_channel->bridge_pvt = NULL;
318
319         /* This should only happen if the exiting channel was swapped out */
320         if (!pu) {
321                 return;
322         }
323
324         /* If we got here without the resolution being set, that's because the call was hung up for some reason without
325          * timing out or being picked up. There may be some forcible park removals later, but the resolution should be
326          * handled in those cases */
327         ao2_lock(pu);
328         if (pu->resolution == PARK_UNSET) {
329                 pu->resolution = PARK_ABANDON;
330         }
331         ao2_unlock(pu);
332
333         parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
334
335         switch (pu->resolution) {
336         case PARK_UNSET:
337                 /* This should be impossible now since the resolution is forcibly set to abandon if it was unset at this point. Resolution
338                    isn't allowed to be changed when it isn't currently PARK_UNSET. */
339                 return;
340         case PARK_ABANDON:
341                 /* Since the call was abandoned without additional handling, we need to issue the give up event and unpark the user. */
342                 publish_parked_call(pu, PARKED_CALL_GIVEUP);
343                 unpark_parked_user(pu);
344                 return;
345         case PARK_FORCED:
346                 /* PARK_FORCED is currently unused, but it is expected that it would be handled similar to PARK_ANSWERED.
347                  * There is currently no event related to forced parked calls either */
348                 return;
349         case PARK_ANSWERED:
350                 /* If answered or forced, the channel should be pulled from the bridge as part of that process and unlinked from
351                  * the parking lot afterwards. We do need to apply bridge features though and play the courtesy tone if set. */
352                 publish_parked_call(pu, PARKED_CALL_UNPARKED);
353                 parked_call_retrieve_enable_features(bridge_channel->chan, pu->lot, AST_FEATURE_FLAG_BYCALLEE);
354
355                 if (pu->lot->cfg->parkedplay & AST_FEATURE_FLAG_BYCALLEE) {
356                         ast_bridge_channel_queue_playfile(bridge_channel, NULL, pu->lot->cfg->courtesytone, NULL);
357                 }
358
359                 return;
360         case PARK_TIMEOUT:
361                 /* Timeout is similar to abandon because it simply sets the bridge state to end and doesn't
362                  * actually pull the channel. Because of that, unpark should happen in here. */
363                 publish_parked_call(pu, PARKED_CALL_TIMEOUT);
364                 unpark_parked_user(pu);
365                 return;
366         }
367 }
368
369 /*!
370  * \internal
371  * \brief ast_bridge parking notify_masquerade method.
372  * \since 12.0.0
373  *
374  * \param self Bridge to operate upon.
375  * \param bridge_channel Bridge channel that was masqueraded.
376  *
377  * \note On entry, self is already locked.
378  * \note XXX Stub... and it will probably go unused.
379  *
380  * \return Nothing
381  */
382 static void bridge_parking_notify_masquerade(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel)
383 {
384         ast_bridge_base_v_table.notify_masquerade(&self->base, bridge_channel);
385 }
386
387 static void bridge_parking_get_merge_priority(struct ast_bridge_parking *self)
388 {
389         ast_bridge_base_v_table.get_merge_priority(&self->base);
390 }
391
392 struct ast_bridge_methods ast_bridge_parking_v_table = {
393         .name = "parking",
394         .destroy = (ast_bridge_destructor_fn) bridge_parking_destroy,
395         .dissolving = (ast_bridge_dissolving_fn) bridge_parking_dissolving,
396         .push = (ast_bridge_push_channel_fn) bridge_parking_push,
397         .pull = (ast_bridge_pull_channel_fn) bridge_parking_pull,
398         .notify_masquerade = (ast_bridge_notify_masquerade_fn) bridge_parking_notify_masquerade,
399         .get_merge_priority = (ast_bridge_merge_priority_fn) bridge_parking_get_merge_priority,
400 };
401
402 static struct ast_bridge *ast_bridge_parking_init(struct ast_bridge_parking *self, struct parking_lot *bridge_lot)
403 {
404         if (!self) {
405                 return NULL;
406         }
407
408         /* If no lot is defined for the bridge, then we aren't allowing the bridge to be initialized. */
409         if (!bridge_lot) {
410                 ao2_ref(self, -1);
411                 return NULL;
412         }
413
414         /* It doesn't need to be a reference since the bridge only lives as long as the parking lot lives. */
415         self->lot = bridge_lot;
416
417         return &self->base;
418 }
419
420 struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot)
421 {
422         void *bridge;
423
424         bridge = ast_bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
425         bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
426                 AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
427                 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM);
428         bridge = ast_bridge_parking_init(bridge, bridge_lot);
429         bridge = ast_bridge_register(bridge);
430         return bridge;
431 }