c36d558b6e443af46f06c3ddef56440778c32b47
[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         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
894                 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
895                 lot = parking_lot_build_or_update(lot_cfg, 0);
896         }
897
898         ao2_iterator_destroy(&iter);
899 }
900
901 int parking_dynamic_lots_enabled(void)
902 {
903         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
904
905         if (!cfg) {
906                 return 0;
907         }
908
909         return cfg->global->parkeddynamic;
910 }
911
912 static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
913 {
914         struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
915
916         if (!cfg) {
917                 return NULL;
918         }
919
920         ast_string_fields_copy(cfg, source);
921
922         /* Needs to be reset after being copied */
923         ast_string_field_set(cfg, name, name);
924
925         /* Stuff that should be cloned that isn't hit by string field copy */
926         cfg->parking_start = source->parking_start;
927         cfg->parking_stop = source->parking_stop;
928         cfg->parkingtime = source->parkingtime;
929         cfg->comebackdialtime = source->comebackdialtime;
930         cfg->parkfindnext = source->parkfindnext;
931         cfg->parkext_exclusive = source->parkext_exclusive;
932         cfg->parkaddhints = source->parkaddhints;
933         cfg->comebacktoorigin = source->comebacktoorigin;
934         cfg->parkedplay = source->parkedplay;
935         cfg->parkedcalltransfers = source->parkedcalltransfers;
936         cfg->parkedcallreparking = source->parkedcallreparking;
937         cfg->parkedcallhangup = source->parkedcallhangup;
938         cfg->parkedcallrecording = source->parkedcallrecording;
939
940         return cfg;
941 }
942
943 static struct parking_lot *create_dynamic_lot_full(const char *name, struct ast_channel *chan, int forced)
944 {
945         RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
946         RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
947
948         struct parking_lot *lot;
949         const char *dyn_context;
950         const char *dyn_exten;
951         const char *dyn_range;
952         const char *template_name;
953         const char *chan_template_name;
954         int dyn_start;
955         int dyn_end;
956
957         if (!forced && !parking_dynamic_lots_enabled()) {
958                 return NULL;
959         }
960
961         ast_channel_lock(chan);
962         chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
963         dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
964         dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
965         dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
966         ast_channel_unlock(chan);
967
968         template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
969
970         template_lot = parking_lot_find_by_name(template_name);
971         if (!template_lot) {
972                 ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
973                         template_name);
974                 return NULL;
975         }
976
977         cfg = clone_parkinglot_cfg(template_lot->cfg, name);
978
979         if (!cfg) {
980                 ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
981                 return NULL;
982         }
983
984         if (!ast_strlen_zero(dyn_exten)) {
985                 ast_string_field_set(cfg, parkext, dyn_exten);
986         }
987
988         if (!ast_strlen_zero(dyn_context)) {
989                 ast_string_field_set(cfg, parking_con, dyn_context);
990         }
991
992         if (!ast_strlen_zero(dyn_range)) {
993                 if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
994                         ast_log(LOG_ERROR,
995                                 "Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
996                                 return NULL;
997                 }
998                 if (dyn_end < dyn_start || dyn_start < 0) {
999                         ast_log(LOG_ERROR,
1000                                 "Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
1001                                 return NULL;
1002                 }
1003
1004                 cfg->parking_start = dyn_start;
1005                 cfg->parking_stop = dyn_end;
1006         }
1007
1008         if (parking_lot_cfg_create_extensions(cfg)) {
1009                 ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
1010                 return NULL;
1011         }
1012
1013         ao2_lock(parking_lot_container);
1014
1015         if ((lot = parking_lot_find_by_name(name))) {
1016                 ao2_unlock(parking_lot_container);
1017                 ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
1018                 ao2_ref(lot, -1);
1019                 return NULL;
1020         }
1021
1022         lot = parking_lot_build_or_update(cfg, 1);
1023         ao2_unlock(parking_lot_container);
1024
1025         if (!lot) {
1026                 ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
1027         }
1028
1029         return lot;
1030 }
1031
1032 struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan){
1033         return create_dynamic_lot_full(name, chan, 0);
1034 }
1035
1036 #if defined(TEST_FRAMEWORK)
1037 struct parking_lot *parking_create_dynamic_lot_forced(const char *name, struct ast_channel *chan) {
1038         return create_dynamic_lot_full(name, chan, 1);
1039 }
1040 #endif
1041
1042 /* Preapply */
1043
1044 static int verify_default_parking_lot(void)
1045 {
1046         struct parking_config *cfg = aco_pending_config(&cfg_info);
1047         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1048
1049         if (!cfg) {
1050                 return 0;
1051         }
1052
1053         lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
1054         if (!lot_cfg) {
1055                 lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
1056                 if (!lot_cfg) {
1057                         return -1;
1058                 }
1059                 ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
1060                 aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
1061                 ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
1062                 ao2_link(cfg->parking_lots, lot_cfg);
1063         }
1064
1065         return 0;
1066 }
1067
1068 static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
1069 {
1070         struct parking_lot_cfg *lot_cfg;
1071         struct ao2_iterator iter;
1072
1073         for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1074                 parking_lot_cfg_remove_extensions(lot_cfg);
1075         }
1076
1077         ao2_iterator_destroy(&iter);
1078
1079         ast_context_destroy(NULL, BASE_REGISTRAR);
1080
1081 }
1082
1083 static int configure_parking_extensions(void)
1084 {
1085         struct parking_config *cfg = aco_pending_config(&cfg_info);
1086         struct ao2_iterator iter;
1087         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1088         int res = 0;
1089
1090         if (!cfg) {
1091                 return 0;
1092         }
1093
1094         /* Clear existing extensions */
1095         remove_all_configured_parking_lot_extensions();
1096
1097         /* Attempt to build new extensions for each lot */
1098         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1099                 if (parking_lot_cfg_create_extensions(lot_cfg)) {
1100                         ao2_cleanup(lot_cfg);
1101                         lot_cfg = NULL;
1102                         res = -1;
1103                         break;
1104                 }
1105         }
1106         ao2_iterator_destroy(&iter);
1107
1108         if (res) {
1109                 remove_pending_parking_lot_extensions(cfg);
1110                 ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
1111         }
1112
1113         return res;
1114 }
1115
1116 static void mark_lots_as_disabled(void)
1117 {
1118         struct ao2_iterator iter;
1119         struct parking_lot *lot;
1120
1121         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1122                 lot->disable_mark = 1;
1123         }
1124
1125         ao2_iterator_destroy(&iter);
1126 }
1127
1128 static int config_parking_preapply(void)
1129 {
1130         mark_lots_as_disabled();
1131
1132         if (verify_default_parking_lot()) {
1133                 return -1;
1134         }
1135
1136         if (configure_parking_extensions()) {
1137                 return -1;
1138         }
1139
1140         return 0;
1141 }
1142
1143 static void disable_marked_lots(void)
1144 {
1145         struct ao2_iterator iter;
1146         struct parking_lot *lot;
1147
1148         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1149                 if (lot->disable_mark) {
1150                         parking_lot_disable(lot);
1151                 }
1152         }
1153
1154         ao2_iterator_destroy(&iter);
1155 }
1156
1157 static void link_configured_disable_marked_lots(void)
1158 {
1159         generate_or_link_lots_to_configs();
1160         disable_marked_lots();
1161 }
1162
1163 const struct ast_module_info *parking_get_module_info(void)
1164 {
1165         return ast_module_info;
1166 }
1167
1168 static int unload_module(void)
1169 {
1170         unload_parking_bridge_features();
1171         remove_all_configured_parking_lot_extensions();
1172         unload_parking_applications();
1173         unload_parking_manager();
1174         unload_parking_ui();
1175         unload_parking_devstate();
1176         unload_parking_tests();
1177         ao2_cleanup(parking_lot_container);
1178         parking_lot_container = NULL;
1179         aco_info_destroy(&cfg_info);
1180         ao2_global_obj_release(globals);
1181
1182         return 0;
1183 }
1184
1185 static int load_module(void)
1186 {
1187         parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
1188                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
1189                 parking_lot_sort_fn,
1190                 NULL);
1191         if (!parking_lot_container) {
1192                 goto error;
1193         }
1194
1195         if (aco_info_init(&cfg_info)) {
1196                 goto error;
1197         }
1198
1199         /* Global options */
1200         aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
1201
1202         /* Register the per parking lot options. */
1203         aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
1204         aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
1205         aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
1206         aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
1207         aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
1208         aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
1209         aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
1210         aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
1211         aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
1212         aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
1213
1214         /* More complicated parking lot options that require special handling */
1215         aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
1216         aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
1217         aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
1218         aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
1219         aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
1220         aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
1221         aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
1222
1223         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1224                 goto error;
1225         }
1226
1227         if (load_parking_applications()) {
1228                 goto error;
1229         }
1230
1231         if (load_parking_ui()) {
1232                 goto error;
1233         }
1234
1235         if (load_parking_manager()) {
1236                 goto error;
1237         }
1238
1239         if (load_parking_bridge_features()) {
1240                 goto error;
1241         }
1242
1243         if (load_parking_devstate()) {
1244                 goto error;
1245         }
1246
1247         if (load_parking_tests()) {
1248                 goto error;
1249         }
1250
1251         return AST_MODULE_LOAD_SUCCESS;
1252
1253 error:
1254         unload_module();
1255         return AST_MODULE_LOAD_DECLINE;
1256 }
1257
1258 static int reload_module(void)
1259 {
1260         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1261                 return AST_MODULE_LOAD_DECLINE;
1262         }
1263
1264         return 0;
1265 }
1266
1267 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
1268         .load = load_module,
1269         .unload = unload_module,
1270         .reload = reload_module,
1271 );