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