res_parking: Update XML documention for DTMF features after parking timeout.
[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         RAII_VAR(struct parking_lot_cfg *, owner, NULL, ao2_cleanup);
693         const char *extension_registrar = ast_get_extension_registrar(extension);
694         const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
695         const char *extension_name = ast_get_extension_name(extension);
696         const char *extension_application = ast_get_extension_app(extension);
697
698         ast_assert(extension_registrar && extension_context && extension_name && extension_application);
699
700         if (strcmp(extension_registrar, BASE_REGISTRAR)) {
701                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
702                         lot_cfg->name, extension_name, extension_context, extension_registrar);
703                 return 0;
704         }
705
706         if (strcmp(extension_application, app_type)) {
707                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
708                         "but a/an %s application is already registered to that extension by %s.\n",
709                         lot_cfg->name, extension_name, extension_context, app_type,
710                         extension_application, BASE_REGISTRAR);
711                 return 0;
712         }
713
714         ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
715                   lot_cfg->name, extension_name, extension_context, app_type);
716         return 1;
717 }
718
719 int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
720 {
721         int parkingspace;
722         struct ast_exten *existing_exten;
723         struct ast_context *lot_context;
724         struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
725         const char *parkext_registrar_pointer; /* Used for park extension */
726         const char *parkedcall_registrar_pointer; /* Used for parkedcall extensions/hints */
727
728         if (ast_strlen_zero(lot_cfg->parkext)) {
729                 return 0;
730         }
731
732         ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
733         parkedcall_registrar_pointer = lot_cfg->registrar;
734
735         if (lot_cfg->parkext_exclusive) {
736                 parkext_registrar_pointer = lot_cfg->registrar;
737         } else {
738                 parkext_registrar_pointer = BASE_REGISTRAR;
739         }
740
741         /* We need the contexts list locked to safely be able to both read and lock the specific context within */
742         if (ast_wrlock_contexts()) {
743                 ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
744                 return -1;
745         }
746
747         if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, parkext_registrar_pointer))) {
748                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
749                         lot_cfg->name, lot_cfg->parking_con);
750                 if (ast_unlock_contexts()) {
751                         ast_assert(0);
752                 }
753                 return -1;
754         }
755
756         /* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
757          * and we don't want something else to destroy them while we are looking at them.
758          */
759         if (ast_wrlock_context(lot_context)) {
760                 ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
761                 return -1;
762         }
763
764         if (ast_unlock_contexts()) {
765                 ast_assert(0);
766         }
767
768         /* Handle generation/confirmation for the Park extension */
769         if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
770                 if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
771                         ast_unlock_context(lot_context);
772                         return -1;
773                 }
774         } else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
775                    lot_cfg->parkext_exclusive ? lot_cfg->name : "", parkext_registrar_pointer)) {
776                 ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
777                         lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
778                 ast_unlock_context(lot_context);
779                 return -1;
780         }
781
782         /* Handle generation/confirmation for the ParkedCall extensions and hints */
783         for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
784                 char space[AST_MAX_EXTENSION];
785                 RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
786                 find_info.stacklen = 0; /* reset for pbx_find_exten */
787
788                 snprintf(space, sizeof(space), "%d", parkingspace);
789
790                 /* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
791                 if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
792                         ast_unlock_context(lot_context);
793                         return -1;
794                 }
795
796                 arguments_string = ast_str_create(32);
797                 if (!arguments_string) {
798                         ast_unlock_context(lot_context);
799                         return -1;
800                 }
801
802                 ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
803                 if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
804                     ast_str_buffer(arguments_string), parkedcall_registrar_pointer)) {
805                         ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
806                                 lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
807                         ast_unlock_context(lot_context);
808                         return -1;
809                 }
810
811                 find_info.stacklen = 0; /* reset for pbx_find_exten */
812
813                 if (lot_cfg->parkaddhints) {
814                         char hint_device[AST_MAX_EXTENSION];
815
816                         snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
817
818                         if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
819                                 ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
820                                 lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
821                                         ast_unlock_context(lot_context);
822                                         return -1;
823                         }
824
825                         if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", parkedcall_registrar_pointer)) {
826                                 ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
827                                         lot_cfg->name, space, lot_cfg->parking_con);
828                                 ast_unlock_context(lot_context);
829                                 return -1;
830                         }
831                 }
832         }
833
834         if (ast_unlock_context(lot_context)) {
835                 ast_assert(0);
836         }
837
838         return 0;
839 }
840
841 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
842 {
843         struct parking_lot *lot;
844         struct parking_lot_cfg *replaced_cfg = NULL;
845         int found = 0;
846
847         /* Start by trying to find it. If that works we can skip the rest. */
848         lot = named_item_find(parking_lot_container, lot_cfg->name);
849         if (!lot) {
850                 lot = alloc_new_parking_lot(lot_cfg);
851
852                 /* If we still don't have a lot, we failed to alloc one. */
853                 if (!lot) {
854                         return NULL;
855                 }
856         } else {
857                 found = 1;
858
859                 if (dynamic) {
860                         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);
861                         ao2_cleanup(lot);
862                         return NULL;
863                 }
864         }
865
866         /* Set the configuration reference. Unref the one currently in the lot if it's there. */
867         if (lot->cfg) {
868                 replaced_cfg = lot->cfg;
869         }
870
871         ao2_ref(lot_cfg, +1);
872         lot->cfg = lot_cfg;
873
874         ao2_cleanup(replaced_cfg);
875
876         /* Set the operating mode to normal since the parking lot has a configuration. */
877         lot->disable_mark = 0;
878         lot->mode = dynamic ? PARKINGLOT_DYNAMIC : PARKINGLOT_NORMAL;
879
880         if (!found) {
881                 /* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
882                 ao2_link(parking_lot_container, lot);
883         };
884
885         return lot;
886 }
887
888 static void generate_or_link_lots_to_configs(void)
889 {
890         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
891         struct parking_lot_cfg *lot_cfg;
892         struct ao2_iterator iter;
893
894         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
895                 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
896                 lot = parking_lot_build_or_update(lot_cfg, 0);
897         }
898
899         ao2_iterator_destroy(&iter);
900 }
901
902 int parking_dynamic_lots_enabled(void)
903 {
904         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
905
906         if (!cfg) {
907                 return 0;
908         }
909
910         return cfg->global->parkeddynamic;
911 }
912
913 static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
914 {
915         struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
916
917         if (!cfg) {
918                 return NULL;
919         }
920
921         ast_string_fields_copy(cfg, source);
922
923         /* Needs to be reset after being copied */
924         ast_string_field_set(cfg, name, name);
925
926         /* Stuff that should be cloned that isn't hit by string field copy */
927         cfg->parking_start = source->parking_start;
928         cfg->parking_stop = source->parking_stop;
929         cfg->parkingtime = source->parkingtime;
930         cfg->comebackdialtime = source->comebackdialtime;
931         cfg->parkfindnext = source->parkfindnext;
932         cfg->parkext_exclusive = source->parkext_exclusive;
933         cfg->parkaddhints = source->parkaddhints;
934         cfg->comebacktoorigin = source->comebacktoorigin;
935         cfg->parkedplay = source->parkedplay;
936         cfg->parkedcalltransfers = source->parkedcalltransfers;
937         cfg->parkedcallreparking = source->parkedcallreparking;
938         cfg->parkedcallhangup = source->parkedcallhangup;
939         cfg->parkedcallrecording = source->parkedcallrecording;
940
941         return cfg;
942 }
943
944 static struct parking_lot *create_dynamic_lot_full(const char *name, struct ast_channel *chan, int forced)
945 {
946         RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
947         RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
948
949         struct parking_lot *lot;
950         const char *dyn_context;
951         const char *dyn_exten;
952         const char *dyn_range;
953         const char *template_name;
954         const char *chan_template_name;
955         int dyn_start;
956         int dyn_end;
957
958         if (!forced && !parking_dynamic_lots_enabled()) {
959                 return NULL;
960         }
961
962         ast_channel_lock(chan);
963         chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
964         dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
965         dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
966         dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
967         ast_channel_unlock(chan);
968
969         template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
970
971         template_lot = parking_lot_find_by_name(template_name);
972         if (!template_lot) {
973                 ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
974                         template_name);
975                 return NULL;
976         }
977
978         cfg = clone_parkinglot_cfg(template_lot->cfg, name);
979
980         if (!cfg) {
981                 ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
982                 return NULL;
983         }
984
985         if (!ast_strlen_zero(dyn_exten)) {
986                 ast_string_field_set(cfg, parkext, dyn_exten);
987         }
988
989         if (!ast_strlen_zero(dyn_context)) {
990                 ast_string_field_set(cfg, parking_con, dyn_context);
991         }
992
993         if (!ast_strlen_zero(dyn_range)) {
994                 if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
995                         ast_log(LOG_ERROR,
996                                 "Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
997                                 return NULL;
998                 }
999                 if (dyn_end < dyn_start || dyn_start < 0) {
1000                         ast_log(LOG_ERROR,
1001                                 "Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
1002                                 return NULL;
1003                 }
1004
1005                 cfg->parking_start = dyn_start;
1006                 cfg->parking_stop = dyn_end;
1007         }
1008
1009         if (parking_lot_cfg_create_extensions(cfg)) {
1010                 ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
1011                 return NULL;
1012         }
1013
1014         ao2_lock(parking_lot_container);
1015
1016         if ((lot = parking_lot_find_by_name(name))) {
1017                 ao2_unlock(parking_lot_container);
1018                 ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
1019                 ao2_ref(lot, -1);
1020                 return NULL;
1021         }
1022
1023         lot = parking_lot_build_or_update(cfg, 1);
1024         ao2_unlock(parking_lot_container);
1025
1026         if (!lot) {
1027                 ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
1028         }
1029
1030         return lot;
1031 }
1032
1033 struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan){
1034         return create_dynamic_lot_full(name, chan, 0);
1035 }
1036
1037 #if defined(TEST_FRAMEWORK)
1038 struct parking_lot *parking_create_dynamic_lot_forced(const char *name, struct ast_channel *chan) {
1039         return create_dynamic_lot_full(name, chan, 1);
1040 }
1041 #endif
1042
1043 /* Preapply */
1044
1045 static int verify_default_parking_lot(void)
1046 {
1047         struct parking_config *cfg = aco_pending_config(&cfg_info);
1048         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1049
1050         if (!cfg) {
1051                 return 0;
1052         }
1053
1054         lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
1055         if (!lot_cfg) {
1056                 lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
1057                 if (!lot_cfg) {
1058                         return -1;
1059                 }
1060                 ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
1061                 aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
1062                 ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
1063                 ao2_link(cfg->parking_lots, lot_cfg);
1064         }
1065
1066         return 0;
1067 }
1068
1069 static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
1070 {
1071         struct parking_lot_cfg *lot_cfg;
1072         struct ao2_iterator iter;
1073
1074         for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1075                 parking_lot_cfg_remove_extensions(lot_cfg);
1076         }
1077
1078         ao2_iterator_destroy(&iter);
1079
1080         ast_context_destroy(NULL, BASE_REGISTRAR);
1081
1082 }
1083
1084 static int configure_parking_extensions(void)
1085 {
1086         struct parking_config *cfg = aco_pending_config(&cfg_info);
1087         struct ao2_iterator iter;
1088         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1089         int res = 0;
1090
1091         if (!cfg) {
1092                 return 0;
1093         }
1094
1095         /* Clear existing extensions */
1096         remove_all_configured_parking_lot_extensions();
1097
1098         /* Attempt to build new extensions for each lot */
1099         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1100                 if (parking_lot_cfg_create_extensions(lot_cfg)) {
1101                         ao2_cleanup(lot_cfg);
1102                         lot_cfg = NULL;
1103                         res = -1;
1104                         break;
1105                 }
1106         }
1107         ao2_iterator_destroy(&iter);
1108
1109         if (res) {
1110                 remove_pending_parking_lot_extensions(cfg);
1111                 ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
1112         }
1113
1114         return res;
1115 }
1116
1117 static void mark_lots_as_disabled(void)
1118 {
1119         struct ao2_iterator iter;
1120         struct parking_lot *lot;
1121
1122         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1123                 lot->disable_mark = 1;
1124         }
1125
1126         ao2_iterator_destroy(&iter);
1127 }
1128
1129 static int config_parking_preapply(void)
1130 {
1131         mark_lots_as_disabled();
1132
1133         if (verify_default_parking_lot()) {
1134                 return -1;
1135         }
1136
1137         if (configure_parking_extensions()) {
1138                 return -1;
1139         }
1140
1141         return 0;
1142 }
1143
1144 static void disable_marked_lots(void)
1145 {
1146         struct ao2_iterator iter;
1147         struct parking_lot *lot;
1148
1149         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1150                 if (lot->disable_mark) {
1151                         parking_lot_disable(lot);
1152                 }
1153         }
1154
1155         ao2_iterator_destroy(&iter);
1156 }
1157
1158 static void link_configured_disable_marked_lots(void)
1159 {
1160         generate_or_link_lots_to_configs();
1161         disable_marked_lots();
1162 }
1163
1164 const struct ast_module_info *parking_get_module_info(void)
1165 {
1166         return ast_module_info;
1167 }
1168
1169 static int unload_module(void)
1170 {
1171         unload_parking_bridge_features();
1172         remove_all_configured_parking_lot_extensions();
1173         unload_parking_applications();
1174         unload_parking_manager();
1175         unload_parking_ui();
1176         unload_parking_devstate();
1177         unload_parking_tests();
1178         ao2_cleanup(parking_lot_container);
1179         parking_lot_container = NULL;
1180         aco_info_destroy(&cfg_info);
1181         ao2_global_obj_release(globals);
1182
1183         return 0;
1184 }
1185
1186 static int load_module(void)
1187 {
1188         parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
1189                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
1190                 parking_lot_sort_fn,
1191                 NULL);
1192         if (!parking_lot_container) {
1193                 goto error;
1194         }
1195
1196         if (aco_info_init(&cfg_info)) {
1197                 goto error;
1198         }
1199
1200         /* Global options */
1201         aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
1202
1203         /* Register the per parking lot options. */
1204         aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
1205         aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
1206         aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
1207         aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
1208         aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
1209         aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
1210         aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
1211         aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
1212         aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
1213         aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
1214
1215         /* More complicated parking lot options that require special handling */
1216         aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
1217         aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
1218         aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
1219         aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
1220         aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
1221         aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
1222         aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
1223
1224         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1225                 goto error;
1226         }
1227
1228         if (load_parking_applications()) {
1229                 goto error;
1230         }
1231
1232         if (load_parking_ui()) {
1233                 goto error;
1234         }
1235
1236         if (load_parking_manager()) {
1237                 goto error;
1238         }
1239
1240         if (load_parking_bridge_features()) {
1241                 goto error;
1242         }
1243
1244         if (load_parking_devstate()) {
1245                 goto error;
1246         }
1247
1248         if (load_parking_tests()) {
1249                 goto error;
1250         }
1251
1252         return AST_MODULE_LOAD_SUCCESS;
1253
1254 error:
1255         unload_module();
1256         return AST_MODULE_LOAD_DECLINE;
1257 }
1258
1259 static int reload_module(void)
1260 {
1261         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1262                 return AST_MODULE_LOAD_DECLINE;
1263         }
1264
1265         return 0;
1266 }
1267
1268 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
1269         .load = load_module,
1270         .unload = unload_module,
1271         .reload = reload_module,
1272 );