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