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