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