2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Mark Michelson <mmichelson@digium.com>
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.
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.
21 #include "asterisk/features_config.h"
22 #include "asterisk/config_options.h"
23 #include "asterisk/datastore.h"
24 #include "asterisk/channel.h"
25 #include "asterisk/pbx.h"
26 #include "asterisk/app.h"
27 #include "asterisk/cli.h"
30 <configInfo name="features" language="en_US">
31 <synopsis>Features Configuration</synopsis>
32 <configFile name="features.conf">
33 <configObject name="globals">
36 <configOption name="featuredigittimeout" default="1000">
37 <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
39 <configOption name="courtesytone">
40 <synopsis>Sound to play when automon or automixmon is activated</synopsis>
42 <configOption name="recordingfailsound">
43 <synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
45 <configOption name="transferdigittimeout" default="3">
46 <synopsis>Seconds allowed between digit presses when dialing a transfer destination</synopsis>
48 <configOption name="atxfernoanswertimeout" default="15">
49 <synopsis>Seconds to wait for attended transfer destination to answer</synopsis>
51 <configOption name="atxferdropcall" default="no">
52 <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
54 <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
55 re-call the transferrer if the call to the transfer target fails. If the call to the
56 transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
57 milliseconds and then attempt to dial the transfer target again. This process will
58 repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
59 the transferrer have occurred.</para>
60 <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
61 to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
62 hang up all channels involved in the transfer.</para>
65 <configOption name="atxferloopdelay" default="10">
66 <synopsis>Seconds to wait between attempts to re-dial transfer destination</synopsis>
67 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
69 <configOption name="atxfercallbackretries" default="2">
70 <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
71 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
73 <configOption name="xfersound" default="beep">
74 <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
76 <para>This sound will play to the transferrer and transfer target channels when
77 an attended transfer completes. This sound is also played to channels when performing
78 an AMI <literal>Bridge</literal> action.</para>
81 <configOption name="xferfailsound" default="beeperr">
82 <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
84 <configOption name="atxferabort" default="*1">
85 <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
87 <para>This option is only available to the transferrer during an attended
88 transfer operation. Aborting a transfer results in the transfer being cancelled and
89 the original parties in the call being re-bridged.</para>
92 <configOption name="atxfercomplete" default="*2">
93 <synopsis>Digits to dial to complete an attended transfer</synopsis>
95 <para>This option is only available to the transferrer during an attended
96 transfer operation. Completing the transfer with a DTMF sequence is functionally
97 equivalent to hanging up the transferrer channel during an attended transfer. The
98 result is that the transfer target and transferees are bridged.</para>
101 <configOption name="atxferthreeway" default="*3">
102 <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
104 <para>This option is only available to the transferrer during an attended
105 transfer operation. Pressing this DTMF sequence will result in the transferrer,
106 the transferees, and the transfer target all being in a single bridge together.</para>
109 <configOption name="atxferswap" default="*4">
110 <synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
112 <para>This option is only available to the transferrer during an attended
113 transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
114 which party he is bridged with. For instance, if the transferrer is currently bridged with
115 the transfer target, then pressing this DTMF sequence will cause the transferrer to be
116 bridged with the transferees.</para>
119 <configOption name="pickupexten" default="*8">
120 <synopsis>Digits used for picking up ringing calls</synopsis>
122 <para>In order for the pickup attempt to be successful, the party attempting to
123 pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
124 common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
125 have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
126 <replaceable>callgroup</replaceable>.</para>
129 <configOption name="pickupsound">
130 <synopsis>Sound to play to picker when a call is picked up</synopsis>
132 <configOption name="pickupfailsound">
133 <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
136 <configObject name="featuremap">
137 <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
138 <configOption name="atxfer">
139 <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
141 <para>The transferee parties will be placed on hold and the
142 transferrer may dial an extension to reach a transfer target. During an
143 attended transfer, the transferrer may consult with the transfer target
144 before completing the transfer. Once the transferrer has hung up or pressed
145 the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
146 and transfer target will be bridged.</para>
149 <configOption name="blindxfer" default="#">
150 <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
152 <para>The transferee parties will be placed on hold and the
153 transferrer may dial an extension to reach a transfer target. During a
154 blind transfer, as soon as the transfer target is dialed, the transferrer
158 <configOption name="disconnect" default="*">
159 <synopsis>DTMF sequence to disconnect the current call</synopsis>
161 <para>Entering this DTMF sequence will cause the bridge to end, no
162 matter the number of parties present</para>
165 <configOption name="parkcall">
166 <synopsis>DTMF sequence to park a call</synopsis>
168 <para>The parking lot used to park the call is determined by using either the
169 <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
170 the channel (provided by the channel driver) if the variable is not present. If
171 no configured value on the channel is present, then <literal>"default"</literal>
172 is used. The call is parked in the next available space in the parking lot.</para>
175 <configOption name="automon">
176 <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
178 <para>This will cause the channel that pressed the DTMF sequence
179 to be monitored by the <literal>Monitor</literal> application. The
180 format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
181 channel variable. If this variable is not specified, then <literal>wav</literal> is the
182 default. The filename is constructed in the following manner:</para>
184 <para> prefix-timestamp-filename</para>
186 <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
187 channel variable or <literal>auto</literal> if the variable is not set. The timestamp
188 is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
189 channel variable or the callerID of the channels if the variable is not set.</para>
192 <configOption name="automixmon">
193 <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
195 <para>Operation of the automixmon is similar to the <literal> automon </literal>
196 feature, with the following exceptions:
197 <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
198 <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
199 There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
201 <see-also><ref type="configOption">automon</ref></see-also>
204 <configObject name="applicationmap">
205 <synopsis>Section for defining custom feature invocations during a call</synopsis>
207 <para>The applicationmap is an area where new custom features can be created. Items
208 defined in the applicationmap are not automatically accessible to bridged parties. Access
209 to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
210 The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
211 either applicationmap item names or featuregroup names.</para>
213 <configOption name="^.*$" regex="true">
214 <synopsis>A custom feature to invoke during a bridged call</synopsis>
216 <para>Each item listed here is a comma-separated list of parameters that determine
217 how a feature may be invoked during a call</para>
218 <para> Example:</para>
219 <para> eggs = *5,self,Playback(hello-world),default</para>
220 <para>This would create a feature called <literal>eggs</literal> that could be invoked
221 during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
222 sequence would then trigger the <literal>Playback</literal> application to play the
223 <literal>hello-world</literal> file. The application invocation would happen on the
224 party that pressed the DTMF sequence since <literal>self</literal> is specified. The
225 other parties in the bridge would hear the <literal>default</literal> music on hold
226 class during the playback.</para>
227 <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
228 is also allowed. The following applicationmap lines are functionally identical:</para>
229 <para> eggs = *5,self,Playback(hello-world),default</para>
230 <para> eggs = *5,self,Playback,hello-world,default</para>
231 <para> eggs = *5,self,Playback,"hello-world",default</para>
234 <parameter name="dtmf" required="true">
235 <para>The DTMF sequence used to trigger the option</para>
237 <parameter name="activate_on" required="true">
238 <para>The party that the feature will be invoked on</para>
240 <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
241 <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
244 <parameter name="app" required="true">
245 <para>The dialplan application to run when the DTMF sequence is pressed</para>
246 <argument name="app_args" required="false">
247 <para>The arguments to the dialplan application to run</para>
250 <parameter name="moh_class" required="false">
251 <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
256 <configObject name="featuregroup">
257 <synopsis>Groupings of items from the applicationmap</synopsis>
259 <para>Feature groups allow for multiple applicationmap items to be
260 grouped together. Like with individual applicationmap items, feature groups
261 can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
262 In addition to creating groupings, the feature group section allows for the
263 DTMF sequence used to invoke an applicationmap item to be overridden with
264 a different sequence.</para>
266 <configOption name="^.*$" regex="true">
267 <synopsis>Applicationmap item to place in the feature group</synopsis>
269 <para>Each item here must be a name of an item in the applicationmap. The
270 argument may either be a new DTMF sequence to use for the item or it
271 may be left blank in order to use the DTMF sequence specified in the
272 applicationmap. For example:</para>
273 <para> eggs => *1</para>
274 <para> bacon =></para>
275 <para>would result in the applicationmap items <literal>eggs</literal> and
276 <literal>bacon</literal> being in the featuregroup. The former would have its
277 default DTMF trigger overridden with <literal>*1</literal> and the latter would
278 have the DTMF value specified in the applicationmap.</para>
284 <function name="FEATURE" language="en_US">
286 Get or set a feature option on a channel.
289 <parameter name="option_name" required="true">
290 <para>The allowed values are:</para>
292 <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
293 <enum name="featuredigittimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='featuredigittimeout']/synopsis/text())" /></para></enum>
294 <enum name="transferdigittimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferdigittimeout']/synopsis/text())" /></para></enum>
295 <enum name="atxfernoanswertimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfernoanswertimeout']/synopsis/text())" /></para></enum>
296 <enum name="atxferdropcall"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferdropcall']/synopsis/text())" /></para></enum>
297 <enum name="atxferloopdelay"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferloopdelay']/synopsis/text())" /></para></enum>
298 <enum name="atxfercallbackretries"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfercallbackretries']/synopsis/text())" /></para></enum>
299 <enum name="xfersound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='xfersound']/synopsis/text())" /></para></enum>
300 <enum name="xferfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='xferfailsound']/synopsis/text())" /></para></enum>
301 <enum name="atxferabort"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferabort']/synopsis/text())" /></para></enum>
302 <enum name="atxfercomplete"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfercomplete']/synopsis/text())" /></para></enum>
303 <enum name="atxferthreeway"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferthreeway']/synopsis/text())" /></para></enum>
304 <enum name="pickupexten"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupexten']/synopsis/text())" /></para></enum>
305 <enum name="pickupsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupsound']/synopsis/text())" /></para></enum>
306 <enum name="pickupfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupfailsound']/synopsis/text())" /></para></enum>
307 <enum name="courtesytone"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='courtesytone']/synopsis/text())" /></para></enum>
308 <enum name="recordingfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='recordingfailsound']/synopsis/text())" /></para></enum>
313 <para>When this function is used as a read, it will get the current
314 value of the specified feature option for this channel. It will be
315 the value of this option configured in features.conf if a channel specific
316 value has not been set. This function can also be used to set a channel
317 specific value for the supported feature options.</para>
320 <ref type="function">FEATUREMAP</ref>
323 <function name="FEATUREMAP" language="en_US">
325 Get or set a feature map to a given value on a specific channel.
328 <parameter name="feature_name" required="true">
329 <para>The allowed values are:</para>
331 <enum name="atxfer"><para>Attended Transfer</para></enum>
332 <enum name="blindxfer"><para>Blind Transfer</para></enum>
333 <enum name="automon"><para>Auto Monitor</para></enum>
334 <enum name="disconnect"><para>Call Disconnect</para></enum>
335 <enum name="parkcall"><para>Park Call</para></enum>
336 <enum name="automixmon"><para>Auto MixMonitor</para></enum>
341 <para>When this function is used as a read, it will get the current
342 digit sequence mapped to the specified feature for this channel. This
343 value will be the one configured in features.conf if a channel specific
344 value has not been set. This function can also be used to set a channel
345 specific value for a feature mapping.</para>
348 <ref type="function">FEATURE</ref>
352 /*! Default general options */
353 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
354 #define DEFAULT_COURTESY_TONE ""
355 #define DEFAULT_RECORDING_FAIL_SOUND ""
357 /*! Default xfer options */
358 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3
359 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15
360 #define DEFAULT_ATXFER_DROP_CALL 0
361 #define DEFAULT_ATXFER_LOOP_DELAY 10
362 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
363 #define DEFAULT_XFERSOUND "beep"
364 #define DEFAULT_XFERFAILSOUND "beeperr"
365 #define DEFAULT_ATXFER_ABORT "*1"
366 #define DEFAULT_ATXFER_COMPLETE "*2"
367 #define DEFAULT_ATXFER_THREEWAY "*3"
368 #define DEFAULT_ATXFER_SWAP "*4"
370 /*! Default pickup options */
371 #define DEFAULT_PICKUPEXTEN "*8"
372 #define DEFAULT_PICKUPSOUND ""
373 #define DEFAULT_PICKUPFAILSOUND ""
375 /*! Default featuremap options */
376 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
377 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
378 #define DEFAULT_FEATUREMAP_AUTOMON ""
379 #define DEFAULT_FEATUREMAP_ATXFER ""
380 #define DEFAULT_FEATUREMAP_PARKCALL ""
381 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
384 * \brief Configuration from the "general" section of features.conf
386 struct features_global_config {
387 struct ast_features_general_config *general;
388 struct ast_features_xfer_config *xfer;
389 struct ast_features_pickup_config *pickup;
392 static void ast_applicationmap_item_destructor(void *obj)
394 struct ast_applicationmap_item *item = obj;
396 ast_string_field_free_memory(item);
399 static int applicationmap_sort(const void *obj, const void *arg, int flags)
401 const struct ast_applicationmap_item *item1 = obj;
402 const struct ast_applicationmap_item *item2;
405 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
408 return strcasecmp(item1->name, key2);
409 case OBJ_PARTIAL_KEY:
411 return strncasecmp(item1->name, key2, strlen(key2));
415 return strcasecmp(item1->name, item2->name);
420 * \brief Entry in the container of featuregroups
422 struct featuregroup_item {
423 AST_DECLARE_STRING_FIELDS(
424 /*! The name of the applicationmap item that we are referring to */
425 AST_STRING_FIELD(appmap_item_name);
426 /*! Custom DTMF override to use instead of the default for the applicationmap item */
427 AST_STRING_FIELD(dtmf_override);
429 /*! The applicationmap item that is being referred to */
430 struct ast_applicationmap_item *appmap_item;
433 static void featuregroup_item_destructor(void *obj)
435 struct featuregroup_item *item = obj;
437 ast_string_field_free_memory(item);
438 ao2_cleanup(item->appmap_item);
441 static int group_item_sort(const void *obj, const void *arg, int flags)
443 const struct featuregroup_item *item1 = obj;
444 const struct featuregroup_item *item2;
447 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
450 return strcasecmp(item1->appmap_item_name, key2);
451 case OBJ_PARTIAL_KEY:
453 return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
456 return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
463 * \brief Featuregroup representation
465 struct featuregroup {
466 /*! The name of the featuregroup */
468 /*! A container of featuregroup_items */
469 struct ao2_container *items;
472 static int featuregroup_hash(const void *obj, int flags)
474 const struct featuregroup *group;
477 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
480 return ast_str_case_hash(key);
481 case OBJ_PARTIAL_KEY:
487 return ast_str_case_hash(group->name);
491 static int featuregroup_cmp(void *obj, void *arg, int flags)
493 struct featuregroup *group1 = obj;
494 struct featuregroup *group2;
497 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
500 return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
501 case OBJ_PARTIAL_KEY:
503 return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
506 return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
512 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
514 return ao2_find(group_container, category, OBJ_KEY);
517 static void featuregroup_destructor(void *obj)
519 struct featuregroup *group = obj;
521 ast_free((char *) group->name);
522 ao2_cleanup(group->items);
525 static void *featuregroup_alloc(const char *cat)
527 struct featuregroup *group;
529 group = ao2_alloc(sizeof(*group), featuregroup_destructor);
534 group->name = ast_strdup(cat);
540 group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
541 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
550 /* Used for deprecated parking configuration */
551 struct dummy_config {
555 struct features_config {
556 struct features_global_config *global;
557 struct ast_featuremap_config *featuremap;
558 struct dummy_config *parkinglots;
559 struct ao2_container *applicationmap;
560 struct ao2_container *featuregroups;
563 static struct aco_type global_option = {
566 .category_match = ACO_WHITELIST,
567 .category = "^general$",
568 .item_offset = offsetof(struct features_config, global),
571 static struct aco_type featuremap_option = {
573 .name = "featuremap",
574 .category_match = ACO_WHITELIST,
575 .category = "^featuremap$",
576 .item_offset = offsetof(struct features_config, featuremap),
579 static struct aco_type applicationmap_option = {
581 .name = "applicationmap",
582 .category_match = ACO_WHITELIST,
583 .category = "^applicationmap$",
584 .item_offset = offsetof(struct features_config, applicationmap),
587 static struct aco_type featuregroup_option = {
589 .name = "featuregroup",
590 .category_match = ACO_BLACKLIST,
591 .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
592 .item_offset = offsetof(struct features_config, featuregroups),
593 .item_alloc = featuregroup_alloc,
594 .item_find = featuregroup_find,
597 static struct aco_type parkinglot_option = {
599 .name = "parkinglot",
600 .category_match = ACO_WHITELIST,
601 .category = "^parkinglot_.*$",
602 .item_offset = offsetof(struct features_config, parkinglots),
606 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
607 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
608 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
609 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
610 static struct aco_type *parkinglot_options[] = ACO_TYPES(&parkinglot_option);
612 static struct aco_file features_conf = {
613 .filename = "features.conf",
614 .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option, &parkinglot_option),
617 AO2_GLOBAL_OBJ_STATIC(globals);
619 static void features_config_destructor(void *obj)
621 struct features_config *cfg = obj;
623 ao2_cleanup(cfg->global);
624 ao2_cleanup(cfg->featuremap);
625 ao2_cleanup(cfg->parkinglots);
626 ao2_cleanup(cfg->applicationmap);
627 ao2_cleanup(cfg->featuregroups);
630 static void featuremap_config_destructor(void *obj)
632 struct ast_featuremap_config *cfg = obj;
634 ast_string_field_free_memory(cfg);
637 static void global_config_destructor(void *obj)
639 struct features_global_config *cfg = obj;
641 ao2_cleanup(cfg->general);
642 ao2_cleanup(cfg->xfer);
643 ao2_cleanup(cfg->pickup);
646 static void general_destructor(void *obj)
648 struct ast_features_general_config *cfg = obj;
650 ast_string_field_free_memory(cfg);
653 static void xfer_destructor(void *obj)
655 struct ast_features_xfer_config *cfg = obj;
657 ast_string_field_free_memory(cfg);
660 static void pickup_destructor(void *obj)
662 struct ast_features_pickup_config *cfg = obj;
664 ast_string_field_free_memory(cfg);
667 static struct features_global_config *global_config_alloc(void)
669 RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
671 cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
676 cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
677 if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
681 cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
682 if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
686 cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
687 if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
695 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
697 return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
698 replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
699 applicationmap_sort, NULL);
704 * \brief Allocate the major configuration structure
706 * The parameter is used to determine if the applicationmap and featuregroup
707 * structures should be allocated. We only want to allocate these structures for
708 * the global features_config structure. For the datastores on channels, we don't
709 * need to allocate these structures because they are not used.
711 * \param allocate_applicationmap See previous explanation
712 * \retval NULL Failed to alloate configuration
713 * \retval non-NULL Allocated configuration
715 static struct features_config *__features_config_alloc(int allocate_applicationmap)
717 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
719 cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
724 cfg->global = global_config_alloc();
729 cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
730 if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
734 cfg->parkinglots = ao2_alloc(sizeof(*cfg->parkinglots), NULL);
735 if (!cfg->parkinglots) {
739 if (allocate_applicationmap) {
740 cfg->applicationmap = applicationmap_alloc(1);
741 if (!cfg->applicationmap) {
745 cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
747 if (!cfg->featuregroups) {
757 static void *features_config_alloc(void)
759 return __features_config_alloc(1);
762 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
764 ast_string_fields_copy(dest, src);
765 dest->featuredigittimeout = src->featuredigittimeout;
768 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
770 ast_string_fields_copy(dest, src);
771 dest->transferdigittimeout = src->transferdigittimeout;
772 dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
773 dest->atxferloopdelay = src->atxferloopdelay;
774 dest->atxfercallbackretries = src->atxfercallbackretries;
775 dest->atxferdropcall = src->atxferdropcall;
778 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
780 ast_string_fields_copy(dest, src);
783 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
785 general_copy(dest->general, src->general);
786 xfer_copy(dest->xfer, src->xfer);
787 pickup_copy(dest->pickup, src->pickup);
790 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
792 ast_string_fields_copy(dest, src);
795 static void features_copy(struct features_config *dest, const struct features_config *src)
797 global_copy(dest->global, src->global);
798 featuremap_copy(dest->featuremap, src->featuremap);
800 /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
801 * is produced on the fly when ast_get_chan_applicationmap() is called
805 static struct features_config *features_config_dup(const struct features_config *orig)
807 struct features_config *dup;
809 dup = __features_config_alloc(0);
814 features_copy(dup, orig);
819 static int general_set(struct ast_features_general_config *general, const char *name,
824 if (!strcasecmp(name, "featuredigittimeout")) {
825 res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
826 } else if (!strcasecmp(name, "courtesytone")) {
827 ast_string_field_set(general, courtesytone, value);
828 } else if (!strcasecmp(name, "recordingfailsound")) {
829 ast_string_field_set(general, recordingfailsound, value);
831 /* Unrecognized option */
838 static int general_get(struct ast_features_general_config *general, const char *field,
839 char *buf, size_t len)
843 if (!strcasecmp(field, "featuredigittimeout")) {
844 snprintf(buf, len, "%u", general->featuredigittimeout);
845 } else if (!strcasecmp(field, "courtesytone")) {
846 ast_copy_string(buf, general->courtesytone, len);
847 } else if (!strcasecmp(field, "recordingfailsound")) {
848 ast_copy_string(buf, general->recordingfailsound, len);
850 /* Unrecognized option */
857 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
862 if (!strcasecmp(name, "transferdigittimeout")) {
863 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
864 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
865 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
866 } else if (!strcasecmp(name, "atxferloopdelay")) {
867 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
868 } else if (!strcasecmp(name, "atxfercallbackretries")) {
869 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
870 } else if (!strcasecmp(name, "atxferdropcall")) {
871 xfer->atxferdropcall = ast_true(value);
872 } else if (!strcasecmp(name, "xfersound")) {
873 ast_string_field_set(xfer, xfersound, value);
874 } else if (!strcasecmp(name, "xferfailsound")) {
875 ast_string_field_set(xfer, xferfailsound, value);
876 } else if (!strcasecmp(name, "atxferabort")) {
877 ast_string_field_set(xfer, atxferabort, value);
878 } else if (!strcasecmp(name, "atxfercomplete")) {
879 ast_string_field_set(xfer, atxfercomplete, value);
880 } else if (!strcasecmp(name, "atxferthreeway")) {
881 ast_string_field_set(xfer, atxferthreeway, value);
882 } else if (!strcasecmp(name, "atxferswap")) {
883 ast_string_field_set(xfer, atxferswap, value);
885 /* Unrecognized option */
892 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
893 char *buf, size_t len)
897 if (!strcasecmp(field, "transferdigittimeout")) {
898 snprintf(buf, len, "%u", xfer->transferdigittimeout);
899 } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
900 snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
901 } else if (!strcasecmp(field, "atxferloopdelay")) {
902 snprintf(buf, len, "%u", xfer->atxferloopdelay);
903 } else if (!strcasecmp(field, "atxfercallbackretries")) {
904 snprintf(buf, len, "%u", xfer->atxfercallbackretries);
905 } else if (!strcasecmp(field, "atxferdropcall")) {
906 snprintf(buf, len, "%u", xfer->atxferdropcall);
907 } else if (!strcasecmp(field, "xfersound")) {
908 ast_copy_string(buf, xfer->xfersound, len);
909 } else if (!strcasecmp(field, "xferfailsound")) {
910 ast_copy_string(buf, xfer->xferfailsound, len);
911 } else if (!strcasecmp(field, "atxferabort")) {
912 ast_copy_string(buf, xfer->atxferabort, len);
913 } else if (!strcasecmp(field, "atxfercomplete")) {
914 ast_copy_string(buf, xfer->atxfercomplete, len);
915 } else if (!strcasecmp(field, "atxferthreeway")) {
916 ast_copy_string(buf, xfer->atxferthreeway, len);
917 } else if (!strcasecmp(field, "atxferswap")) {
918 ast_copy_string(buf, xfer->atxferswap, len);
920 /* Unrecognized option */
927 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
932 if (!strcasecmp(name, "pickupsound")) {
933 ast_string_field_set(pickup, pickupsound, value);
934 } else if (!strcasecmp(name, "pickupfailsound")) {
935 ast_string_field_set(pickup, pickupfailsound, value);
936 } else if (!strcasecmp(name, "pickupexten")) {
937 ast_string_field_set(pickup, pickupexten, value);
939 /* Unrecognized option */
946 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
947 char *buf, size_t len)
951 if (!strcasecmp(field, "pickupsound")) {
952 ast_copy_string(buf, pickup->pickupsound, len);
953 } else if (!strcasecmp(field, "pickupfailsound")) {
954 ast_copy_string(buf, pickup->pickupfailsound, len);
955 } else if (!strcasecmp(field, "pickupexten")) {
956 ast_copy_string(buf, pickup->pickupexten, len);
958 /* Unrecognized option */
965 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
970 if (!strcasecmp(name, "blindxfer")) {
971 ast_string_field_set(featuremap, blindxfer, value);
972 } else if (!strcasecmp(name, "disconnect")) {
973 ast_string_field_set(featuremap, disconnect, value);
974 } else if (!strcasecmp(name, "automon")) {
975 ast_string_field_set(featuremap, automon, value);
976 } else if (!strcasecmp(name, "atxfer")) {
977 ast_string_field_set(featuremap, atxfer, value);
978 } else if (!strcasecmp(name, "automixmon")) {
979 ast_string_field_set(featuremap, automixmon, value);
980 } else if (!strcasecmp(name, "parkcall")) {
981 ast_string_field_set(featuremap, parkcall, value);
983 /* Unrecognized option */
990 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
991 char *buf, size_t len)
995 if (!strcasecmp(field, "blindxfer")) {
996 ast_copy_string(buf, featuremap->blindxfer, len);
997 } else if (!strcasecmp(field, "disconnect")) {
998 ast_copy_string(buf, featuremap->disconnect, len);
999 } else if (!strcasecmp(field, "automon")) {
1000 ast_copy_string(buf, featuremap->automon, len);
1001 } else if (!strcasecmp(field, "atxfer")) {
1002 ast_copy_string(buf, featuremap->atxfer, len);
1003 } else if (!strcasecmp(field, "automixmon")) {
1004 ast_copy_string(buf, featuremap->automixmon, len);
1005 } else if (!strcasecmp(field, "parkcall")) {
1006 ast_copy_string(buf, featuremap->parkcall, len);
1008 /* Unrecognized option */
1015 static void feature_ds_destroy(void *data)
1017 struct features_config *cfg = data;
1021 static void *feature_ds_duplicate(void *data)
1023 struct features_config *old_cfg = data;
1025 return features_config_dup(old_cfg);
1028 static const struct ast_datastore_info feature_ds_info = {
1030 .destroy = feature_ds_destroy,
1031 .duplicate = feature_ds_duplicate,
1036 * \brief Find or create feature datastore on a channel
1038 * \pre chan is locked
1040 * \return the data on the FEATURE datastore, or NULL on error
1042 static struct features_config *get_feature_ds(struct ast_channel *chan)
1044 RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
1045 struct features_config *cfg;
1046 struct ast_datastore *ds;
1048 if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1054 orig = ao2_global_obj_ref(globals);
1059 cfg = features_config_dup(orig);
1064 if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1069 /* Give the datastore a reference to the config */
1073 ast_channel_datastore_add(chan, ds);
1078 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1080 struct ast_datastore *ds;
1082 if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1083 /* Hasn't been created yet. Trigger creation. */
1084 RAII_VAR(struct features_config *, cfg, get_feature_ds(chan), ao2_cleanup);
1085 ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1091 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
1093 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1096 cfg = get_feature_ds(chan);
1098 cfg = ao2_global_obj_ref(globals);
1105 ast_assert(cfg->global && cfg->global->general);
1107 ao2_ref(cfg->global->general, +1);
1108 return cfg->global->general;
1111 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
1113 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1116 cfg = get_feature_ds(chan);
1118 cfg = ao2_global_obj_ref(globals);
1125 ast_assert(cfg->global && cfg->global->xfer);
1127 ao2_ref(cfg->global->xfer, +1);
1128 return cfg->global->xfer;
1131 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
1133 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1136 cfg = get_feature_ds(chan);
1138 cfg = ao2_global_obj_ref(globals);
1145 ast_assert(cfg->global && cfg->global->pickup);
1147 ao2_ref(cfg->global->pickup, +1);
1148 return cfg->global->pickup;
1151 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
1153 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1156 cfg = get_feature_ds(chan);
1158 cfg = ao2_global_obj_ref(globals);
1165 ast_assert(cfg->featuremap != NULL);
1167 ao2_ref(cfg->featuremap, +1);
1168 return cfg->featuremap;
1171 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1173 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1176 cfg = get_feature_ds(chan);
1178 cfg = ao2_global_obj_ref(globals);
1185 return featuremap_get(cfg->featuremap, feature, buf, len);
1188 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1190 RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1191 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1193 if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1197 /* Dang, must be in the application map */
1198 applicationmap = ast_get_chan_applicationmap(chan);
1199 if (!applicationmap) {
1203 item = ao2_find(applicationmap, feature, OBJ_KEY);
1208 ast_copy_string(buf, item->dtmf, len);
1212 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
1213 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1214 unsigned int activate_on_self)
1216 struct ast_applicationmap_item *item;
1218 item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1220 if (!item || ast_string_field_init(item, 64)) {
1224 ast_string_field_set(item, name, name);
1225 ast_string_field_set(item, app, app);
1226 ast_string_field_set(item, app_data, app_data);
1227 ast_string_field_set(item, moh_class, moh_class);
1228 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1229 item->activate_on_self = activate_on_self;
1234 static int add_item(void *obj, void *arg, int flags)
1236 struct featuregroup_item *fg_item = obj;
1237 struct ao2_container *applicationmap = arg;
1238 RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1240 /* If there's no DTMF override, then we can just link
1241 * the applicationmap item directly. Otherwise, we need
1242 * to create a copy with the DTMF override in place and
1245 if (ast_strlen_zero(fg_item->dtmf_override)) {
1246 ao2_ref(fg_item->appmap_item, +1);
1247 appmap_item = fg_item->appmap_item;
1249 appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1250 fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1251 fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1252 fg_item->appmap_item->activate_on_self);
1259 ao2_link(applicationmap, appmap_item);
1263 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
1265 RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1266 struct ao2_container *applicationmap;
1275 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1278 ao2_ref(cfg->applicationmap, +1);
1279 return cfg->applicationmap;
1282 group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1283 if (ast_strlen_zero(group_names)) {
1287 applicationmap = applicationmap_alloc(0);
1288 if (!applicationmap) {
1292 while ((name = strsep(&group_names, "#"))) {
1293 RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1296 RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1299 ao2_link(applicationmap, item);
1301 ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1302 name, ast_channel_name(chan));
1305 ao2_callback(group->items, 0, add_item, applicationmap);
1309 if (ao2_container_count(applicationmap) == 0) {
1310 ao2_cleanup(applicationmap);
1314 return applicationmap;
1317 static int applicationmap_handler(const struct aco_option *opt,
1318 struct ast_variable *var, void *obj)
1320 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1321 struct ao2_container *applicationmap = obj;
1322 AST_DECLARE_APP_ARGS(args,
1324 AST_APP_ARG(activate_on);
1326 AST_APP_ARG(app_data);
1327 AST_APP_ARG(moh_class);
1329 char *parse = ast_strdupa(var->value);
1332 unsigned int activate_on_self;
1334 AST_STANDARD_APP_ARGS(args, parse);
1336 if (ast_strlen_zero(args.dtmf) ||
1337 ast_strlen_zero(args.activate_on) ||
1338 ast_strlen_zero(args.app)) {
1339 ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1343 /* features.conf used to have an "activated_by" portion
1344 * in addition to activate_on. Get rid of whatever may be
1347 slash = strchr(args.activate_on, '/');
1352 /* Some applications do not require arguments. */
1353 if (!args.app_data) {
1357 /* Two syntaxes allowed for applicationmap:
1358 * Old: foo = *1,self,NoOp,Boo!,default
1359 * New: foo = *1,self,NoOp(Boo!),default
1361 * We need to handle both
1363 paren = strchr(args.app, '(');
1368 args.moh_class = args.app_data;
1370 close_paren = strrchr(paren, ')');
1372 *close_paren = '\0';
1374 args.app_data = paren;
1376 /* Re-check that the application is not empty */
1377 if (ast_strlen_zero(args.app)) {
1378 ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1381 } else if (strchr(args.app_data, '"')) {
1382 args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1385 /* Allow caller and callee to be specified for backwards compatibility */
1386 if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1387 activate_on_self = 1;
1388 } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1389 activate_on_self = 0;
1391 ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1392 args.activate_on, var->name);
1396 ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1397 args.dtmf, args.app, args.app_data, args.moh_class);
1399 item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1400 args.moh_class, args.dtmf, activate_on_self);
1406 if (!ao2_link(applicationmap, item)) {
1413 static int featuregroup_handler(const struct aco_option *opt,
1414 struct ast_variable *var, void *obj)
1416 RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
1417 struct featuregroup *group = obj;
1419 item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
1420 if (!item || ast_string_field_init(item, 32)) {
1424 ast_string_field_set(item, appmap_item_name, var->name);
1425 ast_string_field_set(item, dtmf_override, var->value);
1427 if (!ao2_link(group->items, item)) {
1431 /* We wait to look up the application map item in the preapply callback */
1436 static int general_handler(const struct aco_option *opt,
1437 struct ast_variable *var, void *obj)
1439 struct features_global_config *global = obj;
1440 struct ast_features_general_config *general = global->general;
1442 return general_set(general, var->name, var->value);
1445 static int xfer_handler(const struct aco_option *opt,
1446 struct ast_variable *var, void *obj)
1448 struct features_global_config *global = obj;
1449 struct ast_features_xfer_config *xfer = global->xfer;
1451 return xfer_set(xfer, var->name, var->value);
1454 static int pickup_handler(const struct aco_option *opt,
1455 struct ast_variable *var, void *obj)
1457 struct features_global_config *global = obj;
1458 struct ast_features_pickup_config *pickup = global->pickup;
1460 return pickup_set(pickup, var->name, var->value);
1463 static int parking_warning = 0;
1464 static int unsupported_handler(const struct aco_option *opt,
1465 struct ast_variable *var, void *obj)
1467 if (!parking_warning) {
1468 ast_log(LOG_WARNING, "Parkinglots are no longer configurable in features.conf; "
1469 "parking is now handled by res_parking.conf\n");
1470 parking_warning = 1;
1472 ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1476 static int featuremap_handler(const struct aco_option *opt,
1477 struct ast_variable *var, void *obj)
1479 struct ast_featuremap_config *featuremap = obj;
1481 return featuremap_set(featuremap, var->name, var->value);
1484 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1486 struct ast_applicationmap_item *appmap_item;
1487 struct featuregroup_item *fg_item = obj;
1489 struct ao2_container *applicationmap = data;
1491 appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1497 fg_item->appmap_item = appmap_item;
1502 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1504 struct featuregroup *group = obj;
1507 ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1510 ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1514 return *err ? CMP_STOP : 0;
1517 static int features_pre_apply_config(void);
1519 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1520 .files = ACO_FILES(&features_conf),
1521 .pre_apply_config = features_pre_apply_config,
1524 static int features_pre_apply_config(void)
1526 struct features_config *cfg = aco_pending_config(&cfg_info);
1529 /* Now that the entire config has been processed, we can check that the featuregroup
1530 * items refer to actual applicationmap items.
1533 ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
1538 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1539 char *buf, size_t len)
1542 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1543 SCOPED_CHANNELLOCK(lock, chan);
1545 if (!strcasecmp(data, "inherit")) {
1546 struct ast_datastore *ds = get_feature_chan_ds(chan);
1547 unsigned int inherit = ds ? ds->inheritance : 0;
1549 snprintf(buf, len, "%s", inherit ? "yes" : "no");
1553 cfg = get_feature_ds(chan);
1558 res = general_get(cfg->global->general, data, buf, len) &&
1559 xfer_get(cfg->global->xfer, data, buf, len) &&
1560 pickup_get(cfg->global->pickup, data, buf, len);
1563 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1569 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1573 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1574 SCOPED_CHANNELLOCK(lock, chan);
1576 if (!strcasecmp(data, "inherit")) {
1577 struct ast_datastore *ds = get_feature_chan_ds(chan);
1579 ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1584 if (!(cfg = get_feature_ds(chan))) {
1588 res = general_set(cfg->global->general, data, value) &&
1589 xfer_set(cfg->global->xfer, data, value) &&
1590 pickup_set(cfg->global->pickup, data, value);
1593 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1599 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1600 char *buf, size_t len)
1603 SCOPED_CHANNELLOCK(lock, chan);
1605 res = ast_get_builtin_feature(chan, data, buf, len);
1608 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1614 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1618 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1619 SCOPED_CHANNELLOCK(lock, chan);
1621 if (!(cfg = get_feature_ds(chan))) {
1625 res = featuremap_set(cfg->featuremap, data, value);
1627 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1634 static struct ast_custom_function feature_function = {
1636 .read = feature_read,
1637 .write = feature_write
1640 static struct ast_custom_function featuremap_function = {
1641 .name = "FEATUREMAP",
1642 .read = featuremap_read,
1643 .write = featuremap_write
1646 static int load_config(void)
1648 if (aco_info_init(&cfg_info)) {
1649 ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1653 aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1654 __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
1655 aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1656 DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
1657 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1658 DEFAULT_COURTESY_TONE, general_handler, 0);
1660 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1661 __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
1662 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1663 __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
1664 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1665 __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
1666 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1667 __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
1668 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1669 __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
1670 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1671 DEFAULT_XFERSOUND, xfer_handler, 0);
1672 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1673 DEFAULT_XFERFAILSOUND, xfer_handler, 0);
1674 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1675 DEFAULT_ATXFER_ABORT, xfer_handler, 0);
1676 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1677 DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
1678 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1679 DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
1680 aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
1681 DEFAULT_ATXFER_SWAP, xfer_handler, 0);
1683 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1684 DEFAULT_PICKUPEXTEN, pickup_handler, 0);
1685 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1686 DEFAULT_PICKUPSOUND, pickup_handler, 0);
1687 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1688 DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
1690 aco_option_register_custom_nodoc(&cfg_info, "context", ACO_EXACT, global_options,
1691 "", unsupported_handler, 0);
1692 aco_option_register_custom_nodoc(&cfg_info, "parkext", ACO_EXACT, global_options,
1693 "", unsupported_handler, 0);
1694 aco_option_register_custom_nodoc(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1695 "", unsupported_handler, 0);
1696 aco_option_register_custom_nodoc(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1697 "", unsupported_handler, 0);
1698 aco_option_register_custom_nodoc(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1699 "", unsupported_handler, 0);
1700 aco_option_register_custom_nodoc(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1701 "", unsupported_handler, 0);
1702 aco_option_register_custom_nodoc(&cfg_info, "parkpos", ACO_EXACT, global_options,
1703 "", unsupported_handler, 0);
1704 aco_option_register_custom_nodoc(&cfg_info, "findslot", ACO_EXACT, global_options,
1705 "", unsupported_handler, 0);
1706 aco_option_register_custom_nodoc(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1707 "", unsupported_handler, 0);
1708 aco_option_register_custom_nodoc(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1709 "", unsupported_handler, 0);
1710 aco_option_register_custom_nodoc(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1711 "", unsupported_handler, 0);
1712 aco_option_register_custom_nodoc(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1713 "", unsupported_handler, 0);
1714 aco_option_register_custom_nodoc(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1715 "", unsupported_handler, 0);
1716 aco_option_register_custom_nodoc(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1717 "", unsupported_handler, 0);
1718 aco_option_register_custom_nodoc(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1719 "", unsupported_handler, 0);
1720 aco_option_register_custom_nodoc(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1721 "", unsupported_handler, 0);
1722 aco_option_register_custom_nodoc(&cfg_info, "adsipark", ACO_EXACT, global_options,
1723 "", unsupported_handler, 0);
1725 aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1726 DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
1727 aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1728 DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
1729 aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1730 DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
1731 aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1732 DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
1733 aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1734 DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
1735 aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1736 DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
1738 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
1739 "", applicationmap_handler, 0);
1741 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
1742 "", featuregroup_handler, 0);
1744 aco_option_register_custom_nodoc(&cfg_info, "^.*$", ACO_REGEX, parkinglot_options,
1745 "", unsupported_handler, 0);
1747 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1748 RAII_VAR(struct features_config *, features_cfg, __features_config_alloc(0), ao2_cleanup);
1750 if (aco_set_defaults(&global_option, "general", features_cfg->global) ||
1751 aco_set_defaults(&featuremap_option, "featuremap", features_cfg->featuremap)) {
1752 ast_log(LOG_ERROR, "Failed to load features.conf and failed to initialize defaults.\n");
1756 ast_log(LOG_NOTICE, "Could not load features config; using defaults\n");
1757 ao2_global_obj_replace_unref(globals, features_cfg);
1763 static int print_featuregroup(void *obj, void *arg, int flags)
1765 struct featuregroup_item *item = obj;
1766 struct ast_cli_args *a = arg;
1768 ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1769 S_OR(item->dtmf_override, item->appmap_item->dtmf));
1774 static int print_featuregroups(void *obj, void *arg, int flags)
1776 struct featuregroup *group = obj;
1777 struct ast_cli_args *a = arg;
1779 ast_cli(a->fd, "===> Group: %s\n", group->name);
1781 ao2_callback(group->items, 0, print_featuregroup, a);
1785 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1787 static int print_applicationmap(void *obj, void *arg, int flags)
1789 struct ast_applicationmap_item *item = obj;
1790 struct ast_cli_args *a = arg;
1792 ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1797 * \brief CLI command to list configured features
1802 * \retval CLI_SUCCESS on success.
1803 * \retval NULL when tab completion is used.
1805 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1807 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1812 e->command = "features show";
1814 "Usage: features show\n"
1815 " Lists configured features\n";
1821 cfg = ao2_global_obj_ref(globals);
1823 ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1824 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1826 ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1827 ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1828 ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1829 ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1830 ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1831 ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1832 ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1834 ast_cli(a->fd, "\n");
1835 ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1836 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1837 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1838 ast_cli(a->fd, "(none)\n");
1840 ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1843 ast_cli(a->fd, "\nFeature Groups:\n");
1844 ast_cli(a->fd, "---------------\n");
1845 if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1846 ast_cli(a->fd, "(none)\n");
1848 ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1851 ast_cli(a->fd, "\n");
1856 static struct ast_cli_entry cli_features_config[] = {
1857 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1860 void ast_features_config_shutdown(void)
1862 ast_custom_function_unregister(&featuremap_function);
1863 ast_custom_function_unregister(&feature_function);
1864 ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1865 aco_info_destroy(&cfg_info);
1866 ao2_global_obj_release(globals);
1869 int ast_features_config_reload(void)
1871 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1877 int ast_features_config_init(void)
1881 res = load_config();
1882 res |= __ast_custom_function_register(&feature_function, NULL);
1883 res |= __ast_custom_function_register(&featuremap_function, NULL);
1884 res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1887 ast_features_config_shutdown();