Bridge API: Set a cause code on a channel when it is ejected from a bridge.
[asterisk/asterisk.git] / res / res_parking.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 Call Parking Resource
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend>bridge_holding</depend>
28         <support_level>core</support_level>
29  ***/
30
31 /*** DOCUMENTATION
32         <configInfo name="res_parking" language="en_US">
33                 <configFile name="res_parking.conf">
34                         <configObject name="globals">
35                                 <synopsis>Options that apply to every parking lot</synopsis>
36                                 <configOption name="parkeddynamic">
37                                         <synopsis>Enables dynamically created parkinglots.</synopsis>
38                                 </configOption>
39                         </configObject>
40                         <configObject name="parking_lot">
41                                 <synopsis>Defined parking lots for res_parking to use to park calls on</synopsis>
42                                 <configOption name="context" default="parkedcalls">
43                                         <synopsis>The name of the context where calls are parked and picked up from.</synopsis>
44                                         <description><para>This option is only used if parkext is set.</para></description>
45                                 </configOption>
46                                 <configOption name="parkext">
47                                         <synopsis>Extension to park calls to this parking lot.</synopsis>
48                                         <description><para>If this option is used, this extension will automatically be created to place calls into
49                         parking lots. In addition, if parkext_exclusive is set for this parking lot, the name of the parking lot
50                         will be included in the application's arguments so that it only parks to this parking lot. The extension
51                         will be created in <literal>context</literal>. Using this option also creates extensions for retrieving
52                         parked calls from the parking spaces in the same context.</para></description>
53                                 </configOption>
54                                 <configOption name="parkext_exclusive" default="no">
55                                         <synopsis>If yes, the extension registered as parkext will park exclusively to this parking lot.</synopsis>
56                                 </configOption>
57                                 <configOption name="parkpos" default="701-750">
58                                         <synopsis>Numerical range of parking spaces which can be used to retrieve parked calls.</synopsis>
59                                         <description><para>If parkext is set, these extensions will automatically be mapped in <literal>context</literal>
60                                                 in order to pick up calls parked to these parking spaces.</para></description>
61                                 </configOption>
62                                 <configOption name="parkinghints" default="no">
63                                         <synopsis>If yes, this parking lot will add hints automatically for parking spaces.</synopsis>
64                                 </configOption>
65                                 <configOption name="parkingtime" default="45">
66                                         <synopsis>Amount of time a call will remain parked before giving up (in seconds).</synopsis>
67                                 </configOption>
68                                 <configOption name="parkedmusicclass">
69                                         <synopsis>Which music class to use for parked calls. They will use the default if unspecified.</synopsis>
70                                 </configOption>
71                                 <configOption name="comebacktoorigin" default="yes">
72                                         <synopsis>Determines what should be done with the parked channel if no one picks it up before it times out.</synopsis>
73                                         <description><para>Valid Options:</para>
74                                                 <enumlist>
75                                                         <enum name="yes">
76                                                                 <para>Automatically have the parked channel dial the device that parked the call with dial
77                                                                         timeout set by the <literal>parkingtime</literal> option. When the call times out an extension
78                                                                         to dial the PARKER will automatically be created in the <literal>park-dial</literal> context with
79                                                                         an extension of the flattened parker device name. If the call is not answered, the parked channel
80                                                                         that is timing out will continue in the dial plan at that point if there are more priorities in
81                                                                         the extension (which won't be the case unless the dialplan deliberately includes such priorities
82                                                                         in the <literal>park-dial</literal> context through pattern matching or deliberately written
83                                                                         flattened peer extensions).</para>
84                                                         </enum>
85                                                         <enum name="no">
86                                                                 <para>Place the call into the PBX at <literal>comebackcontext</literal> instead. The extension will
87                                                                         still be set as the flattened peer name. If an extension the flattened peer name isn't available
88                                                                         then it will fall back to the <literal>s</literal> extension. If that also is unavailable it will
89                                                                         attempt to fall back to <literal>s@default</literal>. The normal dial extension will still be
90                                                                         created in the <literal>park-dial</literal> context with the extension also being the flattened
91                                                                         peer name.</para>
92                                                         </enum>
93                                                 </enumlist>
94                                                 <note><para>Flattened Peer Names - Extensions can not include slash characters since those are used for pattern
95                                                         matching. When a peer name is flattened, slashes become underscores. For example if the parker of a call
96                                                         is called <literal>SIP/0004F2040001</literal> then flattened peer name and therefor the extensions created
97                                                         and used on timeouts will be <literal>SIP_0004F204001</literal>.</para></note>
98                                                 <note><para>When parking times out and the channel returns to the dial plan, the following variables are set:
99                                                 </para></note>
100                                                 <variablelist>
101                                                         <variable name="PARKINGSLOT">
102                                                                 <para>extension that the call was parked in prior to timing out.</para>
103                                                         </variable>
104                                                         <variable name="PARKEDLOT">
105                                                                 <para>name of the lot that the call was parked in prior to timing out.</para>
106                                                         </variable>
107                                                         <variable name="PARKER">
108                                                                 <para>The device that parked the call</para>
109                                                         </variable>
110                                                 </variablelist>
111                                         </description>
112                                 </configOption>
113                                 <configOption name="comebackdialtime" default="30">
114                                         <synopsis>Timeout for the Dial extension created to call back the parker when a parked call times out.</synopsis>
115                                 </configOption>
116                                 <configOption name="comebackcontext" default="parkedcallstimeout">
117                                         <synopsis>Context where parked calls will enter the PBX on timeout when comebacktoorigin=no</synopsis>
118                                         <description><para>The extension the call enters will prioritize the flattened peer name in this context.
119                                                 If the flattened peer name extension is unavailable, then the 's' extension in this context will be
120                                                 used. If that also is unavailable, the 's' extension in the 'default' context will be used.</para>
121                                         </description>
122                                 </configOption>
123                                 <configOption name="courtesytone">
124                                         <synopsis>If the name of a sound file is provided, use this as the courtesy tone</synopsis>
125                                         <description><para>By default, this tone is only played to the caller of a parked call. Who receives the tone
126                                                 can be changed using the <literal>parkedplay</literal> option.</para>
127                                         </description>
128                                 </configOption>
129                                 <configOption name="parkedplay" default="caller">
130                                         <synopsis>Who we should play the courtesytone to on the pickup of a parked call from this lot</synopsis>
131                                         <description>
132                                                 <enumlist>
133                                                         <enum name="no"><para>Apply to neither side.</para></enum>
134                                                         <enum name="caller"><para>Apply to only to the caller picking up the parked call.</para></enum>
135                                                         <enum name="callee"><para>Apply to only to the parked call being picked up.</para></enum>
136                                                         <enum name="both"><para>Apply to both the caller and the callee.</para></enum>
137                                                 </enumlist>
138                                                 <note><para>If courtesy tone is not specified then this option will be ignored.</para></note>
139                                         </description>
140                                 </configOption>
141                                 <configOption name="parkedcalltransfers" default="no">
142                                         <synopsis>Apply the DTMF transfer features to the caller and/or callee when parked calls are picked up.</synopsis>
143                                         <description>
144                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
145                                         </description>
146                                 </configOption>
147                                 <configOption name="parkedcallreparking" default="no">
148                                         <synopsis>Apply the DTMF parking feature to the caller and/or callee when parked calls are picked up.</synopsis>
149                                         <description>
150                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
151                                         </description>
152                                 </configOption>
153                                 <configOption name="parkedcallhangup" default="no">
154                                         <synopsis>Apply the DTMF Hangup feature to the caller and/or callee when parked calls are picked up.</synopsis>
155                                         <description>
156                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
157                                         </description>
158                                 </configOption>
159                                 <configOption name="parkedcallrecording" default="no">
160                                         <synopsis>Apply the DTMF recording features to the caller and/or callee when parked calls are picked up</synopsis>
161                                         <description>
162                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
163                                         </description>
164                                 </configOption>
165                                 <configOption name="findslot" default="first">
166                                         <synopsis>Rule to use when trying to figure out which parking space a call should be parked with.</synopsis>
167                                         <description>
168                                                 <enumlist>
169                                                         <enum name="first"><para>Always try to place in the lowest available space in the parking lot</para></enum>
170                                                         <enum name="next"><para>Track the last parking space used and always attempt to use the one immediately after.
171                                                         </para></enum>
172                                                 </enumlist>
173                                         </description>
174                                 </configOption>
175                                 <configOption name="courtesytone">
176                                         <synopsis>If set, the sound set will be played to whomever is set by parkedplay</synopsis>
177                                 </configOption>
178                         </configObject>
179                 </configFile>
180         </configInfo>
181  ***/
182
183 #include "asterisk.h"
184
185 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
186
187 #include "parking/res_parking.h"
188 #include "asterisk/config.h"
189 #include "asterisk/config_options.h"
190 #include "asterisk/utils.h"
191 #include "asterisk/module.h"
192 #include "asterisk/cli.h"
193 #include "asterisk/astobj2.h"
194 #include "asterisk/features.h"
195 #include "asterisk/manager.h"
196 #include "asterisk/pbx.h"
197
198 static int parking_lot_sort_fn(const void *obj_left, const void *obj_right, int flags)
199 {
200         const struct parking_lot *left = obj_left;
201         const struct parking_lot *right = obj_right;
202         const char *right_key = obj_right;
203         int cmp;
204
205         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
206         default:
207         case OBJ_POINTER:
208                 right_key = right->name;
209                 /* Fall through */
210         case OBJ_KEY:
211                 cmp = strcmp(left->name, right_key);
212                 break;
213         case OBJ_PARTIAL_KEY:
214                 cmp = strncmp(left->name, right_key, strlen(right_key));
215         }
216         return cmp;
217 }
218
219 /*! All parking lots that are currently alive in some fashion can be obtained from here */
220 static struct ao2_container *parking_lot_container;
221
222 static void *parking_config_alloc(void);
223
224 static void *parking_lot_cfg_alloc(const char *cat);
225 static void *named_item_find(struct ao2_container *container, const char *name); /* XXX This is really just a generic string find. Move to astobj2.c? */
226
227 static int config_parking_preapply(void);
228 static void link_configured_disable_marked_lots(void);
229
230 struct parking_global_config {
231         int parkeddynamic;
232 };
233
234 struct parking_config {
235         struct parking_global_config *global;
236         struct ao2_container *parking_lots;
237 };
238
239 static struct aco_type global_option = {
240         .type = ACO_GLOBAL,
241         .name = "globals",
242         .item_offset = offsetof(struct parking_config, global),
243         .category_match = ACO_WHITELIST,
244         .category = "^general$",
245 };
246
247 struct aco_type *global_options[] = ACO_TYPES(&global_option);
248
249 static struct aco_type parking_lot_type = {
250         .type = ACO_ITEM,
251         .name = "parking_lot",
252         .category_match = ACO_BLACKLIST,
253         .category = "^(general)$",
254         .item_alloc = parking_lot_cfg_alloc,
255         .item_find = named_item_find,
256         .item_offset = offsetof(struct parking_config, parking_lots),
257 };
258
259 struct aco_type *parking_lot_types[] = ACO_TYPES(&parking_lot_type);
260
261 struct aco_file parking_lot_conf = {
262         .filename = "res_parking.conf",
263         .types = ACO_TYPES(&global_option, &parking_lot_type),
264 };
265
266 static AO2_GLOBAL_OBJ_STATIC(globals);
267
268 CONFIG_INFO_STANDARD(cfg_info, globals, parking_config_alloc,
269         .files = ACO_FILES(&parking_lot_conf),
270         .pre_apply_config = config_parking_preapply,
271         .post_apply_config = link_configured_disable_marked_lots,
272 );
273
274 static int parking_lot_cfg_hash_fn(const void *obj, const int flags)
275 {
276         const struct parking_lot_cfg *entry;
277         const char *key;
278
279         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
280         case OBJ_KEY:
281                 key = obj;
282                 return ast_str_hash(key);
283         case OBJ_PARTIAL_KEY:
284                 ast_assert(0);
285                 return 0;
286         default:
287                 entry = obj;
288                 return ast_str_hash(entry->name);
289         }
290 }
291
292 static int parking_lot_cfg_cmp_fn(void *obj, void *arg, const int flags)
293 {
294         struct parking_lot_cfg *entry1 = obj;
295
296         char *key;
297         size_t key_size;
298         struct parking_lot_cfg *entry2;
299
300         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
301         case OBJ_KEY:
302                 key = arg;
303                 return (!strcmp(entry1->name, key)) ? CMP_MATCH : 0;
304         case OBJ_PARTIAL_KEY:
305                 key = arg;
306                 key_size = strlen(key);
307                 return (!strncmp(entry1->name, key, key_size)) ? CMP_MATCH : 0;
308         case OBJ_POINTER:
309                 entry2 = arg;
310                 return (!strcmp(entry1->name, entry2->name)) ? CMP_MATCH : 0;
311         default:
312                 return CMP_STOP;
313         }
314 }
315
316 /*! \brief destructor for parking_config */
317 static void parking_config_destructor(void *obj)
318 {
319         struct parking_config *cfg = obj;
320         ao2_cleanup(cfg->parking_lots);
321         ao2_cleanup(cfg->global);
322 }
323
324 /*! \brief destructor for parking_global_config */
325 static void parking_global_config_destructor(void *obj)
326 {
327         /* For now, do nothing. */
328 }
329
330 /*! \brief allocator callback for parking_config. Notice it returns void * since it is only used by the backend config code */
331 static void *parking_config_alloc(void)
332 {
333         RAII_VAR(struct parking_config *, cfg, NULL, ao2_cleanup);
334
335         if (!(cfg = ao2_alloc(sizeof(*cfg), parking_config_destructor))) {
336                 return NULL;
337         }
338
339         if (!(cfg->parking_lots = ao2_container_alloc(37, parking_lot_cfg_hash_fn, parking_lot_cfg_cmp_fn))) {
340                 return NULL;
341         }
342
343         if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), parking_global_config_destructor))) {
344                 return NULL;
345         }
346
347         /* Bump the ref count since RAII_VAR is going to eat one */
348         ao2_ref(cfg, +1);
349         return cfg;
350 }
351
352 int parking_lot_remove_if_unused(struct parking_lot *lot)
353 {
354         if (lot->mode != PARKINGLOT_DISABLED) {
355                 return -1;
356         }
357
358         if (!ao2_container_count(lot->parked_users)) {
359                 ao2_unlink(parking_lot_container, lot);
360                 return 0;
361         }
362
363         return -1;
364 }
365
366 static void parking_lot_disable(struct parking_lot *lot)
367 {
368         /* If a dynamic lot wasn't removed, we need to restore it to full functionality afterwards. */
369         int was_dynamic = (lot->mode == PARKINGLOT_DYNAMIC);
370
371         lot->mode = PARKINGLOT_DISABLED;
372         if (parking_lot_remove_if_unused(lot) && was_dynamic) {
373                 lot->mode = PARKINGLOT_DYNAMIC;
374                 lot->disable_mark = 0;
375         }
376 }
377
378 /*! \brief Destroy a parking lot cfg object */
379 static void parking_lot_cfg_destructor(void *obj)
380 {
381         struct parking_lot_cfg *lot_cfg = obj;
382         parking_lot_cfg_remove_extensions(lot_cfg);
383         ast_string_field_free_memory(lot_cfg);
384 }
385
386 /* The arg just needs to have the parking space with it */
387 static int parked_user_cmp_fn(void *obj, void *arg, int flags)
388 {
389         int *search_space = arg;
390         struct parked_user *user = obj;
391         int object_space = user->parking_space;
392
393         if (*search_space == object_space) {
394                 return CMP_MATCH;
395         }
396         return 0;
397 }
398
399 static int parked_user_sort_fn(const void *obj_left, const void *obj_right, int flags)
400 {
401         const struct parked_user *left = obj_left;
402         const struct parked_user *right = obj_right;
403
404         return left->parking_space - right->parking_space;
405 }
406
407 /*!
408  * \brief create a parking lot structure
409  * \param cat name given to the parking lot
410  * \retval NULL failure
411  * \retval non-NULL successfully allocated parking lot
412  */
413 static void *parking_lot_cfg_alloc(const char *cat)
414 {
415         struct parking_lot_cfg *lot_cfg;
416
417         lot_cfg = ao2_alloc(sizeof(*lot_cfg), parking_lot_cfg_destructor);
418         if (!lot_cfg) {
419                 return NULL;
420         }
421
422         if (ast_string_field_init(lot_cfg, 32)) {
423                 ao2_cleanup(lot_cfg);
424                 return NULL;
425         }
426
427         ast_string_field_set(lot_cfg, name, cat);
428
429         return lot_cfg;
430 }
431
432 #if defined(TEST_FRAMEWORK)
433 struct parking_lot_cfg *parking_lot_cfg_create(const char *cat)
434 {
435         return parking_lot_cfg_alloc(cat);
436 }
437 #endif
438
439 /*!
440  * XXX This is actually incredibly generic and might be better placed in something like astobj2 if there isn't already an equivalent
441  * \brief find an item in a container by its name
442  *
443  * \param container ao2container where we want the item from
444  * \param key name of the item wanted to be found
445  *
446  * \retval pointer to the parking lot if available. NULL if not found.
447  */
448 static void *named_item_find(struct ao2_container *container, const char *name)
449 {
450         return ao2_find(container, name, OBJ_KEY);
451 }
452
453 /*!
454  * \brief Custom field handler for parking positions
455  */
456 static int option_handler_parkpos(const struct aco_option *opt, struct ast_variable *var, void *obj)
457 {
458         struct parking_lot_cfg *lot_cfg = obj;
459         int low;
460         int high;
461
462         if (sscanf(var->value, "%30d-%30d", &low, &high) != 2) {
463                 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers\n");
464         } else if (high < low || low <= 0 || high <= 0) {
465                 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a <= b\n");
466         } else {
467                 lot_cfg->parking_start = low;
468                 lot_cfg->parking_stop = high;
469                 return 0;
470         }
471         return -1;
472 }
473
474 /*!
475  * \brief Custom field handler for the findslot option
476  */
477 static int option_handler_findslot(const struct aco_option *opt, struct ast_variable *var, void *obj)
478 {
479         struct parking_lot_cfg *lot_cfg = obj;
480
481         if (!strcmp(var->value, "first")) {
482                 lot_cfg->parkfindnext = 0;
483         } else if (!strcmp(var->value, "next")) {
484                 lot_cfg->parkfindnext = 1;
485         } else {
486                 ast_log(LOG_WARNING, "value '%s' is not valid for findslot option.\n", var->value);
487                 return -1;
488         }
489
490         return 0;
491 }
492
493 /*!
494  * \brief Maps string values for option_handler_parkedfeature to their ENUM values
495  */
496 static int parking_feature_flag_cfg(int *param, const char *var)
497 {
498         if (ast_false(var)) {
499                 *param = 0;
500         } else if (!strcasecmp(var, "both")) {
501                 *param = AST_FEATURE_FLAG_BYBOTH;
502         } else if (!strcasecmp(var, "caller")) {
503                 *param = AST_FEATURE_FLAG_BYCALLER;
504         } else if (!strcasecmp(var, "callee")) {
505                 *param = AST_FEATURE_FLAG_BYCALLEE;
506         } else {
507                 return -1;
508         }
509
510         return 0;
511 }
512
513 /*!
514  * \brief Custom field handler for feature mapping on parked call pickup options
515  */
516 static int option_handler_parkedfeature(const struct aco_option *opt, struct ast_variable *var, void *obj)
517 {
518         struct parking_lot_cfg *cfg = obj;
519         enum parked_call_feature_options option = aco_option_get_flags(opt);
520         int *parameter = NULL;
521
522         switch (option) {
523         case OPT_PARKEDPLAY:
524                 parameter = &cfg->parkedplay;
525                 break;
526         case OPT_PARKEDTRANSFERS:
527                 parameter = &cfg->parkedcalltransfers;
528                 break;
529         case OPT_PARKEDREPARKING:
530                 parameter = &cfg->parkedcallreparking;
531                 break;
532         case OPT_PARKEDHANGUP:
533                 parameter = &cfg->parkedcallhangup;
534                 break;
535         case OPT_PARKEDRECORDING:
536                 parameter = &cfg->parkedcallrecording;
537                 break;
538         }
539
540         ast_assert(parameter != NULL);
541         if (!parameter || parking_feature_flag_cfg(parameter, var->value)) {
542                 return -1;
543         }
544
545         return 0;
546 }
547
548 struct ao2_container *get_parking_lot_container(void)
549 {
550         return parking_lot_container;
551 }
552
553 struct parking_lot *parking_lot_find_by_name(const char *lot_name)
554 {
555         struct parking_lot *lot = named_item_find(parking_lot_container, lot_name);
556         return lot;
557 }
558
559 const char *find_channel_parking_lot_name(struct ast_channel *chan)
560 {
561         const char *name;
562
563         /* The channel variable overrides everything */
564         name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
565         if (ast_strlen_zero(name) && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
566                 /* Use the channel's parking lot. */
567                 name = ast_channel_parkinglot(chan);
568         }
569
570         /* If the name couldn't be pulled from that either, use the default parking lot name. */
571         if (ast_strlen_zero(name)) {
572                 name = DEFAULT_PARKING_LOT;
573         }
574
575         return name;
576 }
577
578 static void parking_lot_destructor(void *obj)
579 {
580         struct parking_lot *lot = obj;
581
582         if (lot->parking_bridge) {
583                 ast_bridge_destroy(lot->parking_bridge, 0);
584         }
585         ao2_cleanup(lot->parked_users);
586         ao2_cleanup(lot->cfg);
587         ast_string_field_free_memory(lot);
588 }
589
590 static struct parking_lot *alloc_new_parking_lot(struct parking_lot_cfg *lot_cfg)
591 {
592         struct parking_lot *lot;
593         if (!(lot = ao2_alloc(sizeof(*lot), parking_lot_destructor))) {
594                 return NULL;
595         }
596
597         if (ast_string_field_init(lot, 32)) {
598                 return NULL;
599         }
600
601         /* Create parked user ordered list */
602         lot->parked_users = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
603                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
604                 parked_user_sort_fn,
605                 parked_user_cmp_fn);
606
607         if (!lot->parked_users) {
608                 ao2_cleanup(lot);
609                 return NULL;
610         }
611
612         ast_string_field_set(lot, name, lot_cfg->name);
613         return lot;
614 }
615
616 void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg)
617 {
618         if (!ast_strlen_zero(lot_cfg->registrar)) {
619                 /* Although the function is called ast_context_destroy, the use of this funtion is
620                  * intended only to remove extensions, hints, etc registered by the parking lot's registrar.
621                  * It won't actually destroy the context unless that context is empty afterwards and it is
622                  * unreferenced.
623                  */
624                 ast_context_destroy(NULL, lot_cfg->registrar);
625         }
626 }
627
628 static void remove_all_configured_parking_lot_extensions(void)
629 {
630         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
631         struct parking_lot_cfg *lot_cfg;
632         struct ao2_iterator iter;
633
634         if (!cfg) {
635                 return;
636         }
637
638         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
639                 parking_lot_cfg_remove_extensions(lot_cfg);
640         }
641
642         ast_context_destroy(NULL, BASE_REGISTRAR);
643
644         ao2_iterator_destroy(&iter);
645 }
646
647 /*!
648  * \internal
649  * \since 12
650  * \brief Create an extension using ast_add_extension2_nolock. This function automatically allocates a duplicate
651  *        of the data string so that whatever calls it doesn't have to deal with freeing it if the ast_add_extension2_nolock
652  *        fails.
653  *
654  * \param context a write locked ast_context. Make certain it is write locked prior to calling this function
655  * \param replace whether the extension should replace existing extensions
656  * \param extension name of the extension desired
657  * \param priority priority of the extension we are registering
658  * \param application name of the application being used for the extension
659  * \param data application arguments
660  * \param registrar name of the registrar you should use for the extension.
661  *        Make sure this string doesn't go anywhere while there are still extensions using it.
662  */
663 static int parking_add_extension(struct ast_context *context, int replace, const char *extension,
664         int priority, const char *application, const char *data, const char *registrar)
665 {
666         char *data_duplicate = ast_strdup(data);
667
668         if (!data_duplicate) {
669                 return -1;
670         }
671
672         if (ast_add_extension2_nolock(context, replace, extension, priority, NULL, NULL,
673                         application, data_duplicate, ast_free_ptr, registrar)) {
674                 ast_free(data_duplicate);
675                 return -1;
676         }
677
678         return 0;
679 }
680
681 static int extension_is_compatible(struct parking_lot_cfg *lot_cfg, const char *app_type, struct ast_exten *extension)
682 {
683         RAII_VAR(struct parking_lot_cfg *, owner, NULL, ao2_cleanup);
684         const char *extension_registrar = ast_get_extension_registrar(extension);
685         const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
686         const char *extension_name = ast_get_extension_name(extension);
687         const char *extension_application = ast_get_extension_app(extension);
688
689         ast_assert(extension_registrar && extension_context && extension_name && extension_application);
690
691         if (strcmp(extension_registrar, BASE_REGISTRAR)) {
692                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
693                         lot_cfg->name, extension_name, extension_context, extension_registrar);
694                 return 0;
695         }
696
697         if (strcmp(extension_application, app_type)) {
698                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
699                         "but a/an %s application is already registered to that extension by %s.\n",
700                         lot_cfg->name, extension_name, extension_context, app_type,
701                         extension_application, BASE_REGISTRAR);
702                 return 0;
703         }
704
705         ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
706                   lot_cfg->name, extension_name, extension_context, app_type);
707         return 1;
708 }
709
710 int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
711 {
712         int parkingspace;
713         struct ast_exten *existing_exten;
714         struct ast_context *lot_context;
715         struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
716         const char *parkext_registrar_pointer; /* Used for park extension */
717         const char *parkedcall_registrar_pointer; /* Used for parkedcall extensions/hints */
718
719         if (ast_strlen_zero(lot_cfg->parkext)) {
720                 return 0;
721         }
722
723         ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
724         parkedcall_registrar_pointer = lot_cfg->registrar;
725
726         if (lot_cfg->parkext_exclusive) {
727                 parkext_registrar_pointer = lot_cfg->registrar;
728         } else {
729                 parkext_registrar_pointer = BASE_REGISTRAR;
730         }
731
732         /* We need the contexts list locked to safely be able to both read and lock the specific context within */
733         if (ast_wrlock_contexts()) {
734                 ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
735                 return -1;
736         }
737
738         if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, parkext_registrar_pointer))) {
739                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
740                         lot_cfg->name, lot_cfg->parking_con);
741                 if (ast_unlock_contexts()) {
742                         ast_assert(0);
743                 }
744                 return -1;
745         }
746
747         /* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
748          * and we don't want something else to destroy them while we are looking at them.
749          */
750         if (ast_wrlock_context(lot_context)) {
751                 ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
752                 return -1;
753         }
754
755         if (ast_unlock_contexts()) {
756                 ast_assert(0);
757         }
758
759         /* Handle generation/confirmation for the Park extension */
760         if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
761                 if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
762                         ast_unlock_context(lot_context);
763                         return -1;
764                 }
765         } else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
766                    lot_cfg->parkext_exclusive ? lot_cfg->name : "", parkext_registrar_pointer)) {
767                 ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
768                         lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
769                 ast_unlock_context(lot_context);
770                 return -1;
771         }
772
773         /* Handle generation/confirmation for the ParkedCall extensions and hints */
774         for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
775                 char space[AST_MAX_EXTENSION];
776                 RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
777                 find_info.stacklen = 0; /* reset for pbx_find_exten */
778
779                 snprintf(space, sizeof(space), "%d", parkingspace);
780
781                 /* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
782                 if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
783                         ast_unlock_context(lot_context);
784                         return -1;
785                 }
786
787                 arguments_string = ast_str_create(32);
788                 if (!arguments_string) {
789                         ast_unlock_context(lot_context);
790                         return -1;
791                 }
792
793                 ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
794                 if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
795                     ast_str_buffer(arguments_string), parkedcall_registrar_pointer)) {
796                         ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
797                                 lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
798                         ast_unlock_context(lot_context);
799                         return -1;
800                 }
801
802                 find_info.stacklen = 0; /* reset for pbx_find_exten */
803
804                 if (lot_cfg->parkaddhints) {
805                         char hint_device[AST_MAX_EXTENSION];
806
807                         snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
808
809                         if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
810                                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
811                                 lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
812                                         ast_unlock_context(lot_context);
813                                         return -1;
814                         }
815
816                         if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", parkedcall_registrar_pointer)) {
817                                 ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
818                                         lot_cfg->name, space, lot_cfg->parking_con);
819                                 ast_unlock_context(lot_context);
820                                 return -1;
821                         }
822                 }
823         }
824
825         if (ast_unlock_context(lot_context)) {
826                 ast_assert(0);
827         }
828
829         return 0;
830 }
831
832 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
833 {
834         struct parking_lot *lot;
835         struct parking_lot_cfg *replaced_cfg = NULL;
836         int found = 0;
837
838         /* Start by trying to find it. If that works we can skip the rest. */
839         lot = named_item_find(parking_lot_container, lot_cfg->name);
840         if (!lot) {
841                 lot = alloc_new_parking_lot(lot_cfg);
842
843                 /* If we still don't have a lot, we failed to alloc one. */
844                 if (!lot) {
845                         return NULL;
846                 }
847         } else {
848                 found = 1;
849
850                 if (dynamic) {
851                         ast_log(LOG_ERROR, "Tried to create dynamic parking lot with name '%s' but a lot with that name already exists.\n", lot_cfg->name);
852                         ao2_cleanup(lot);
853                         return NULL;
854                 }
855         }
856
857         /* Set the configuration reference. Unref the one currently in the lot if it's there. */
858         if (lot->cfg) {
859                 replaced_cfg = lot->cfg;
860         }
861
862         ao2_ref(lot_cfg, +1);
863         lot->cfg = lot_cfg;
864
865         ao2_cleanup(replaced_cfg);
866
867         /* Set the operating mode to normal since the parking lot has a configuration. */
868         lot->disable_mark = 0;
869         lot->mode = dynamic ? PARKINGLOT_DYNAMIC : PARKINGLOT_NORMAL;
870
871         if (!found) {
872                 /* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
873                 ao2_link(parking_lot_container, lot);
874         };
875
876         return lot;
877 }
878
879 static void generate_or_link_lots_to_configs(void)
880 {
881         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
882         struct parking_lot_cfg *lot_cfg;
883         struct ao2_iterator iter;
884
885         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
886                 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
887                 lot = parking_lot_build_or_update(lot_cfg, 0);
888         }
889
890         ao2_iterator_destroy(&iter);
891 }
892
893 int parking_dynamic_lots_enabled(void)
894 {
895         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
896
897         if (!cfg) {
898                 return 0;
899         }
900
901         return cfg->global->parkeddynamic;
902 }
903
904 static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
905 {
906         struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
907
908         if (!cfg) {
909                 return NULL;
910         }
911
912         ast_string_fields_copy(cfg, source);
913
914         /* Needs to be reset after being copied */
915         ast_string_field_set(cfg, name, name);
916
917         /* Stuff that should be cloned that isn't hit by string field copy */
918         cfg->parking_start = source->parking_start;
919         cfg->parking_stop = source->parking_stop;
920         cfg->parkingtime = source->parkingtime;
921         cfg->comebackdialtime = source->comebackdialtime;
922         cfg->parkfindnext = source->parkfindnext;
923         cfg->parkext_exclusive = source->parkext_exclusive;
924         cfg->parkaddhints = source->parkaddhints;
925         cfg->comebacktoorigin = source->comebacktoorigin;
926         cfg->parkedplay = source->parkedplay;
927         cfg->parkedcalltransfers = source->parkedcalltransfers;
928         cfg->parkedcallreparking = source->parkedcallreparking;
929         cfg->parkedcallhangup = source->parkedcallhangup;
930         cfg->parkedcallrecording = source->parkedcallrecording;
931
932         return cfg;
933 }
934
935 static struct parking_lot *create_dynamic_lot_full(const char *name, struct ast_channel *chan, int forced)
936 {
937         RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
938         RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
939
940         struct parking_lot *lot;
941         const char *dyn_context;
942         const char *dyn_exten;
943         const char *dyn_range;
944         const char *template_name;
945         const char *chan_template_name;
946         int dyn_start;
947         int dyn_end;
948
949         if (!forced && !parking_dynamic_lots_enabled()) {
950                 return NULL;
951         }
952
953         ast_channel_lock(chan);
954         chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
955         dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
956         dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
957         dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
958         ast_channel_unlock(chan);
959
960         template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
961
962         template_lot = parking_lot_find_by_name(template_name);
963         if (!template_lot) {
964                 ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
965                         template_name);
966                 return NULL;
967         }
968
969         cfg = clone_parkinglot_cfg(template_lot->cfg, name);
970
971         if (!cfg) {
972                 ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
973                 return NULL;
974         }
975
976         if (!ast_strlen_zero(dyn_exten)) {
977                 ast_string_field_set(cfg, parkext, dyn_exten);
978         }
979
980         if (!ast_strlen_zero(dyn_context)) {
981                 ast_string_field_set(cfg, parking_con, dyn_context);
982         }
983
984         if (!ast_strlen_zero(dyn_range)) {
985                 if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
986                         ast_log(LOG_ERROR,
987                                 "Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
988                                 return NULL;
989                 }
990                 if (dyn_end < dyn_start || dyn_start < 0) {
991                         ast_log(LOG_ERROR,
992                                 "Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
993                                 return NULL;
994                 }
995
996                 cfg->parking_start = dyn_start;
997                 cfg->parking_stop = dyn_end;
998         }
999
1000         if (parking_lot_cfg_create_extensions(cfg)) {
1001                 ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
1002                 return NULL;
1003         }
1004
1005         ao2_lock(parking_lot_container);
1006
1007         if ((lot = parking_lot_find_by_name(name))) {
1008                 ao2_unlock(parking_lot_container);
1009                 ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
1010                 ao2_ref(lot, -1);
1011                 return NULL;
1012         }
1013
1014         lot = parking_lot_build_or_update(cfg, 1);
1015         ao2_unlock(parking_lot_container);
1016
1017         if (!lot) {
1018                 ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
1019         }
1020
1021         return lot;
1022 }
1023
1024 struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan){
1025         return create_dynamic_lot_full(name, chan, 0);
1026 }
1027
1028 #if defined(TEST_FRAMEWORK)
1029 struct parking_lot *parking_create_dynamic_lot_forced(const char *name, struct ast_channel *chan) {
1030         return create_dynamic_lot_full(name, chan, 1);
1031 }
1032 #endif
1033
1034 /* Preapply */
1035
1036 static int verify_default_parking_lot(void)
1037 {
1038         struct parking_config *cfg = aco_pending_config(&cfg_info);
1039         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1040
1041         if (!cfg) {
1042                 return 0;
1043         }
1044
1045         lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
1046         if (!lot_cfg) {
1047                 lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
1048                 if (!lot_cfg) {
1049                         return -1;
1050                 }
1051                 ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
1052                 aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
1053                 ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
1054                 ao2_link(cfg->parking_lots, lot_cfg);
1055         }
1056
1057         return 0;
1058 }
1059
1060 static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
1061 {
1062         struct parking_lot_cfg *lot_cfg;
1063         struct ao2_iterator iter;
1064
1065         for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1066                 parking_lot_cfg_remove_extensions(lot_cfg);
1067         }
1068
1069         ao2_iterator_destroy(&iter);
1070
1071         ast_context_destroy(NULL, BASE_REGISTRAR);
1072
1073 }
1074
1075 static int configure_parking_extensions(void)
1076 {
1077         struct parking_config *cfg = aco_pending_config(&cfg_info);
1078         struct ao2_iterator iter;
1079         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1080         int res = 0;
1081
1082         if (!cfg) {
1083                 return 0;
1084         }
1085
1086         /* Clear existing extensions */
1087         remove_all_configured_parking_lot_extensions();
1088
1089         /* Attempt to build new extensions for each lot */
1090         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1091                 if (parking_lot_cfg_create_extensions(lot_cfg)) {
1092                         ao2_cleanup(lot_cfg);
1093                         lot_cfg = NULL;
1094                         res = -1;
1095                         break;
1096                 }
1097         }
1098         ao2_iterator_destroy(&iter);
1099
1100         if (res) {
1101                 remove_pending_parking_lot_extensions(cfg);
1102                 ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
1103         }
1104
1105         return res;
1106 }
1107
1108 static void mark_lots_as_disabled(void)
1109 {
1110         struct ao2_iterator iter;
1111         struct parking_lot *lot;
1112
1113         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1114                 lot->disable_mark = 1;
1115         }
1116
1117         ao2_iterator_destroy(&iter);
1118 }
1119
1120 static int config_parking_preapply(void)
1121 {
1122         mark_lots_as_disabled();
1123
1124         if (verify_default_parking_lot()) {
1125                 return -1;
1126         }
1127
1128         if (configure_parking_extensions()) {
1129                 return -1;
1130         }
1131
1132         return 0;
1133 }
1134
1135 static void disable_marked_lots(void)
1136 {
1137         struct ao2_iterator iter;
1138         struct parking_lot *lot;
1139
1140         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1141                 if (lot->disable_mark) {
1142                         parking_lot_disable(lot);
1143                 }
1144         }
1145
1146         ao2_iterator_destroy(&iter);
1147 }
1148
1149 static void link_configured_disable_marked_lots(void)
1150 {
1151         generate_or_link_lots_to_configs();
1152         disable_marked_lots();
1153 }
1154
1155 const struct ast_module_info *parking_get_module_info(void)
1156 {
1157         return ast_module_info;
1158 }
1159
1160 static int unload_module(void)
1161 {
1162         unload_parking_bridge_features();
1163         remove_all_configured_parking_lot_extensions();
1164         unload_parking_applications();
1165         unload_parking_manager();
1166         unload_parking_ui();
1167         unload_parking_devstate();
1168         unload_parking_tests();
1169         ao2_cleanup(parking_lot_container);
1170         parking_lot_container = NULL;
1171         aco_info_destroy(&cfg_info);
1172
1173         return 0;
1174 }
1175
1176 static int load_module(void)
1177 {
1178         parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
1179                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
1180                 parking_lot_sort_fn,
1181                 NULL);
1182         if (!parking_lot_container) {
1183                 goto error;
1184         }
1185
1186         if (aco_info_init(&cfg_info)) {
1187                 goto error;
1188         }
1189
1190         /* Global options */
1191         aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
1192
1193         /* Register the per parking lot options. */
1194         aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
1195         aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
1196         aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
1197         aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
1198         aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
1199         aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
1200         aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
1201         aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
1202         aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
1203         aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
1204
1205         /* More complicated parking lot options that require special handling */
1206         aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
1207         aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
1208         aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
1209         aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
1210         aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
1211         aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
1212         aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
1213
1214         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1215                 goto error;
1216         }
1217
1218         if (load_parking_applications()) {
1219                 goto error;
1220         }
1221
1222         if (load_parking_ui()) {
1223                 goto error;
1224         }
1225
1226         if (load_parking_manager()) {
1227                 goto error;
1228         }
1229
1230         if (load_parking_bridge_features()) {
1231                 goto error;
1232         }
1233
1234         if (load_parking_devstate()) {
1235                 goto error;
1236         }
1237
1238         if (load_parking_tests()) {
1239                 goto error;
1240         }
1241
1242         return AST_MODULE_LOAD_SUCCESS;
1243
1244 error:
1245         unload_module();
1246         return AST_MODULE_LOAD_DECLINE;
1247 }
1248
1249 static int reload_module(void)
1250 {
1251         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1252                 return AST_MODULE_LOAD_DECLINE;
1253         }
1254
1255         return 0;
1256 }
1257
1258 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
1259         .load = load_module,
1260         .unload = unload_module,
1261         .reload = reload_module,
1262 );