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