72627f16461ffa06cb05f3eaa6b1f5cea297c0fc
[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                         </configObject>
37                         <configObject name="parking_lot">
38                                 <synopsis>Defined parking lots for res_parking to use to park calls on</synopsis>
39                                 <configOption name="context" default="parkedcalls">
40                                         <synopsis>The name of the context where calls are parked and picked up from.</synopsis>
41                                         <description><para>This option is only used if parkext is set.</para></description>
42                                 </configOption>
43                                 <configOption name="parkext">
44                                         <synopsis>Extension to park calls to this parking lot.</synopsis>
45                                         <description><para>If this option is used, this extension will automatically be created to place calls into
46                         parking lots. In addition, if parkext_exclusive is set for this parking lot, the name of the parking lot
47                         will be included in the application's arguments so that it only parks to this parking lot. The extension
48                         will be created in <literal>context</literal>. Using this option also creates extensions for retrieving
49                         parked calls from the parking spaces in the same context.</para></description>
50                                 </configOption>
51                                 <configOption name="parkext_exclusive" default="no">
52                                         <synopsis>If yes, the extension registered as parkext will park exclusively to this parking lot.</synopsis>
53                                 </configOption>
54                                 <configOption name="parkpos" default="701-750">
55                                         <synopsis>Numerical range of parking spaces which can be used to retrieve parked calls.</synopsis>
56                                         <description><para>If parkext is set, these extensions will automatically be mapped in <literal>context</literal>
57                                                 in order to pick up calls parked to these parking spaces.</para></description>
58                                 </configOption>
59                                 <configOption name="parkinghints" default="no">
60                                         <synopsis>If yes, this parking lot will add hints automatically for parking spaces.</synopsis>
61                                 </configOption>
62                                 <configOption name="parkingtime" default="45">
63                                         <synopsis>Amount of time a call will remain parked before giving up (in seconds).</synopsis>
64                                 </configOption>
65                                 <configOption name="parkedmusicclass">
66                                         <synopsis>Which music class to use for parked calls. They will use the default if unspecified.</synopsis>
67                                 </configOption>
68                                 <configOption name="comebacktoorigin" default="yes">
69                                         <synopsis>Determines what should be done with the parked channel if no one picks it up before it times out.</synopsis>
70                                         <description><para>Valid Options:</para>
71                                                 <enumlist>
72                                                         <enum name="yes">
73                                                                 <para>Automatically have the parked channel dial the device that parked the call with dial
74                                                                         timeout set by the <literal>parkingtime</literal> option. When the call times out an extension
75                                                                         to dial the PARKER will automatically be created in the <literal>park-dial</literal> context with
76                                                                         an extension of the flattened parker device name. If the call is not answered, the parked channel
77                                                                         that is timing out will continue in the dial plan at that point if there are more priorities in
78                                                                         the extension (which won't be the case unless the dialplan deliberately includes such priorities
79                                                                         in the <literal>park-dial</literal> context through pattern matching or deliberately written
80                                                                         flattened peer extensions).</para>
81                                                         </enum>
82                                                         <enum name="no">
83                                                                 <para>Place the call into the PBX at <literal>comebackcontext</literal> instead. The extension will
84                                                                         still be set as the flattened peer name. If an extension the flattened peer name isn't available
85                                                                         then it will fall back to the <literal>s</literal> extension. If that also is unavailable it will
86                                                                         attempt to fall back to <literal>s@default</literal>. The normal dial extension will still be
87                                                                         created in the <literal>park-dial</literal> context with the extension also being the flattened
88                                                                         peer name.</para>
89                                                         </enum>
90                                                 </enumlist>
91                                                 <note><para>Flattened Peer Names - Extensions can not include slash characters since those are used for pattern
92                                                         matching. When a peer name is flattened, slashes become underscores. For example if the parker of a call
93                                                         is called <literal>SIP/0004F2040001</literal> then flattened peer name and therefor the extensions created
94                                                         and used on timeouts will be <literal>SIP_0004F204001</literal>.</para></note>
95                                                 <note><para>When parking times out and the channel returns to the dial plan, the following variables are set:
96                                                 </para></note>
97                                                 <variablelist>
98                                                         <variable name="PARKINGSLOT">
99                                                                 <para>extension that the call was parked in prior to timing out.</para>
100                                                         </variable>
101                                                         <variable name="PARKEDLOT">
102                                                                 <para>name of the lot that the call was parked in prior to timing out.</para>
103                                                         </variable>
104                                                         <variable name="PARKER">
105                                                                 <para>The device that parked the call</para>
106                                                         </variable>
107                                                 </variablelist>
108                                         </description>
109                                 </configOption>
110                                 <configOption name="comebackdialtime" default="30">
111                                         <synopsis>Timeout for the Dial extension created to call back the parker when a parked call times out.</synopsis>
112                                 </configOption>
113                                 <configOption name="comebackcontext" default="parkedcallstimeout">
114                                         <synopsis>Context where parked calls will enter the PBX on timeout when comebacktoorigin=no</synopsis>
115                                         <description><para>The extension the call enters will prioritize the flattened peer name in this context.
116                                                 If the flattened peer name extension is unavailable, then the 's' extension in this context will be
117                                                 used. If that also is unavailable, the 's' extension in the 'default' context will be used.</para>
118                                         </description>
119                                 </configOption>
120                                 <configOption name="courtesytone">
121                                         <synopsis>If the name of a sound file is provided, use this as the courtesy tone</synopsis>
122                                         <description><para>By default, this tone is only played to the caller of a parked call. Who receives the tone
123                                                 can be changed using the <literal>parkedplay</literal> option.</para>
124                                         </description>
125                                 </configOption>
126                                 <configOption name="parkedplay" default="caller">
127                                         <synopsis>Who we should play the courtesytone to on the pickup of a parked call from this lot</synopsis>
128                                         <description>
129                                                 <enumlist>
130                                                         <enum name="no"><para>Apply to neither side.</para></enum>
131                                                         <enum name="caller"><para>Apply to only to the caller picking up the parked call.</para></enum>
132                                                         <enum name="callee"><para>Apply to only to the parked call being picked up.</para></enum>
133                                                         <enum name="both"><para>Apply to both the caller and the callee.</para></enum>
134                                                 </enumlist>
135                                                 <note><para>If courtesy tone is not specified then this option will be ignored.</para></note>
136                                         </description>
137                                 </configOption>
138                                 <configOption name="parkedcalltransfers" default="no">
139                                         <synopsis>Apply the DTMF transfer features to the caller and/or callee when parked calls are picked up.</synopsis>
140                                         <description>
141                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
142                                         </description>
143                                 </configOption>
144                                 <configOption name="parkedcallreparking" default="no">
145                                         <synopsis>Apply the DTMF parking feature to the caller and/or callee when parked calls are picked up.</synopsis>
146                                         <description>
147                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
148                                         </description>
149                                 </configOption>
150                                 <configOption name="parkedcallhangup" default="no">
151                                         <synopsis>Apply the DTMF Hangup feature to the caller and/or callee when parked calls are picked up.</synopsis>
152                                         <description>
153                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
154                                         </description>
155                                 </configOption>
156                                 <configOption name="parkedcallrecording" default="no">
157                                         <synopsis>Apply the DTMF recording features to the caller and/or callee when parked calls are picked up</synopsis>
158                                         <description>
159                                                 <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
160                                         </description>
161                                 </configOption>
162                                 <configOption name="findslot" default="first">
163                                         <synopsis>Rule to use when trying to figure out which parking space a call should be parked with.</synopsis>
164                                         <description>
165                                                 <enumlist>
166                                                         <enum name="first"><para>Always try to place in the lowest available space in the parking lot</para></enum>
167                                                         <enum name="next"><para>Track the last parking space used and always attempt to use the one immediately after.
168                                                         </para></enum>
169                                                 </enumlist>
170                                         </description>
171                                 </configOption>
172                                 <configOption name="courtesytone">
173                                         <synopsis>If set, the sound set will be played to whomever is set by parkedplay</synopsis>
174                                 </configOption>
175                         </configObject>
176                 </configFile>
177         </configInfo>
178  ***/
179
180 #include "asterisk.h"
181
182 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
183
184 #include "parking/res_parking.h"
185 #include "asterisk/config.h"
186 #include "asterisk/config_options.h"
187 #include "asterisk/event.h"
188 #include "asterisk/utils.h"
189 #include "asterisk/module.h"
190 #include "asterisk/cli.h"
191 #include "asterisk/astobj2.h"
192 #include "asterisk/features.h"
193 #include "asterisk/manager.h"
194
195 #define PARKED_CALL_APPLICATION "ParkedCall"
196 #define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
197
198 /* TODO Add unit tests for parking */
199
200 static int parking_lot_sort_fn(const void *obj_left, const void *obj_right, int flags)
201 {
202         const struct parking_lot *left = obj_left;
203         const struct parking_lot *right = obj_right;
204         const char *right_key = obj_right;
205         int cmp;
206
207         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
208         default:
209         case OBJ_POINTER:
210                 right_key = right->name;
211                 /* Fall through */
212         case OBJ_KEY:
213                 cmp = strcmp(left->name, right_key);
214                 break;
215         case OBJ_PARTIAL_KEY:
216                 cmp = strncmp(left->name, right_key, strlen(right_key));
217         }
218         return cmp;
219 }
220
221 /*! All parking lots that are currently alive in some fashion can be obtained from here */
222 static struct ao2_container *parking_lot_container;
223
224 static void *parking_config_alloc(void);
225
226 static void *parking_lot_cfg_alloc(const char *cat);
227 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? */
228
229 static int config_parking_preapply(void);
230 static void link_configured_disable_marked_lots(void);
231
232 struct parking_global_config {
233         /* TODO Implement dynamic parking lots. Entirely. */
234         int parkeddynamic;
235 };
236
237 struct parking_config {
238         struct parking_global_config *global;
239         struct ao2_container *parking_lots;
240 };
241
242 static struct aco_type global_option = {
243         .type = ACO_GLOBAL,
244         .name = "globals",
245         .item_offset = offsetof(struct parking_config, global),
246         .category_match = ACO_WHITELIST,
247         .category = "^general$",
248 };
249
250 struct aco_type *global_options[] = ACO_TYPES(&global_option);
251
252 static struct aco_type parking_lot_type = {
253         .type = ACO_ITEM,
254         .name = "parking_lot",
255         .category_match = ACO_BLACKLIST,
256         .category = "^(general)$",
257         .item_alloc = parking_lot_cfg_alloc,
258         .item_find = named_item_find,
259         .item_offset = offsetof(struct parking_config, parking_lots),
260 };
261
262 struct aco_type *parking_lot_types[] = ACO_TYPES(&parking_lot_type);
263
264 struct aco_file parking_lot_conf = {
265         .filename = "res_parking.conf",
266         .types = ACO_TYPES(&global_option, &parking_lot_type),
267 };
268
269 static AO2_GLOBAL_OBJ_STATIC(globals);
270
271 CONFIG_INFO_STANDARD(cfg_info, globals, parking_config_alloc,
272         .files = ACO_FILES(&parking_lot_conf),
273         .pre_apply_config = config_parking_preapply,
274         .post_apply_config = link_configured_disable_marked_lots,
275 );
276
277 static int parking_lot_cfg_hash_fn(const void *obj, const int flags)
278 {
279         const struct parking_lot_cfg *entry;
280         const char *key;
281
282         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
283         case OBJ_KEY:
284                 key = obj;
285                 return ast_str_hash(key);
286         case OBJ_PARTIAL_KEY:
287                 ast_assert(0);
288                 return 0;
289         default:
290                 entry = obj;
291                 return ast_str_hash(entry->name);
292         }
293 }
294
295 static int parking_lot_cfg_cmp_fn(void *obj, void *arg, const int flags)
296 {
297         struct parking_lot_cfg *entry1 = obj;
298
299         char *key;
300         size_t key_size;
301         struct parking_lot_cfg *entry2;
302
303         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
304         case OBJ_KEY:
305                 key = arg;
306                 return (!strcmp(entry1->name, key)) ? CMP_MATCH : 0;
307         case OBJ_PARTIAL_KEY:
308                 key = arg;
309                 key_size = strlen(key);
310                 return (!strncmp(entry1->name, key, key_size)) ? CMP_MATCH : 0;
311         case OBJ_POINTER:
312                 entry2 = arg;
313                 return (!strcmp(entry1->name, entry2->name)) ? CMP_MATCH : 0;
314         default:
315                 return CMP_STOP;
316         }
317 }
318
319 /*! \brief destructor for parking_config */
320 static void parking_config_destructor(void *obj)
321 {
322         struct parking_config *cfg = obj;
323         ao2_cleanup(cfg->parking_lots);
324         ao2_cleanup(cfg->global);
325 }
326
327 /*! \brief destructor for parking_global_config */
328 static void parking_global_config_destructor(void *obj)
329 {
330         /* For now, do nothing. */
331 }
332
333 /*! \brief allocator callback for parking_config. Notice it returns void * since it is only used by the backend config code */
334 static void *parking_config_alloc(void)
335 {
336         RAII_VAR(struct parking_config *, cfg, NULL, ao2_cleanup);
337
338         if (!(cfg = ao2_alloc(sizeof(*cfg), parking_config_destructor))) {
339                 return NULL;
340         }
341
342         if (!(cfg->parking_lots = ao2_container_alloc(37, parking_lot_cfg_hash_fn, parking_lot_cfg_cmp_fn))) {
343                 return NULL;
344         }
345
346         if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), parking_global_config_destructor))) {
347                 return NULL;
348         }
349
350         /* Bump the ref count since RAII_VAR is going to eat one */
351         ao2_ref(cfg, +1);
352         return cfg;
353 }
354
355 void parking_lot_remove_if_unused(struct parking_lot *lot)
356 {
357
358         if (lot->mode != PARKINGLOT_DISABLED) {
359                 return;
360         }
361
362
363         if (!ao2_container_count(lot->parked_users)) {
364                 ao2_unlink(parking_lot_container, lot);
365         }
366 }
367
368 static void parking_lot_disable(struct parking_lot *lot)
369 {
370         lot->mode = PARKINGLOT_DISABLED;
371         parking_lot_remove_if_unused(lot);
372 }
373
374 /*! \brief Destroy a parking lot cfg object */
375 static void parking_lot_cfg_destructor(void *obj)
376 {
377         struct parking_lot_cfg *lot_cfg = obj;
378
379         ast_string_field_free_memory(lot_cfg);
380 }
381
382 /* The arg just needs to have the parking space with it */
383 static int parked_user_cmp_fn(void *obj, void *arg, int flags)
384 {
385         int *search_space = arg;
386         struct parked_user *user = obj;
387         int object_space = user->parking_space;
388
389         if (*search_space == object_space) {
390                 return CMP_MATCH;
391         }
392         return 0;
393 }
394
395 static int parked_user_sort_fn(const void *obj_left, const void *obj_right, int flags)
396 {
397         const struct parked_user *left = obj_left;
398         const struct parked_user *right = obj_right;
399
400         return left->parking_space - right->parking_space;
401 }
402
403 /*!
404  * \brief create a parking lot structure
405  * \param cat name given to the parking lot
406  * \retval NULL failure
407  * \retval non-NULL successfully allocated parking lot
408  */
409 static void *parking_lot_cfg_alloc(const char *cat)
410 {
411         struct parking_lot_cfg *lot_cfg;
412
413         lot_cfg = ao2_alloc(sizeof(*lot_cfg), parking_lot_cfg_destructor);
414         if (!lot_cfg) {
415                 return NULL;
416         }
417
418         if (ast_string_field_init(lot_cfg, 32)) {
419                 ao2_cleanup(lot_cfg);
420                 return NULL;
421         }
422
423         ast_string_field_set(lot_cfg, name, cat);
424
425         return lot_cfg;
426 }
427
428 /*!
429  * XXX This is actually incredibly generic and might be better placed in something like astobj2 if there isn't already an equivalent
430  * \brief find an item in a container by its name
431  *
432  * \param container ao2container where we want the item from
433  * \param key name of the item wanted to be found
434  *
435  * \retval pointer to the parking lot if available. NULL if not found.
436  */
437 static void *named_item_find(struct ao2_container *container, const char *name)
438 {
439         return ao2_find(container, name, OBJ_KEY);
440 }
441
442 /*!
443  * \brief Custom field handler for parking positions
444  */
445 static int option_handler_parkpos(const struct aco_option *opt, struct ast_variable *var, void *obj)
446 {
447         struct parking_lot_cfg *lot_cfg = obj;
448         int low;
449         int high;
450
451         if (sscanf(var->value, "%30d-%30d", &low, &high) != 2) {
452                 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers\n");
453         } else if (high < low || low <= 0 || high <= 0) {
454                 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a <= b\n");
455         } else {
456                 lot_cfg->parking_start = low;
457                 lot_cfg->parking_stop = high;
458                 return 0;
459         }
460         return -1;
461 }
462
463 /*!
464  * \brief Custom field handler for the findslot option
465  */
466 static int option_handler_findslot(const struct aco_option *opt, struct ast_variable *var, void *obj)
467 {
468         struct parking_lot_cfg *lot_cfg = obj;
469
470         if (!strcmp(var->value, "first")) {
471                 lot_cfg->parkfindnext = 0;
472         } else if (!strcmp(var->value, "next")) {
473                 lot_cfg->parkfindnext = 1;
474         } else {
475                 ast_log(LOG_WARNING, "value '%s' is not valid for findslot option.\n", var->value);
476                 return -1;
477         }
478
479         return 0;
480 }
481
482 /*!
483  * \brief Maps string values for option_handler_parkedfeature to their ENUM values
484  */
485 static int parking_feature_flag_cfg(int *param, const char *var)
486 {
487         if (ast_false(var)) {
488                 *param = 0;
489         } else if (!strcasecmp(var, "both")) {
490                 *param = AST_FEATURE_FLAG_BYBOTH;
491         } else if (!strcasecmp(var, "caller")) {
492                 *param = AST_FEATURE_FLAG_BYCALLER;
493         } else if (!strcasecmp(var, "callee")) {
494                 *param = AST_FEATURE_FLAG_BYCALLEE;
495         } else {
496                 return -1;
497         }
498
499         return 0;
500 }
501
502 /*!
503  * \brief Custom field handler for feature mapping on parked call pickup options
504  */
505 static int option_handler_parkedfeature(const struct aco_option *opt, struct ast_variable *var, void *obj)
506 {
507         struct parking_lot_cfg *cfg = obj;
508         enum parked_call_feature_options option = aco_option_get_flags(opt);
509         int *parameter = NULL;
510
511         switch (option) {
512         case OPT_PARKEDPLAY:
513                 parameter = &cfg->parkedplay;
514                 break;
515         case OPT_PARKEDTRANSFERS:
516                 parameter = &cfg->parkedcalltransfers;
517                 break;
518         case OPT_PARKEDREPARKING:
519                 parameter = &cfg->parkedcallreparking;
520                 break;
521         case OPT_PARKEDHANGUP:
522                 parameter = &cfg->parkedcallhangup;
523                 break;
524         case OPT_PARKEDRECORDING:
525                 parameter = &cfg->parkedcallrecording;
526                 break;
527         }
528
529         if (!parameter) {
530                 ast_log(LOG_ERROR, "Unable to handle option '%s'\n", var->name);
531                 return -1;
532         }
533
534         if (parking_feature_flag_cfg(parameter, var->value)) {
535                 ast_log(LOG_ERROR, "'%s' is not a valid value for parking lot option '%s'\n", var->value, var->name);
536                 return -1;
537         }
538
539         return 0;
540 }
541
542 struct ao2_container *get_parking_lot_container(void)
543 {
544         return parking_lot_container;
545 }
546
547 struct parking_lot *parking_lot_find_by_name(const char *lot_name)
548 {
549         struct parking_lot *lot = named_item_find(parking_lot_container, lot_name);
550         return lot;
551 }
552
553 const char *find_channel_parking_lot_name(struct ast_channel *chan)
554 {
555         const char *name;
556
557         /* The channel variable overrides everything */
558         name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
559         if (ast_strlen_zero(name) && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
560                 /* Use the channel's parking lot. */
561                 name = ast_channel_parkinglot(chan);
562         }
563
564         /* If the name couldn't be pulled from that either, use the default parking lot name. */
565         if (ast_strlen_zero(name)) {
566                 name = DEFAULT_PARKING_LOT;
567         }
568
569         return name;
570 }
571
572 static void parking_lot_destructor(void *obj)
573 {
574         struct parking_lot *lot = obj;
575
576         if (lot->parking_bridge) {
577                 ast_bridge_destroy(lot->parking_bridge);
578         }
579         ao2_cleanup(lot->parked_users);
580         ao2_cleanup(lot->cfg);
581         ast_string_field_free_memory(lot);
582 }
583
584 static struct parking_lot *alloc_new_parking_lot(struct parking_lot_cfg *lot_cfg)
585 {
586         struct parking_lot *lot;
587         if (!(lot = ao2_alloc(sizeof(*lot), parking_lot_destructor))) {
588                 return NULL;
589         }
590
591         if (ast_string_field_init(lot, 32)) {
592                 return NULL;
593         }
594
595         /* Create parked user ordered list */
596         lot->parked_users = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
597                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
598                 parked_user_sort_fn,
599                 parked_user_cmp_fn);
600
601         if (!lot->parked_users) {
602                 ao2_cleanup(lot);
603                 return NULL;
604         }
605
606         ast_string_field_set(lot, name, lot_cfg->name);
607         return lot;
608 }
609
610 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg)
611 {
612         struct parking_lot *lot;
613         struct parking_lot_cfg *replaced_cfg = NULL;
614         int found = 0;
615
616         /* Start by trying to find it. If that works we can skip the rest. */
617         lot = named_item_find(parking_lot_container, lot_cfg->name);
618         if (!lot) {
619                 lot = alloc_new_parking_lot(lot_cfg);
620
621                 /* If we still don't have a lot, we failed to alloc one. */
622                 if (!lot) {
623                         return NULL;
624                 }
625         } else {
626                 found = 1;
627         }
628
629         /* Set the configuration reference. Unref the one currently in the lot if it's there. */
630         if (lot->cfg) {
631                 replaced_cfg = lot->cfg;
632         }
633
634         ao2_ref(lot_cfg, +1);
635         lot->cfg = lot_cfg;
636
637         ao2_cleanup(replaced_cfg);
638
639         /* Set the operating mode to normal since the parking lot has a configuration. */
640         lot->disable_mark = 0;
641         lot->mode = PARKINGLOT_NORMAL;
642
643         if (!found) {
644                 /* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
645                 ao2_link(parking_lot_container, lot);
646         };
647
648         return lot;
649 }
650
651 static void generate_or_link_lots_to_configs(void)
652 {
653         RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
654         struct parking_lot_cfg *lot_cfg;
655         struct ao2_iterator iter;
656
657         for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
658                 RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
659                 lot = parking_lot_build_or_update(lot_cfg);
660         }
661
662         ao2_iterator_destroy(&iter);
663 }
664
665 /* Preapply */
666
667 static int verify_default_parking_lot(void)
668 {
669         struct parking_config *cfg = aco_pending_config(&cfg_info);
670         RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
671
672         if (!cfg) {
673                 return 0;
674         }
675
676         lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
677         if (!lot_cfg) {
678                 lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
679                 if (!lot_cfg) {
680                         return -1;
681                 }
682                 ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
683                 aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
684                 ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
685                 ao2_link(cfg->parking_lots, lot_cfg);
686         }
687
688         return 0;
689 }
690
691 static void mark_lots_as_disabled(void)
692 {
693         struct ao2_iterator iter;
694         struct parking_lot *lot;
695
696         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
697                 /* We aren't concerned with dynamic lots */
698                 if (lot->mode == PARKINGLOT_DYNAMIC) {
699                         continue;
700                 }
701
702                 lot->disable_mark = 1;
703         }
704
705         ao2_iterator_destroy(&iter);
706 }
707
708 static int config_parking_preapply(void)
709 {
710         mark_lots_as_disabled();
711         return verify_default_parking_lot();
712 }
713
714 static void disable_marked_lots(void)
715 {
716         struct ao2_iterator iter;
717         struct parking_lot *lot;
718
719         for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
720                 if (lot->disable_mark) {
721                         parking_lot_disable(lot);
722                 }
723         }
724
725         ao2_iterator_destroy(&iter);
726 }
727
728 static void link_configured_disable_marked_lots(void)
729 {
730         generate_or_link_lots_to_configs();
731         disable_marked_lots();
732 }
733
734 static int load_module(void)
735 {
736         if (aco_info_init(&cfg_info)) {
737                 goto error;
738         }
739
740         parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
741                 AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
742                 parking_lot_sort_fn,
743                 NULL);
744
745         if (!parking_lot_container) {
746                 goto error;
747         }
748
749         /* Global options */
750
751         /* Register the per parking lot options. */
752         aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
753         aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
754         aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
755         aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
756         aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
757         aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
758         aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
759         aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
760         aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
761         aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
762
763         /* More complicated parking lot options that require special handling */
764         aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
765         aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
766         aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
767         aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
768         aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
769         aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
770         aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
771
772         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
773                 goto error;
774         }
775
776         if (ast_register_application_xml(PARK_APPLICATION, park_app_exec)) {
777                 goto error;
778         }
779
780         if (ast_register_application_xml(PARKED_CALL_APPLICATION, parked_call_app_exec)) {
781                 goto error;
782         }
783
784         if (ast_register_application_xml(PARK_AND_ANNOUNCE_APPLICATION, park_and_announce_app_exec)) {
785                 goto error;
786         }
787
788         if (load_parking_ui()) {
789                 goto error;
790         }
791
792         if (load_parking_manager()) {
793                 goto error;
794         }
795
796         if (load_parking_bridge_features()) {
797                 goto error;
798         }
799
800         /* TODO Dialplan generation for parking lots that set parkext */
801         /* TODO Generate hints for parking lots that set parkext and have hints enabled */
802
803         return AST_MODULE_LOAD_SUCCESS;
804
805 error:
806         aco_info_destroy(&cfg_info);
807         return AST_MODULE_LOAD_DECLINE;
808 }
809
810 static int reload_module(void)
811 {
812         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
813                 return AST_MODULE_LOAD_DECLINE;
814         }
815
816         return 0;
817 }
818
819 static int unload_module(void)
820 {
821         /* XXX Parking is currently unloadable due to the fact that it loads features which could cause
822          *     significant problems if they disappeared while a channel still had access to them.
823          */
824         return -1;
825 }
826
827 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
828         .load = load_module,
829         .unload = unload_module,
830         .reload = reload_module,
831 );