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="transferdigittimeout" default="3000">
43 <synopsis>Milliseconds allowed between digit presses when dialing a transfer destination</synopsis>
45 <configOption name="atxfernoanswertimeout" default="15000">
46 <synopsis>Milliseconds to wait for attended transfer destination to answer</synopsis>
48 <configOption name="atxferdropcall" default="no">
49 <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
51 <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
52 re-call the transferrer if the call to the transfer target fails. If the call to the
53 transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
54 milliseconds and then attempt to dial the transfer target again. This process will
55 repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
56 the transferrer have occurred.</para>
57 <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
58 to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
59 hang up all channels involved in the transfer.</para>
62 <configOption name="atxferloopdelay" default="10000">
63 <synopsis>Milliseconds to wait between attempts to re-dial transfer destination</synopsis>
64 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
66 <configOption name="atxfercallbackretries" default="2">
67 <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
68 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
70 <configOption name="xfersound" default="beep">
71 <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
73 <para>This sound will play to the transferrer and transfer target channels when
74 an attended transfer completes. This sound is also played to channels when performing
75 an AMI <literal>Bridge</literal> action.</para>
78 <configOption name="xferfailsound" default="beeperr">
79 <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
81 <configOption name="atxferabort" default="*1">
82 <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
84 <para>This option is only available to the transferrer during an attended
85 transfer operation. Aborting a transfer results in the transfer being cancelled and
86 the original parties in the call being re-bridged.</para>
89 <configOption name="atxfercomplete" default="*2">
90 <synopsis>Digits to dial to complete an attended transfer</synopsis>
92 <para>This option is only available to the transferrer during an attended
93 transfer operation. Completing the transfer with a DTMF sequence is functionally
94 equivalent to hanging up the transferrer channel during an attended transfer. The
95 result is that the transfer target and transferees are bridged.</para>
98 <configOption name="atxferthreeway" default="*3">
99 <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
101 <para>This option is only available to the transferrer during an attended
102 transfer operation. Pressing this DTMF sequence will result in the transferrer,
103 the transferees, and the transfer target all being in a single bridge together.</para>
106 <configOption name="pickupexten" default="*8">
107 <synopsis>Digits used for picking up ringing calls</synopsis>
109 <para>In order for the pickup attempt to be successful, the party attempting to
110 pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
111 common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
112 have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
113 <replaceable>callgroup</replaceable>.</para>
116 <configOption name="pickupsound">
117 <synopsis>Sound to play to picker when a call is picked up</synopsis>
119 <configOption name="pickupfailsound">
120 <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
123 <configObject name="featuremap">
124 <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
125 <configOption name="atxfer">
126 <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
128 <para>The transferee parties will be placed on hold and the
129 transferrer may dial an extension to reach a transfer target. During an
130 attended transfer, the transferrer may consult with the transfer target
131 before completing the transfer. Once the transferrer has hung up or pressed
132 the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
133 and transfer target will be bridged.</para>
136 <configOption name="blindxfer" default="#">
137 <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
139 <para>The transferee parties will be placed on hold and the
140 transferrer may dial an extension to reach a transfer target. During a
141 blind transfer, as soon as the transfer target is dialed, the transferrer
145 <configOption name="disconnect" default="*">
146 <synopsis>DTMF sequence to disconnect the current call</synopsis>
148 <para>Entering this DTMF sequence will cause the bridge to end, no
149 matter the number of parties present</para>
152 <configOption name="parkcall">
153 <synopsis>DTMF sequence to park a call</synopsis>
155 <para>The parking lot used to park the call is determined by using either the
156 <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
157 the channel (provided by the channel driver) if the variable is not present. If
158 no configured value on the channel is present, then <literal>"default"</literal>
159 is used. The call is parked in the next available space in the parking lot.</para>
162 <configOption name="automon">
163 <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
165 <para>This will cause the channel that pressed the DTMF sequence
166 to be monitored by the <literal>Monitor</literal> application. The
167 format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
168 channel variable. If this variable is not specified, then <literal>wav</literal> is the
169 default. The filename is constructed in the following manner:</para>
171 <para> prefix-timestamp-filename</para>
173 <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
174 channel variable or <literal>auto</literal> if the variable is not set. The timestamp
175 is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
176 channel variable or the callerID of the channels if the variable is not set.</para>
179 <configOption name="automixmon">
180 <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
182 <para>Operation of the automixmon is similar to the <literal> automon </literal>
183 feature, with the following exceptions:
184 <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
185 <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
186 There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
188 <see-also><ref type="configOption">automon</ref></see-also>
191 <configObject name="applicationmap">
192 <synopsis>Section for defining custom feature invocations during a call</synopsis>
194 <para>The applicationmap is an area where new custom features can be created. Items
195 defined in the applicationmap are not automatically accessible to bridged parties. Access
196 to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
197 The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
198 either applicationmap item names or featuregroup names.</para>
200 <configOption name="^.*$" regex="true">
201 <synopsis>A custom feature to invoke during a bridged call</synopsis>
203 <para>Each item listed here is a comma-separated list of parameters that determine
204 how a feature may be invoked during a call</para>
205 <para> Example:</para>
206 <para> eggs = *5,self,Playback(hello-world),default</para>
207 <para>This would create a feature called <literal>eggs</literal> that could be invoked
208 during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
209 sequence would then trigger the <literal>Playback</literal> application to play the
210 <literal>hello-world</literal> file. The application invocation would happen on the
211 party that pressed the DTMF sequence since <literal>self</literal> is specified. The
212 other parties in the bridge would hear the <literal>default</literal> music on hold
213 class during the playback.</para>
214 <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
215 is also allowed. The following applicationmap lines are functionally identical:</para>
216 <para> eggs = *5,self,Playback(hello-world),default</para>
217 <para> eggs = *5,self,Playback,hello-world,default</para>
218 <para> eggs = *5,self,Playback,"hello-world",default</para>
221 <parameter name="dtmf" required="true">
222 <para>The DTMF sequence used to trigger the option</para>
224 <parameter name="activate_on" required="true">
225 <para>The party that the feature will be invoked on</para>
227 <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
228 <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
231 <parameter name="app" required="true">
232 <para>The dialplan application to run when the DTMF sequence is pressed</para>
233 <argument name="app_args" required="false">
234 <para>The arguments to the dialplan application to run</para>
237 <parameter name="moh_class" required="false">
238 <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
243 <configObject name="featuregroup">
244 <synopsis>Groupings of items from the applicationmap</synopsis>
246 <para>Feature groups allow for multiple applicationmap items to be
247 grouped together. Like with individual applicationmap items, feature groups
248 can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
249 In addition to creating groupings, the feature group section allows for the
250 DTMF sequence used to invoke an applicationmap item to be overridden with
251 a different sequence.</para>
253 <configOption name="^.*$" regex="true">
254 <synopsis>Applicationmap item to place in the feature group</synopsis>
256 <para>Each item here must be a name of an item in the applicationmap. The
257 argument may either be a new DTMF sequence to use for the item or it
258 may be left blank in order to use the DTMF sequence specified in the
259 applicationmap. For example:</para>
260 <para> eggs => *1</para>
261 <para> bacon =></para>
262 <para>would result in the applicationmap items <literal>eggs</literal> and
263 <literal>bacon</literal> being in the featuregroup. The former would have its
264 default DTMF trigger overridden with <literal>*1</literal> and the latter would
265 have the DTMF value specified in the applicationmap.</para>
271 <function name="FEATURE" language="en_US">
273 Get or set a feature option on a channel.
276 <parameter name="option_name" required="true">
277 <para>The allowed values are:</para>
279 <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
280 <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>
281 <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>
282 <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>
283 <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>
284 <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>
285 <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>
286 <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>
287 <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>
288 <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>
289 <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>
290 <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>
291 <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>
292 <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>
293 <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>
294 <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>
299 <para>When this function is used as a read, it will get the current
300 value of the specified feature option for this channel. It will be
301 the value of this option configured in features.conf if a channel specific
302 value has not been set. This function can also be used to set a channel
303 specific value for the supported feature options.</para>
306 <ref type="function">FEATUREMAP</ref>
309 <function name="FEATUREMAP" language="en_US">
311 Get or set a feature map to a given value on a specific channel.
314 <parameter name="feature_name" required="true">
315 <para>The allowed values are:</para>
317 <enum name="atxfer"><para>Attended Transfer</para></enum>
318 <enum name="blindxfer"><para>Blind Transfer</para></enum>
319 <enum name="automon"><para>Auto Monitor</para></enum>
320 <enum name="disconnect"><para>Call Disconnect</para></enum>
321 <enum name="parkcall"><para>Park Call</para></enum>
322 <enum name="automixmon"><para>Auto MixMonitor</para></enum>
327 <para>When this function is used as a read, it will get the current
328 digit sequence mapped to the specified feature for this channel. This
329 value will be the one configured in features.conf if a channel specific
330 value has not been set. This function can also be used to set a channel
331 specific value for a feature mapping.</para>
334 <ref type="function">FEATURE</ref>
338 /*! Default general options */
339 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
340 #define DEFAULT_COURTESY_TONE ""
342 /*! Default xfer options */
343 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
344 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
345 #define DEFAULT_ATXFER_DROP_CALL 0
346 #define DEFAULT_ATXFER_LOOP_DELAY 10000
347 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
348 #define DEFAULT_XFERSOUND "beep"
349 #define DEFAULT_XFERFAILSOUND "beeperr"
350 #define DEFAULT_ATXFER_ABORT "*1"
351 #define DEFAULT_ATXFER_COMPLETE "*2"
352 #define DEFAULT_ATXFER_THREEWAY "*3"
354 /*! Default pickup options */
355 #define DEFAULT_PICKUPEXTEN "*8"
356 #define DEFAULT_PICKUPSOUND ""
357 #define DEFAULT_PICKUPFAILSOUND ""
359 /*! Default featuremap options */
360 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
361 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
362 #define DEFAULT_FEATUREMAP_AUTOMON ""
363 #define DEFAULT_FEATUREMAP_ATXFER ""
364 #define DEFAULT_FEATUREMAP_PARKCALL ""
365 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
368 * \brief Configuration from the "general" section of features.conf
370 struct features_global_config {
371 struct ast_features_general_config *general;
372 struct ast_features_xfer_config *xfer;
373 struct ast_features_pickup_config *pickup;
376 static void ast_applicationmap_item_destructor(void *obj)
378 struct ast_applicationmap_item *item = obj;
380 ast_string_field_free_memory(item);
383 static int applicationmap_sort(const void *obj, const void *arg, int flags)
385 const struct ast_applicationmap_item *item1 = obj;
386 const struct ast_applicationmap_item *item2;
389 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
392 return strcasecmp(item1->name, key2);
393 case OBJ_PARTIAL_KEY:
395 return strncasecmp(item1->name, key2, strlen(key2));
399 return strcasecmp(item1->name, item2->name);
404 * \brief Entry in the container of featuregroups
406 struct featuregroup_item {
407 AST_DECLARE_STRING_FIELDS(
408 /*! The name of the applicationmap item that we are referring to */
409 AST_STRING_FIELD(appmap_item_name);
410 /*! Custom DTMF override to use instead of the default for the applicationmap item */
411 AST_STRING_FIELD(dtmf_override);
413 /*! The applicationmap item that is being referred to */
414 struct ast_applicationmap_item *appmap_item;
417 static void featuregroup_item_destructor(void *obj)
419 struct featuregroup_item *item = obj;
421 ast_string_field_free_memory(item);
422 ao2_cleanup(item->appmap_item);
425 static int group_item_sort(const void *obj, const void *arg, int flags)
427 const struct featuregroup_item *item1 = obj;
428 const struct featuregroup_item *item2;
431 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
434 return strcasecmp(item1->appmap_item_name, key2);
435 case OBJ_PARTIAL_KEY:
437 return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
440 return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
447 * \brief Featuregroup representation
449 struct featuregroup {
450 /*! The name of the featuregroup */
452 /*! A container of featuregroup_items */
453 struct ao2_container *items;
456 static int featuregroup_hash(const void *obj, int flags)
458 const struct featuregroup *group;
461 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
464 return ast_str_case_hash(key);
465 case OBJ_PARTIAL_KEY:
471 return ast_str_case_hash(group->name);
475 static int featuregroup_cmp(void *obj, void *arg, int flags)
477 struct featuregroup *group1 = obj;
478 struct featuregroup *group2;
481 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
484 return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
485 case OBJ_PARTIAL_KEY:
487 return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
490 return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
496 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
498 return ao2_find(group_container, category, OBJ_KEY);
501 static void featuregroup_destructor(void *obj)
503 struct featuregroup *group = obj;
505 ast_free((char *) group->name);
506 ao2_cleanup(group->items);
509 static void *featuregroup_alloc(const char *cat)
511 struct featuregroup *group;
513 group = ao2_alloc(sizeof(*group), featuregroup_destructor);
518 group->name = ast_strdup(cat);
524 group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
525 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
534 struct features_config {
535 struct features_global_config *global;
536 struct ast_featuremap_config *featuremap;
537 struct ao2_container *applicationmap;
538 struct ao2_container *featuregroups;
541 static struct aco_type global_option = {
544 .category_match = ACO_WHITELIST,
545 .category = "^general$",
546 .item_offset = offsetof(struct features_config, global),
549 static struct aco_type featuremap_option = {
551 .name = "featuremap",
552 .category_match = ACO_WHITELIST,
553 .category = "^featuremap$",
554 .item_offset = offsetof(struct features_config, featuremap),
557 static struct aco_type applicationmap_option = {
559 .name = "applicationmap",
560 .category_match = ACO_WHITELIST,
561 .category = "^applicationmap$",
562 .item_offset = offsetof(struct features_config, applicationmap),
565 static struct aco_type featuregroup_option = {
567 .name = "featuregroup",
568 .category_match = ACO_BLACKLIST,
569 .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
570 .item_offset = offsetof(struct features_config, featuregroups),
571 .item_alloc = featuregroup_alloc,
572 .item_find = featuregroup_find,
575 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
576 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
577 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
578 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
580 static struct aco_file features_conf = {
581 .filename = "features.conf",
582 .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option),
585 AO2_GLOBAL_OBJ_STATIC(globals);
587 static void features_config_destructor(void *obj)
589 struct features_config *cfg = obj;
591 ao2_cleanup(cfg->global);
592 ao2_cleanup(cfg->featuremap);
593 ao2_cleanup(cfg->applicationmap);
594 ao2_cleanup(cfg->featuregroups);
597 static void featuremap_config_destructor(void *obj)
599 struct ast_featuremap_config *cfg = obj;
601 ast_string_field_free_memory(cfg);
604 static void global_config_destructor(void *obj)
606 struct features_global_config *cfg = obj;
608 ao2_cleanup(cfg->general);
609 ao2_cleanup(cfg->xfer);
610 ao2_cleanup(cfg->pickup);
613 static void general_destructor(void *obj)
615 struct ast_features_general_config *cfg = obj;
617 ast_string_field_free_memory(cfg);
620 static void xfer_destructor(void *obj)
622 struct ast_features_xfer_config *cfg = obj;
624 ast_string_field_free_memory(cfg);
627 static void pickup_destructor(void *obj)
629 struct ast_features_pickup_config *cfg = obj;
631 ast_string_field_free_memory(cfg);
634 static struct features_global_config *global_config_alloc(void)
636 RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
638 cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
643 cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
644 if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
648 cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
649 if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
653 cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
654 if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
662 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
664 return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
665 replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
666 applicationmap_sort, NULL);
671 * \brief Allocate the major configuration structure
673 * The parameter is used to determine if the applicationmap and featuregroup
674 * structures should be allocated. We only want to allocate these structures for
675 * the global features_config structure. For the datastores on channels, we don't
676 * need to allocate these structures because they are not used.
678 * \param allocate_applicationmap See previous explanation
679 * \retval NULL Failed to alloate configuration
680 * \retval non-NULL Allocated configuration
682 static struct features_config *__features_config_alloc(int allocate_applicationmap)
684 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
686 cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
691 cfg->global = global_config_alloc();;
696 cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
697 if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
701 if (allocate_applicationmap) {
702 cfg->applicationmap = applicationmap_alloc(1);
703 if (!cfg->applicationmap) {
707 cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
709 if (!cfg->featuregroups) {
719 static void *features_config_alloc(void)
721 return __features_config_alloc(1);
724 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
726 ast_string_fields_copy(dest, src);
727 dest->featuredigittimeout = src->featuredigittimeout;
730 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
732 ast_string_fields_copy(dest, src);
733 dest->transferdigittimeout = src->transferdigittimeout;
734 dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
735 dest->atxferloopdelay = src->atxferloopdelay;
736 dest->atxfercallbackretries = src->atxfercallbackretries;
737 dest->atxferdropcall = src->atxferdropcall;
740 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
742 ast_string_fields_copy(dest, src);
745 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
747 general_copy(dest->general, src->general);
748 xfer_copy(dest->xfer, src->xfer);
749 pickup_copy(dest->pickup, src->pickup);
752 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
754 ast_string_fields_copy(dest, src);
757 static void features_copy(struct features_config *dest, const struct features_config *src)
759 global_copy(dest->global, src->global);
760 featuremap_copy(dest->featuremap, src->featuremap);
762 /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
763 * is produced on the fly when ast_get_chan_applicationmap() is called
767 static struct features_config *features_config_dup(const struct features_config *orig)
769 struct features_config *dup;
771 dup = __features_config_alloc(0);
776 features_copy(dup, orig);
781 static int general_set(struct ast_features_general_config *general, const char *name,
786 if (!strcasecmp(name, "featuredigittimeout")) {
787 res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
788 } else if (!strcasecmp(name, "courtesytone")) {
789 ast_string_field_set(general, courtesytone, value);
791 /* Unrecognized option */
798 static int general_get(struct ast_features_general_config *general, const char *field,
799 char *buf, size_t len)
803 if (!strcasecmp(field, "featuredigittimeout")) {
804 snprintf(buf, len, "%u", general->featuredigittimeout);
805 } else if (!strcasecmp(field, "courtesytone")) {
806 ast_copy_string(buf, general->courtesytone, len);
808 /* Unrecognized option */
815 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
820 if (!strcasecmp(name, "transferdigittimeout")) {
821 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
822 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
823 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
824 } else if (!strcasecmp(name, "atxferloopdelay")) {
825 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
826 } else if (!strcasecmp(name, "atxfercallbackretries")) {
827 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
828 } else if (!strcasecmp(name, "atxferdropcall")) {
829 xfer->atxferdropcall = ast_true(value);
830 } else if (!strcasecmp(name, "xfersound")) {
831 ast_string_field_set(xfer, xfersound, value);
832 } else if (!strcasecmp(name, "xferfailsound")) {
833 ast_string_field_set(xfer, xferfailsound, value);
834 } else if (!strcasecmp(name, "atxferabort")) {
835 ast_string_field_set(xfer, atxferabort, value);
836 } else if (!strcasecmp(name, "atxfercomplete")) {
837 ast_string_field_set(xfer, atxfercomplete, value);
838 } else if (!strcasecmp(name, "atxferthreeway")) {
839 ast_string_field_set(xfer, atxferthreeway, value);
841 /* Unrecognized option */
848 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
849 char *buf, size_t len)
853 if (!strcasecmp(field, "transferdigittimeout")) {
854 snprintf(buf, len, "%u", xfer->transferdigittimeout);
855 } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
856 snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
857 } else if (!strcasecmp(field, "atxferloopdelay")) {
858 snprintf(buf, len, "%u", xfer->atxferloopdelay);
859 } else if (!strcasecmp(field, "atxfercallbackretries")) {
860 snprintf(buf, len, "%u", xfer->atxfercallbackretries);
861 } else if (!strcasecmp(field, "atxferdropcall")) {
862 snprintf(buf, len, "%u", xfer->atxferdropcall);
863 } else if (!strcasecmp(field, "xfersound")) {
864 ast_copy_string(buf, xfer->xfersound, len);
865 } else if (!strcasecmp(field, "xferfailsound")) {
866 ast_copy_string(buf, xfer->xferfailsound, len);
867 } else if (!strcasecmp(field, "atxferabort")) {
868 ast_copy_string(buf, xfer->atxferabort, len);
869 } else if (!strcasecmp(field, "atxfercomplete")) {
870 ast_copy_string(buf, xfer->atxfercomplete, len);
871 } else if (!strcasecmp(field, "atxferthreeway")) {
872 ast_copy_string(buf, xfer->atxferthreeway, len);
874 /* Unrecognized option */
881 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
886 if (!strcasecmp(name, "pickupsound")) {
887 ast_string_field_set(pickup, pickupsound, value);
888 } else if (!strcasecmp(name, "pickupfailsound")) {
889 ast_string_field_set(pickup, pickupfailsound, value);
890 } else if (!strcasecmp(name, "pickupexten")) {
891 ast_string_field_set(pickup, pickupexten, value);
893 /* Unrecognized option */
900 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
901 char *buf, size_t len)
905 if (!strcasecmp(field, "pickupsound")) {
906 ast_copy_string(buf, pickup->pickupsound, len);
907 } else if (!strcasecmp(field, "pickupfailsound")) {
908 ast_copy_string(buf, pickup->pickupfailsound, len);
909 } else if (!strcasecmp(field, "pickupexten")) {
910 ast_copy_string(buf, pickup->pickupexten, len);
912 /* Unrecognized option */
919 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
924 if (!strcasecmp(name, "blindxfer")) {
925 ast_string_field_set(featuremap, blindxfer, value);
926 } else if (!strcasecmp(name, "disconnect")) {
927 ast_string_field_set(featuremap, disconnect, value);
928 } else if (!strcasecmp(name, "automon")) {
929 ast_string_field_set(featuremap, automon, value);
930 } else if (!strcasecmp(name, "atxfer")) {
931 ast_string_field_set(featuremap, atxfer, value);
932 } else if (!strcasecmp(name, "automixmon")) {
933 ast_string_field_set(featuremap, automixmon, value);
934 } else if (!strcasecmp(name, "parkcall")) {
935 ast_string_field_set(featuremap, parkcall, value);
937 /* Unrecognized option */
944 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
945 char *buf, size_t len)
949 if (!strcasecmp(field, "blindxfer")) {
950 ast_copy_string(buf, featuremap->blindxfer, len);
951 } else if (!strcasecmp(field, "disconnect")) {
952 ast_copy_string(buf, featuremap->disconnect, len);
953 } else if (!strcasecmp(field, "automon")) {
954 ast_copy_string(buf, featuremap->automon, len);
955 } else if (!strcasecmp(field, "atxfer")) {
956 ast_copy_string(buf, featuremap->atxfer, len);
957 } else if (!strcasecmp(field, "automixmon")) {
958 ast_copy_string(buf, featuremap->automixmon, len);
959 } else if (!strcasecmp(field, "parkcall")) {
960 ast_copy_string(buf, featuremap->parkcall, len);
962 /* Unrecognized option */
969 static void feature_ds_destroy(void *data)
971 struct features_config *cfg = data;
975 static void *feature_ds_duplicate(void *data)
977 struct features_config *old_cfg = data;
979 return features_config_dup(old_cfg);
982 static const struct ast_datastore_info feature_ds_info = {
984 .destroy = feature_ds_destroy,
985 .duplicate = feature_ds_duplicate,
990 * \brief Find or create feature datastore on a channel
992 * \pre chan is locked
994 * \return the data on the FEATURE datastore, or NULL on error
996 static struct features_config *get_feature_ds(struct ast_channel *chan)
998 RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
999 struct features_config *cfg;
1000 struct ast_datastore *ds;
1002 if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1008 orig = ao2_global_obj_ref(globals);
1013 cfg = features_config_dup(orig);
1018 if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1023 /* Give the datastore a reference to the config */
1027 ast_channel_datastore_add(chan, ds);
1032 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1034 struct ast_datastore *ds;
1036 if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1037 /* Hasn't been created yet. Trigger creation. */
1038 RAII_VAR(struct features_config *, cfg, get_feature_ds(chan), ao2_cleanup);
1039 ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1045 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
1047 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1050 cfg = get_feature_ds(chan);
1052 cfg = ao2_global_obj_ref(globals);
1059 ast_assert(cfg->global && cfg->global->general);
1061 ao2_ref(cfg->global->general, +1);
1062 return cfg->global->general;
1065 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
1067 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1070 cfg = get_feature_ds(chan);
1072 cfg = ao2_global_obj_ref(globals);
1079 ast_assert(cfg->global && cfg->global->xfer);
1081 ao2_ref(cfg->global->xfer, +1);
1082 return cfg->global->xfer;
1085 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
1087 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1090 cfg = get_feature_ds(chan);
1092 cfg = ao2_global_obj_ref(globals);
1099 ast_assert(cfg->global && cfg->global->pickup);
1101 ao2_ref(cfg->global->pickup, +1);
1102 return cfg->global->pickup;
1105 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
1107 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1110 cfg = get_feature_ds(chan);
1112 cfg = ao2_global_obj_ref(globals);
1119 ast_assert(cfg->featuremap != NULL);
1121 ao2_ref(cfg->featuremap, +1);
1122 return cfg->featuremap;
1125 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1127 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1130 cfg = get_feature_ds(chan);
1132 cfg = ao2_global_obj_ref(globals);
1139 return featuremap_get(cfg->featuremap, feature, buf, len);
1142 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1144 RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1145 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1147 if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1151 /* Dang, must be in the application map */
1152 applicationmap = ast_get_chan_applicationmap(chan);
1153 if (!applicationmap) {
1157 item = ao2_find(applicationmap, feature, OBJ_KEY);
1162 ast_copy_string(buf, item->dtmf, len);
1166 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
1167 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1168 unsigned int activate_on_self)
1170 struct ast_applicationmap_item *item;
1172 item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1174 if (!item || ast_string_field_init(item, 64)) {
1178 ast_string_field_set(item, name, name);
1179 ast_string_field_set(item, app, app);
1180 ast_string_field_set(item, app_data, app_data);
1181 ast_string_field_set(item, moh_class, moh_class);
1182 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1183 item->activate_on_self = activate_on_self;
1188 static int add_item(void *obj, void *arg, int flags)
1190 struct featuregroup_item *fg_item = obj;
1191 struct ao2_container *applicationmap = arg;
1192 RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1194 /* If there's no DTMF override, then we can just link
1195 * the applicationmap item directly. Otherwise, we need
1196 * to create a copy with the DTMF override in place and
1199 if (ast_strlen_zero(fg_item->dtmf_override)) {
1200 ao2_ref(fg_item->appmap_item, +1);
1201 appmap_item = fg_item->appmap_item;
1203 appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1204 fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1205 fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1206 fg_item->appmap_item->activate_on_self);
1213 ao2_link(applicationmap, appmap_item);
1217 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
1219 RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1220 struct ao2_container *applicationmap;
1229 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1232 ao2_ref(cfg->applicationmap, +1);
1233 return cfg->applicationmap;
1236 group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1237 if (ast_strlen_zero(group_names)) {
1241 applicationmap = applicationmap_alloc(0);
1242 if (!applicationmap) {
1246 while ((name = strsep(&group_names, "#"))) {
1247 RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1250 RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1253 ao2_link(applicationmap, item);
1255 ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1256 name, ast_channel_name(chan));
1259 ao2_callback(group->items, 0, add_item, applicationmap);
1263 if (ao2_container_count(applicationmap) == 0) {
1264 ao2_cleanup(applicationmap);
1268 return applicationmap;
1271 static int applicationmap_handler(const struct aco_option *opt,
1272 struct ast_variable *var, void *obj)
1274 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1275 struct ao2_container *applicationmap = obj;
1276 AST_DECLARE_APP_ARGS(args,
1278 AST_APP_ARG(activate_on);
1280 AST_APP_ARG(app_data);
1281 AST_APP_ARG(moh_class);
1283 char *parse = ast_strdupa(var->value);
1286 unsigned int activate_on_self;
1288 AST_STANDARD_APP_ARGS(args, parse);
1290 if (ast_strlen_zero(args.dtmf) ||
1291 ast_strlen_zero(args.activate_on) ||
1292 ast_strlen_zero(args.app)) {
1293 ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1297 /* features.conf used to have an "activated_by" portion
1298 * in addition to activate_on. Get rid of whatever may be
1301 slash = strchr(args.activate_on, '/');
1306 /* Two syntaxes allowed for applicationmap:
1307 * Old: foo = *1,self,NoOp,Boo!,default
1308 * New: foo = *1,self,NoOp(Boo!),default
1310 * We need to handle both
1312 paren = strchr(args.app, '(');
1317 args.moh_class = args.app_data;
1319 close_paren = strrchr(paren, ')');
1321 *close_paren = '\0';
1323 args.app_data = paren;
1325 /* Re-check that the application is not empty */
1326 if (ast_strlen_zero(args.app)) {
1327 ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1330 } else if (strchr(args.app_data, '"')) {
1331 args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1334 /* Allow caller and callee to be specified for backwards compatibility */
1335 if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1336 activate_on_self = 1;
1337 } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1338 activate_on_self = 0;
1340 ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1341 args.activate_on, var->name);
1345 ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1346 args.dtmf, args.app, args.app_data, args.moh_class);
1348 item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1349 args.moh_class, args.dtmf, activate_on_self);
1355 if (!ao2_link(applicationmap, item)) {
1362 static int featuregroup_handler(const struct aco_option *opt,
1363 struct ast_variable *var, void *obj)
1365 RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
1366 struct featuregroup *group = obj;
1368 item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
1369 if (!item || ast_string_field_init(item, 32)) {
1373 ast_string_field_set(item, appmap_item_name, var->name);
1374 ast_string_field_set(item, dtmf_override, var->value);
1376 if (!ao2_link(group->items, item)) {
1380 /* We wait to look up the application map item in the preapply callback */
1385 static int general_handler(const struct aco_option *opt,
1386 struct ast_variable *var, void *obj)
1388 struct features_global_config *global = obj;
1389 struct ast_features_general_config *general = global->general;
1391 return general_set(general, var->name, var->value);
1394 static int xfer_handler(const struct aco_option *opt,
1395 struct ast_variable *var, void *obj)
1397 struct features_global_config *global = obj;
1398 struct ast_features_xfer_config *xfer = global->xfer;
1400 return xfer_set(xfer, var->name, var->value);
1403 static int pickup_handler(const struct aco_option *opt,
1404 struct ast_variable *var, void *obj)
1406 struct features_global_config *global = obj;
1407 struct ast_features_pickup_config *pickup = global->pickup;
1409 return pickup_set(pickup, var->name, var->value);
1412 static int unsupported_handler(const struct aco_option *opt,
1413 struct ast_variable *var, void *obj)
1415 ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1419 static int featuremap_handler(const struct aco_option *opt,
1420 struct ast_variable *var, void *obj)
1422 struct ast_featuremap_config *featuremap = obj;
1424 return featuremap_set(featuremap, var->name, var->value);
1427 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1429 struct ast_applicationmap_item *appmap_item;
1430 struct featuregroup_item *fg_item = obj;
1432 struct ao2_container *applicationmap = data;
1434 appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1440 fg_item->appmap_item = appmap_item;
1445 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1447 struct featuregroup *group = obj;
1450 ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1453 ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1457 return *err ? CMP_STOP : 0;
1460 static int features_pre_apply_config(void);
1462 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1463 .files = ACO_FILES(&features_conf),
1464 .pre_apply_config = features_pre_apply_config,
1467 static int features_pre_apply_config(void)
1469 struct features_config *cfg = aco_pending_config(&cfg_info);
1472 /* Now that the entire config has been processed, we can check that the featuregroup
1473 * items refer to actual applicationmap items.
1476 ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
1481 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1482 char *buf, size_t len)
1485 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1486 SCOPED_CHANNELLOCK(lock, chan);
1488 if (!strcasecmp(data, "inherit")) {
1489 struct ast_datastore *ds = get_feature_chan_ds(chan);
1490 unsigned int inherit = ds ? ds->inheritance : 0;
1492 snprintf(buf, len, "%s", inherit ? "yes" : "no");
1496 cfg = get_feature_ds(chan);
1501 res = general_get(cfg->global->general, data, buf, len) &&
1502 xfer_get(cfg->global->xfer, data, buf, len) &&
1503 pickup_get(cfg->global->pickup, data, buf, len);
1506 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1512 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1516 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1517 SCOPED_CHANNELLOCK(lock, chan);
1519 if (!strcasecmp(data, "inherit")) {
1520 struct ast_datastore *ds = get_feature_chan_ds(chan);
1522 ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1527 if (!(cfg = get_feature_ds(chan))) {
1531 res = general_set(cfg->global->general, data, value) &&
1532 xfer_set(cfg->global->xfer, data, value) &&
1533 pickup_set(cfg->global->pickup, data, value);
1536 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1542 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1543 char *buf, size_t len)
1546 SCOPED_CHANNELLOCK(lock, chan);
1548 res = ast_get_builtin_feature(chan, data, buf, len);
1551 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1557 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1561 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1562 SCOPED_CHANNELLOCK(lock, chan);
1564 if (!(cfg = get_feature_ds(chan))) {
1568 res = featuremap_set(cfg->featuremap, data, value);
1570 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1577 static struct ast_custom_function feature_function = {
1579 .read = feature_read,
1580 .write = feature_write
1583 static struct ast_custom_function featuremap_function = {
1584 .name = "FEATUREMAP",
1585 .read = featuremap_read,
1586 .write = featuremap_write
1589 static int load_config(void)
1591 if (aco_info_init(&cfg_info)) {
1592 ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1596 aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1597 __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
1598 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1599 __stringify(DEFAULT_COURTESY_TONE), general_handler, 0);
1601 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1602 __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
1603 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1604 __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
1605 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1606 __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
1607 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1608 __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
1609 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1610 __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
1611 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1612 DEFAULT_XFERSOUND, xfer_handler, 0);
1613 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1614 DEFAULT_XFERFAILSOUND, xfer_handler, 0);
1615 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1616 DEFAULT_ATXFER_ABORT, xfer_handler, 0);
1617 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1618 DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
1619 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1620 DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
1622 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1623 DEFAULT_PICKUPEXTEN, pickup_handler, 0);
1624 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1625 DEFAULT_PICKUPSOUND, pickup_handler, 0);
1626 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1627 DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
1629 aco_option_register_custom(&cfg_info, "context", ACO_EXACT, global_options,
1630 "", unsupported_handler, 0);
1631 aco_option_register_custom(&cfg_info, "parkext", ACO_EXACT, global_options,
1632 "", unsupported_handler, 0);
1633 aco_option_register_custom(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1634 "", unsupported_handler, 0);
1635 aco_option_register_custom(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1636 "", unsupported_handler, 0);
1637 aco_option_register_custom(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1638 "", unsupported_handler, 0);
1639 aco_option_register_custom(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1640 "", unsupported_handler, 0);
1641 aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, global_options,
1642 "", unsupported_handler, 0);
1643 aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, global_options,
1644 "", unsupported_handler, 0);
1645 aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1646 "", unsupported_handler, 0);
1647 aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1648 "", unsupported_handler, 0);
1649 aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1650 "", unsupported_handler, 0);
1651 aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1652 "", unsupported_handler, 0);
1653 aco_option_register_custom(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1654 "", unsupported_handler, 0);
1655 aco_option_register_custom(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1656 "", unsupported_handler, 0);
1657 aco_option_register_custom(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1658 "", unsupported_handler, 0);
1659 aco_option_register_custom(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1660 "", unsupported_handler, 0);
1661 aco_option_register_custom(&cfg_info, "adsipark", ACO_EXACT, global_options,
1662 "", unsupported_handler, 0);
1664 aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1665 DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
1666 aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1667 DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
1668 aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1669 DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
1670 aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1671 DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
1672 aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1673 DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
1674 aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1675 DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
1677 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
1678 "", applicationmap_handler, 0);
1680 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
1681 "", featuregroup_handler, 0);
1683 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1684 ast_log(LOG_ERROR, "Failed to process features.conf configuration!\n");
1685 aco_info_destroy(&cfg_info);
1686 ao2_global_obj_release(globals);
1693 static int print_featuregroup(void *obj, void *arg, int flags)
1695 struct featuregroup_item *item = obj;
1696 struct ast_cli_args *a = arg;
1698 ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1699 S_OR(item->dtmf_override, item->appmap_item->dtmf));
1704 static int print_featuregroups(void *obj, void *arg, int flags)
1706 struct featuregroup *group = obj;
1707 struct ast_cli_args *a = arg;
1709 ast_cli(a->fd, "===> Group: %s\n", group->name);
1711 ao2_callback(group->items, 0, print_featuregroup, a);
1715 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1717 static int print_applicationmap(void *obj, void *arg, int flags)
1719 struct ast_applicationmap_item *item = obj;
1720 struct ast_cli_args *a = arg;
1722 ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1727 * \brief CLI command to list configured features
1732 * \retval CLI_SUCCESS on success.
1733 * \retval NULL when tab completion is used.
1735 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1737 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1742 e->command = "features show";
1744 "Usage: features show\n"
1745 " Lists configured features\n";
1751 cfg = ao2_global_obj_ref(globals);
1753 ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1754 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1756 ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1757 ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1758 ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1759 ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1760 ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1761 ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1762 ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1764 ast_cli(a->fd, "\n");
1765 ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1766 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1767 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1768 ast_cli(a->fd, "(none)\n");
1770 ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1773 ast_cli(a->fd, "\nFeature Groups:\n");
1774 ast_cli(a->fd, "---------------\n");
1775 if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1776 ast_cli(a->fd, "(none)\n");
1778 ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1781 ast_cli(a->fd, "\n");
1786 static struct ast_cli_entry cli_features_config[] = {
1787 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1790 void ast_features_config_shutdown(void)
1792 ast_custom_function_unregister(&featuremap_function);
1793 ast_custom_function_unregister(&feature_function);
1794 ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1795 aco_info_destroy(&cfg_info);
1796 ao2_global_obj_release(globals);
1799 int ast_features_config_reload(void)
1801 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1807 int ast_features_config_init(void)
1811 res = load_config();
1812 res |= __ast_custom_function_register(&feature_function, NULL);
1813 res |= __ast_custom_function_register(&featuremap_function, NULL);
1814 res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1817 ast_features_config_shutdown();