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