channel locking: Add locking for channel snapshot creation
[asterisk/asterisk.git] / res / parking / parking_bridge_features.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 DTMF and Interval features
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/utils.h"
32 #include "asterisk/astobj2.h"
33 #include "asterisk/logger.h"
34 #include "asterisk/pbx.h"
35 #include "asterisk/bridge.h"
36 #include "asterisk/bridge_internal.h"
37 #include "asterisk/bridge_channel.h"
38 #include "asterisk/bridge_features.h"
39 #include "asterisk/features.h"
40 #include "asterisk/say.h"
41 #include "asterisk/datastore.h"
42 #include "asterisk/stasis.h"
43 #include "asterisk/module.h"
44 #include "asterisk/core_local.h"
45 #include "asterisk/causes.h"
46
47 struct parked_subscription_datastore {
48         struct stasis_subscription *parked_subscription;
49 };
50
51 struct parked_subscription_data {
52         char *parkee_uuid;
53         char parker_uuid[0];
54 };
55
56 static void parked_subscription_datastore_destroy(void *data)
57 {
58         struct parked_subscription_datastore *subscription_datastore = data;
59
60         stasis_unsubscribe(subscription_datastore->parked_subscription);
61         subscription_datastore->parked_subscription = NULL;
62
63         ast_free(subscription_datastore);
64 }
65
66 static const struct ast_datastore_info parked_subscription_info = {
67         .type = "park subscription",
68         .destroy = parked_subscription_datastore_destroy,
69 };
70
71 static void wipe_subscription_datastore(struct ast_channel *chan)
72 {
73         struct ast_datastore *datastore;
74
75         ast_channel_lock(chan);
76
77         datastore = ast_channel_datastore_find(chan, &parked_subscription_info, NULL);
78         if (datastore) {
79                 ast_channel_datastore_remove(chan, datastore);
80                 ast_datastore_free(datastore);
81         }
82         ast_channel_unlock(chan);
83 }
84
85 static void parker_parked_call_message_response(struct ast_parked_call_payload *message, struct parked_subscription_data *data,
86         struct stasis_subscription *sub)
87 {
88         const char *parkee_to_act_on = data->parkee_uuid;
89         char saynum_buf[16];
90         struct ast_channel_snapshot *parkee_snapshot = message->parkee;
91         RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup);
92         RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
93
94         if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) {
95                 return;
96         }
97
98         if (message->event_type != PARKED_CALL && message->event_type != PARKED_CALL_FAILED) {
99                 /* We only care about these two event types */
100                 return;
101         }
102
103         parker = ast_channel_get_by_name(data->parker_uuid);
104         if (!parker) {
105                 return;
106         }
107
108         ast_channel_lock(parker);
109         bridge_channel = ast_channel_get_bridge_channel(parker);
110         ast_channel_unlock(parker);
111         if (!bridge_channel) {
112                 return;
113         }
114
115         if (message->event_type == PARKED_CALL) {
116                 /* queue the saynum on the bridge channel and hangup */
117                 snprintf(saynum_buf, sizeof(saynum_buf), "%u %u", 1, message->parkingspace);
118                 ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL);
119                 wipe_subscription_datastore(bridge_channel->chan);
120         }
121
122         if (message->event_type == PARKED_CALL_FAILED) {
123                 ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL);
124                 wipe_subscription_datastore(bridge_channel->chan);
125         }
126 }
127
128 static void parker_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
129 {
130         if (stasis_subscription_final_message(sub, message)) {
131                 ast_free(data);
132                 return;
133         }
134
135         if (stasis_message_type(message) == ast_parked_call_type()) {
136                 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
137                 parker_parked_call_message_response(parked_call_message, data, sub);
138         }
139 }
140
141 static int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid)
142 {
143         struct ast_datastore *datastore;
144         struct parked_subscription_datastore *parked_datastore;
145         struct parked_subscription_data *subscription_data;
146
147         char *parker_uuid = ast_strdupa(ast_channel_uniqueid(chan));
148         size_t parker_uuid_size = strlen(parker_uuid) + 1;
149
150         /* If there is already a subscription, get rid of it. */
151         wipe_subscription_datastore(chan);
152
153         if (!(datastore = ast_datastore_alloc(&parked_subscription_info, NULL))) {
154                 return -1;
155         }
156
157         if (!(parked_datastore = ast_calloc(1, sizeof(*parked_datastore)))) {
158                 ast_datastore_free(datastore);
159                 return -1;
160         }
161
162         if (!(subscription_data = ast_calloc(1, sizeof(*subscription_data) + parker_uuid_size +
163                         strlen(parkee_uuid) + 1))) {
164                 ast_datastore_free(datastore);
165                 ast_free(parked_datastore);
166                 return -1;
167         }
168
169         subscription_data->parkee_uuid = subscription_data->parker_uuid + parker_uuid_size;
170         strcpy(subscription_data->parkee_uuid, parkee_uuid);
171         strcpy(subscription_data->parker_uuid, parker_uuid);
172
173         if (!(parked_datastore->parked_subscription = stasis_subscribe(ast_parking_topic(), parker_update_cb, subscription_data))) {
174                 return -1;
175         }
176
177         datastore->data = parked_datastore;
178
179         ast_channel_lock(chan);
180         ast_channel_datastore_add(chan, datastore);
181         ast_channel_unlock(chan);
182
183         return 0;
184 }
185
186 /*!
187  * \internal
188  * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly
189  *        identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the
190  *        local channel and the channel that instigated the park.
191  */
192 static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten)
193 {
194         char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
195         struct ast_channel *parkee;
196         struct ast_channel *parkee_side_2;
197         int cause;
198
199         /* Fill the variable with the extension and context we want to call */
200         snprintf(destination, sizeof(destination), "%s@%s", exten, context);
201
202         /* Now we request that chan_local prepare to call the destination */
203         parkee = ast_request("Local", ast_channel_nativeformats(parker), parker, destination,
204                 &cause);
205         if (!parkee) {
206                 return NULL;
207         }
208
209         /* Before we actually dial out let's inherit appropriate information. */
210         ast_channel_lock_both(parker, parkee);
211         ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker));
212         ast_channel_inherit_variables(parker, parkee);
213         ast_channel_datastore_inherit(parker, parkee);
214         ast_channel_unlock(parker);
215
216         parkee_side_2 = ast_local_get_peer(parkee);
217         ast_assert(parkee_side_2 != NULL);
218         ast_channel_unlock(parkee);
219
220         /* We need to have the parker subscribe to the new local channel before hand. */
221         create_parked_subscription(parker, ast_channel_uniqueid(parkee_side_2));
222
223         ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0);
224
225         ast_channel_unref(parkee_side_2);
226
227         /* Since the above worked fine now we actually call it and return the channel */
228         if (ast_call(parkee, destination, 0)) {
229                 ast_hangup(parkee);
230                 return NULL;
231         }
232
233         return parkee;
234 }
235
236 /*!
237  * \internal
238  * \brief Determine if an extension is a parking extension
239  */
240 static int parking_is_exten_park(const char *context, const char *exten)
241 {
242         struct ast_exten *exten_obj;
243         struct pbx_find_info info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
244         const char *app_at_exten;
245
246         ast_debug(4, "Checking if %s@%s is a parking exten\n", exten, context);
247         exten_obj = pbx_find_extension(NULL, NULL, &info, context, exten, 1, NULL, NULL, E_MATCH);
248         if (!exten_obj) {
249                 return 0;
250         }
251
252         app_at_exten = ast_get_extension_app(exten_obj);
253         if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
254                 return 0;
255         }
256
257         return 1;
258 }
259
260 /*!
261  * \internal
262  * \since 12.0.0
263  * \brief Perform a blind transfer to a parking lot
264  *
265  * In general, most parking features should work to call this function. This will safely
266  * park either a channel in the bridge with \ref bridge_channel or will park the entire
267  * bridge if more than one channel is in the bridge. It will create the correct data to
268  * pass to the \ref AstBridging Bridging API to safely park the channel.
269  *
270  * \param bridge_channel The bridge_channel representing the channel performing the park
271  * \param context The context to blind transfer to
272  * \param exten The extension to blind transfer to
273  *
274  * \retval 0 on success
275  * \retval non-zero on error
276  */
277 static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel,
278                 const char *context, const char *exten)
279 {
280         RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup);
281         int peer_count;
282
283         if (ast_strlen_zero(context) || ast_strlen_zero(exten)) {
284                 return -1;
285         }
286
287         if (!bridge_channel->in_bridge) {
288                 return -1;
289         }
290
291         if (!parking_is_exten_park(context, exten)) {
292                 return -1;
293         }
294
295         ast_bridge_channel_lock_bridge(bridge_channel);
296         peer_count = bridge_channel->bridge->num_channels;
297         if (peer_count == 2) {
298                 other = ast_bridge_channel_peer(bridge_channel);
299                 ao2_ref(other, +1);
300         }
301         ast_bridge_unlock(bridge_channel->bridge);
302
303         if (peer_count < 2) {
304                 /* There is nothing to do if there is no one to park. */
305                 return -1;
306         }
307
308         /* With a multiparty bridge, we need to do a regular blind transfer. We link the
309          * existing bridge to the parking lot with a Local channel rather than
310          * transferring others. */
311         if (peer_count > 2) {
312                 struct ast_channel *transfer_chan = NULL;
313
314                 transfer_chan = park_local_transfer(bridge_channel->chan, context, exten);
315                 if (!transfer_chan) {
316                         return -1;
317                 }
318
319                 if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL,
320                         AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
321                         ast_hangup(transfer_chan);
322                         return -1;
323                 }
324                 return 0;
325         }
326
327         /* Subscribe to park messages with the other channel entering */
328         if (create_parked_subscription(bridge_channel->chan, ast_channel_uniqueid(other->chan))) {
329                 return -1;
330         }
331
332         /* Write the park frame with the intended recipient and other data out to the bridge. */
333         ast_bridge_channel_write_park(bridge_channel,
334                 ast_channel_uniqueid(other->chan),
335                 ast_channel_uniqueid(bridge_channel->chan),
336                 NULL);
337
338         return 0;
339 }
340
341
342 /*!
343  * \internal
344  * \since 12.0.0
345  * \brief Perform a direct park on a channel in a bridge
346  *
347  * \note This will be called from within the \ref AstBridging Bridging API
348  *
349  * \param bridge_channel The bridge_channel representing the channel to be parked
350  * \param uuid_parkee The UUID of the channel being parked
351  * \param uuid_parker The UUID of the channel performing the park
352  * \param app_data Application parseable data to pass to the parking application
353  */
354 static int parking_park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data)
355 {
356         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
357         RAII_VAR(struct ast_bridge *, original_bridge, NULL, ao2_cleanup);
358         RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup);
359
360         if (strcmp(ast_channel_uniqueid(bridge_channel->chan), uuid_parkee)) {
361                 /* We aren't the parkee, so ignore this action. */
362                 return -1;
363         }
364
365         parker = ast_channel_get_by_name(uuid_parker);
366
367         if (!parker) {
368                 ast_log(LOG_NOTICE, "Channel with uuid %s left before we could start parking the call. Parking canceled.\n", uuid_parker);
369                 publish_parked_call_failure(bridge_channel->chan);
370                 return -1;
371         }
372
373         if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) {
374                 publish_parked_call_failure(bridge_channel->chan);
375                 return -1;
376         }
377
378         ast_bridge_set_transfer_variables(bridge_channel->chan, ast_channel_name(parker), 0);
379
380         /* bridge_channel must be locked so we can get a reference to the bridge it is currently on */
381         ao2_lock(bridge_channel);
382
383         original_bridge = bridge_channel->bridge;
384         if (!original_bridge) {
385                 ao2_unlock(bridge_channel);
386                 publish_parked_call_failure(bridge_channel->chan);
387                 return -1;
388         }
389
390         ao2_ref(original_bridge, +1); /* Cleaned by RAII_VAR */
391
392         ao2_unlock(bridge_channel);
393
394         if (ast_bridge_move(parking_bridge, original_bridge, bridge_channel->chan, NULL, 1)) {
395                 ast_log(LOG_ERROR, "Failed to move %s into the parking bridge.\n",
396                         ast_channel_name(bridge_channel->chan));
397                 return -1;
398         }
399
400         return 0;
401 }
402
403 /*!
404  * \internal
405  * \since 12.0.0
406  * \brief Park a call
407  *
408  * \param parker The bridge_channel parking the call
409  * \param exten Optional. The extension where the call was parked.
410  * \param length Optional. If \c exten is specified, the length of the buffer.
411  *
412  * \note This will determine the context and extension to park the channel based on
413  * the configuration of the \ref ast_channel associated with \ref parker. It will then
414  * park either the channel or the entire bridge.
415  *
416  * \retval 0 on success
417  * \retval -1 on error
418  */
419 static int parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length)
420 {
421         RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
422         const char *lot_name = NULL;
423
424         ast_channel_lock(parker->chan);
425         lot_name = find_channel_parking_lot_name(parker->chan);
426         if (!ast_strlen_zero(lot_name)) {
427                 lot_name = ast_strdupa(lot_name);
428         }
429         ast_channel_unlock(parker->chan);
430
431         if (ast_strlen_zero(lot_name)) {
432                 return -1;
433         }
434
435         lot = parking_lot_find_by_name(lot_name);
436         if (!lot) {
437                 ast_log(AST_LOG_WARNING, "Cannot Park %s: lot %s unknown\n",
438                         ast_channel_name(parker->chan), lot_name);
439                 return -1;
440         }
441
442         if (exten) {
443                 ast_copy_string(exten, lot->cfg->parkext, length);
444         }
445         return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext);
446 }
447
448 static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
449 {
450         SCOPED_MODULE_USE(parking_get_module_info()->self);
451
452         return parking_park_call(bridge_channel, NULL, 0);
453 }
454
455 /*!
456  * \internal
457  * \brief Setup the caller features for when that channel is dialed.
458  * \since 12.0.0
459  *
460  * \param chan Parked channel leaving the parking lot.
461  * \param cfg Parking lot configuration.
462  *
463  * \return Nothing
464  */
465 static void parking_timeout_set_caller_features(struct ast_channel *chan, struct parking_lot_cfg *cfg)
466 {
467         char features[5];
468         char *pos;
469
470         /*
471          * We are setting the callee Dial flag values because in the
472          * timeout case, the caller is who is being called back.
473          */
474         pos = features;
475         if (cfg->parkedcalltransfers & AST_FEATURE_FLAG_BYCALLER) {
476                 *pos++ = 't';
477         }
478         if (cfg->parkedcallreparking & AST_FEATURE_FLAG_BYCALLER) {
479                 *pos++ = 'k';
480         }
481         if (cfg->parkedcallhangup & AST_FEATURE_FLAG_BYCALLER) {
482                 *pos++ = 'h';
483         }
484         if (cfg->parkedcallrecording & AST_FEATURE_FLAG_BYCALLER) {
485                 *pos++ = 'x';
486         }
487         *pos = '\0';
488
489         pbx_builtin_setvar_helper(chan, "BRIDGE_FEATURES", features);
490 }
491
492 /*! \internal
493  * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
494  *
495  * \param bridge_channel bridge channel this interval hook is being executed on
496  * \param hook_pvt A pointer to the parked_user struct associated with the channel is stuffed in here
497  */
498 static int parking_duration_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
499 {
500         struct parked_user *user = hook_pvt;
501         struct ast_channel *chan = user->chan;
502         struct ast_context *park_dial_context;
503         const char *dial_string;
504         char *dial_string_flat;
505         char parking_space[AST_MAX_EXTENSION];
506
507         char returnexten[AST_MAX_EXTENSION];
508         char *duplicate_returnexten;
509         struct ast_exten *existing_exten;
510         struct pbx_find_info pbx_finder = { .stacklen = 0 }; /* The rest is reset in pbx_find_extension */
511
512
513         /* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
514            to deal with this, lock the parked user, check and set resolution. */
515         ao2_lock(user);
516         if (user->resolution != PARK_UNSET) {
517                 /* Abandon timeout since something else has resolved the parked user before we got to it. */
518                 ao2_unlock(user);
519                 return -1;
520         }
521         user->resolution = PARK_TIMEOUT;
522         ao2_unlock(user);
523
524         ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
525                 AST_CAUSE_NORMAL_CLEARING);
526
527         dial_string = user->parker_dial_string;
528         dial_string_flat = ast_strdupa(dial_string);
529         flatten_dial_string(dial_string_flat);
530
531         /* Set parking timeout channel variables */
532         snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
533         ast_channel_lock(chan);
534         ast_channel_stage_snapshot(chan);
535         pbx_builtin_setvar_helper(chan, "PARKING_SPACE", parking_space);
536         pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
537         pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
538         pbx_builtin_setvar_helper(chan, "PARKER", dial_string);
539         pbx_builtin_setvar_helper(chan, "PARKER_FLAT", dial_string_flat);
540         parking_timeout_set_caller_features(chan, user->lot->cfg);
541         ast_channel_stage_snapshot_done(chan);
542         ast_channel_unlock(chan);
543
544         /* Dialplan generation for park-dial extensions */
545
546         if (ast_wrlock_contexts()) {
547                 ast_log(LOG_ERROR, "Failed to lock the contexts list. Can't add the park-dial extension.\n");
548                 return -1;
549         }
550
551         if (!(park_dial_context = ast_context_find_or_create(NULL, NULL, PARK_DIAL_CONTEXT, BASE_REGISTRAR))) {
552                 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", PARK_DIAL_CONTEXT);
553                 if (ast_unlock_contexts()) {
554                         ast_assert(0);
555                 }
556                 goto abandon_extension_creation;
557         }
558
559         if (ast_wrlock_context(park_dial_context)) {
560                 ast_log(LOG_ERROR, "failed to obtain write lock on context '%s'\n", PARK_DIAL_CONTEXT);
561                 if (ast_unlock_contexts()) {
562                         ast_assert(0);
563                 }
564                 goto abandon_extension_creation;
565         }
566
567         if (ast_unlock_contexts()) {
568                 ast_assert(0);
569         }
570
571         snprintf(returnexten, sizeof(returnexten), "%s,%u", dial_string,
572                 user->lot->cfg->comebackdialtime);
573
574         duplicate_returnexten = ast_strdup(returnexten);
575         if (!duplicate_returnexten) {
576                 ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
577                         dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
578         }
579
580         /* If an extension already exists here because we registered it for another parked call timing out, then we may overwrite it. */
581         if ((existing_exten = pbx_find_extension(NULL, NULL, &pbx_finder, PARK_DIAL_CONTEXT, dial_string_flat, 1, NULL, NULL, E_MATCH)) &&
582             (strcmp(ast_get_extension_registrar(existing_exten), BASE_REGISTRAR))) {
583                 ast_debug(3, "An extension for '%s@%s' was already registered by another registrar '%s'\n",
584                         dial_string_flat, PARK_DIAL_CONTEXT, ast_get_extension_registrar(existing_exten));
585         } else if (ast_add_extension2_nolock(park_dial_context, 1, dial_string_flat, 1, NULL, NULL,
586                         "Dial", duplicate_returnexten, ast_free_ptr, BASE_REGISTRAR)) {
587                         ast_free(duplicate_returnexten);
588                 ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
589                         dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
590         }
591
592         if (ast_unlock_context(park_dial_context)) {
593                 ast_assert(0);
594         }
595
596 abandon_extension_creation:
597
598         /* async_goto the proper PBX destination - this should happen when we come out of the bridge */
599         if (!ast_strlen_zero(user->comeback)) {
600                 ast_async_parseable_goto(chan, user->comeback);
601         } else {
602                 comeback_goto(user, user->lot);
603         }
604
605         return -1;
606 }
607
608 void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *payload)
609 {
610         int numeric_value;
611         int hangup_after;
612
613         if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) {
614                 /* If say_parking_space is called with a non-numeric string, we have a problem. */
615                 ast_assert(0);
616                 ast_bridge_channel_leave_bridge(bridge_channel,
617                         BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
618                 return;
619         }
620
621         ast_say_digits(bridge_channel->chan, numeric_value, "",
622                 ast_channel_language(bridge_channel->chan));
623
624         if (hangup_after) {
625                 ast_bridge_channel_leave_bridge(bridge_channel,
626                         BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
627         }
628 }
629
630 void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user)
631 {
632         unsigned int time_limit;
633
634         time_limit = user->time_limit * 1000;
635
636         if (!time_limit) {
637                 /* There is no duration limit that we need to apply. */
638                 return;
639         }
640
641         /* If the time limit has already been passed, set a really low time limit so we can kick them out immediately. */
642         time_limit = ast_remaining_ms(user->start, time_limit);
643         if (time_limit <= 0) {
644                 time_limit = 1;
645         }
646
647         /* The interval hook is going to need a reference to the parked_user */
648         ao2_ref(user, +1);
649
650         if (ast_bridge_interval_hook(features, 0, time_limit,
651                 parking_duration_callback, user, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
652                 ast_log(LOG_ERROR, "Failed to apply duration limit to the parked call.\n");
653                 ao2_ref(user, -1);
654         }
655 }
656
657 struct ast_parking_bridge_feature_fn_table parking_provider = {
658         .module_version = PARKING_MODULE_VERSION,
659         .module_name = __FILE__,
660         .parking_is_exten_park = parking_is_exten_park,
661         .parking_blind_transfer_park = parking_blind_transfer_park,
662         .parking_park_bridge_channel = parking_park_bridge_channel,
663         .parking_park_call = parking_park_call,
664 };
665
666 void unload_parking_bridge_features(void)
667 {
668         ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_PARKCALL);
669         ast_parking_unregister_bridge_features(parking_provider.module_name);
670 }
671
672 int load_parking_bridge_features(void)
673 {
674         parking_provider.module_info = parking_get_module_info();
675
676         if (ast_parking_register_bridge_features(&parking_provider)) {
677                 return -1;
678         }
679
680         if (ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park_call, NULL)) {
681                 return -1;
682         }
683
684         return 0;
685 }