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="3000">
46 <synopsis>Milliseconds allowed between digit presses when dialing a transfer destination</synopsis>
48 <configOption name="atxfernoanswertimeout" default="15000">
49 <synopsis>Milliseconds 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="10000">
66 <synopsis>Milliseconds 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="pickupexten" default="*8">
110 <synopsis>Digits used for picking up ringing calls</synopsis>
112 <para>In order for the pickup attempt to be successful, the party attempting to
113 pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
114 common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
115 have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
116 <replaceable>callgroup</replaceable>.</para>
119 <configOption name="pickupsound">
120 <synopsis>Sound to play to picker when a call is picked up</synopsis>
122 <configOption name="pickupfailsound">
123 <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
126 <configObject name="featuremap">
127 <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
128 <configOption name="atxfer">
129 <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
131 <para>The transferee parties will be placed on hold and the
132 transferrer may dial an extension to reach a transfer target. During an
133 attended transfer, the transferrer may consult with the transfer target
134 before completing the transfer. Once the transferrer has hung up or pressed
135 the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
136 and transfer target will be bridged.</para>
139 <configOption name="blindxfer" default="#">
140 <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
142 <para>The transferee parties will be placed on hold and the
143 transferrer may dial an extension to reach a transfer target. During a
144 blind transfer, as soon as the transfer target is dialed, the transferrer
148 <configOption name="disconnect" default="*">
149 <synopsis>DTMF sequence to disconnect the current call</synopsis>
151 <para>Entering this DTMF sequence will cause the bridge to end, no
152 matter the number of parties present</para>
155 <configOption name="parkcall">
156 <synopsis>DTMF sequence to park a call</synopsis>
158 <para>The parking lot used to park the call is determined by using either the
159 <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
160 the channel (provided by the channel driver) if the variable is not present. If
161 no configured value on the channel is present, then <literal>"default"</literal>
162 is used. The call is parked in the next available space in the parking lot.</para>
165 <configOption name="automon">
166 <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
168 <para>This will cause the channel that pressed the DTMF sequence
169 to be monitored by the <literal>Monitor</literal> application. The
170 format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
171 channel variable. If this variable is not specified, then <literal>wav</literal> is the
172 default. The filename is constructed in the following manner:</para>
174 <para> prefix-timestamp-filename</para>
176 <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
177 channel variable or <literal>auto</literal> if the variable is not set. The timestamp
178 is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
179 channel variable or the callerID of the channels if the variable is not set.</para>
182 <configOption name="automixmon">
183 <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
185 <para>Operation of the automixmon is similar to the <literal> automon </literal>
186 feature, with the following exceptions:
187 <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
188 <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
189 There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
191 <see-also><ref type="configOption">automon</ref></see-also>
194 <configObject name="applicationmap">
195 <synopsis>Section for defining custom feature invocations during a call</synopsis>
197 <para>The applicationmap is an area where new custom features can be created. Items
198 defined in the applicationmap are not automatically accessible to bridged parties. Access
199 to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
200 The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
201 either applicationmap item names or featuregroup names.</para>
203 <configOption name="^.*$" regex="true">
204 <synopsis>A custom feature to invoke during a bridged call</synopsis>
206 <para>Each item listed here is a comma-separated list of parameters that determine
207 how a feature may be invoked during a call</para>
208 <para> Example:</para>
209 <para> eggs = *5,self,Playback(hello-world),default</para>
210 <para>This would create a feature called <literal>eggs</literal> that could be invoked
211 during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
212 sequence would then trigger the <literal>Playback</literal> application to play the
213 <literal>hello-world</literal> file. The application invocation would happen on the
214 party that pressed the DTMF sequence since <literal>self</literal> is specified. The
215 other parties in the bridge would hear the <literal>default</literal> music on hold
216 class during the playback.</para>
217 <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
218 is also allowed. The following applicationmap lines are functionally identical:</para>
219 <para> eggs = *5,self,Playback(hello-world),default</para>
220 <para> eggs = *5,self,Playback,hello-world,default</para>
221 <para> eggs = *5,self,Playback,"hello-world",default</para>
224 <parameter name="dtmf" required="true">
225 <para>The DTMF sequence used to trigger the option</para>
227 <parameter name="activate_on" required="true">
228 <para>The party that the feature will be invoked on</para>
230 <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
231 <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
234 <parameter name="app" required="true">
235 <para>The dialplan application to run when the DTMF sequence is pressed</para>
236 <argument name="app_args" required="false">
237 <para>The arguments to the dialplan application to run</para>
240 <parameter name="moh_class" required="false">
241 <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
246 <configObject name="featuregroup">
247 <synopsis>Groupings of items from the applicationmap</synopsis>
249 <para>Feature groups allow for multiple applicationmap items to be
250 grouped together. Like with individual applicationmap items, feature groups
251 can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
252 In addition to creating groupings, the feature group section allows for the
253 DTMF sequence used to invoke an applicationmap item to be overridden with
254 a different sequence.</para>
256 <configOption name="^.*$" regex="true">
257 <synopsis>Applicationmap item to place in the feature group</synopsis>
259 <para>Each item here must be a name of an item in the applicationmap. The
260 argument may either be a new DTMF sequence to use for the item or it
261 may be left blank in order to use the DTMF sequence specified in the
262 applicationmap. For example:</para>
263 <para> eggs => *1</para>
264 <para> bacon =></para>
265 <para>would result in the applicationmap items <literal>eggs</literal> and
266 <literal>bacon</literal> being in the featuregroup. The former would have its
267 default DTMF trigger overridden with <literal>*1</literal> and the latter would
268 have the DTMF value specified in the applicationmap.</para>
274 <function name="FEATURE" language="en_US">
276 Get or set a feature option on a channel.
279 <parameter name="option_name" required="true">
280 <para>The allowed values are:</para>
282 <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
283 <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>
284 <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>
285 <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>
286 <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>
287 <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>
288 <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>
289 <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>
290 <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>
291 <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>
292 <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>
293 <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>
294 <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>
295 <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>
296 <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>
297 <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>
298 <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>
303 <para>When this function is used as a read, it will get the current
304 value of the specified feature option for this channel. It will be
305 the value of this option configured in features.conf if a channel specific
306 value has not been set. This function can also be used to set a channel
307 specific value for the supported feature options.</para>
310 <ref type="function">FEATUREMAP</ref>
313 <function name="FEATUREMAP" language="en_US">
315 Get or set a feature map to a given value on a specific channel.
318 <parameter name="feature_name" required="true">
319 <para>The allowed values are:</para>
321 <enum name="atxfer"><para>Attended Transfer</para></enum>
322 <enum name="blindxfer"><para>Blind Transfer</para></enum>
323 <enum name="automon"><para>Auto Monitor</para></enum>
324 <enum name="disconnect"><para>Call Disconnect</para></enum>
325 <enum name="parkcall"><para>Park Call</para></enum>
326 <enum name="automixmon"><para>Auto MixMonitor</para></enum>
331 <para>When this function is used as a read, it will get the current
332 digit sequence mapped to the specified feature for this channel. This
333 value will be the one configured in features.conf if a channel specific
334 value has not been set. This function can also be used to set a channel
335 specific value for a feature mapping.</para>
338 <ref type="function">FEATURE</ref>
342 /*! Default general options */
343 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
344 #define DEFAULT_COURTESY_TONE ""
345 #define DEFAULT_RECORDING_FAIL_SOUND ""
347 /*! Default xfer options */
348 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
349 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
350 #define DEFAULT_ATXFER_DROP_CALL 0
351 #define DEFAULT_ATXFER_LOOP_DELAY 10000
352 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
353 #define DEFAULT_XFERSOUND "beep"
354 #define DEFAULT_XFERFAILSOUND "beeperr"
355 #define DEFAULT_ATXFER_ABORT "*1"
356 #define DEFAULT_ATXFER_COMPLETE "*2"
357 #define DEFAULT_ATXFER_THREEWAY "*3"
359 /*! Default pickup options */
360 #define DEFAULT_PICKUPEXTEN "*8"
361 #define DEFAULT_PICKUPSOUND ""
362 #define DEFAULT_PICKUPFAILSOUND ""
364 /*! Default featuremap options */
365 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
366 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
367 #define DEFAULT_FEATUREMAP_AUTOMON ""
368 #define DEFAULT_FEATUREMAP_ATXFER ""
369 #define DEFAULT_FEATUREMAP_PARKCALL ""
370 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
373 * \brief Configuration from the "general" section of features.conf
375 struct features_global_config {
376 struct ast_features_general_config *general;
377 struct ast_features_xfer_config *xfer;
378 struct ast_features_pickup_config *pickup;
381 static void ast_applicationmap_item_destructor(void *obj)
383 struct ast_applicationmap_item *item = obj;
385 ast_string_field_free_memory(item);
388 static int applicationmap_sort(const void *obj, const void *arg, int flags)
390 const struct ast_applicationmap_item *item1 = obj;
391 const struct ast_applicationmap_item *item2;
394 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
397 return strcasecmp(item1->name, key2);
398 case OBJ_PARTIAL_KEY:
400 return strncasecmp(item1->name, key2, strlen(key2));
404 return strcasecmp(item1->name, item2->name);
409 * \brief Entry in the container of featuregroups
411 struct featuregroup_item {
412 AST_DECLARE_STRING_FIELDS(
413 /*! The name of the applicationmap item that we are referring to */
414 AST_STRING_FIELD(appmap_item_name);
415 /*! Custom DTMF override to use instead of the default for the applicationmap item */
416 AST_STRING_FIELD(dtmf_override);
418 /*! The applicationmap item that is being referred to */
419 struct ast_applicationmap_item *appmap_item;
422 static void featuregroup_item_destructor(void *obj)
424 struct featuregroup_item *item = obj;
426 ast_string_field_free_memory(item);
427 ao2_cleanup(item->appmap_item);
430 static int group_item_sort(const void *obj, const void *arg, int flags)
432 const struct featuregroup_item *item1 = obj;
433 const struct featuregroup_item *item2;
436 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
439 return strcasecmp(item1->appmap_item_name, key2);
440 case OBJ_PARTIAL_KEY:
442 return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
445 return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
452 * \brief Featuregroup representation
454 struct featuregroup {
455 /*! The name of the featuregroup */
457 /*! A container of featuregroup_items */
458 struct ao2_container *items;
461 static int featuregroup_hash(const void *obj, int flags)
463 const struct featuregroup *group;
466 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
469 return ast_str_case_hash(key);
470 case OBJ_PARTIAL_KEY:
476 return ast_str_case_hash(group->name);
480 static int featuregroup_cmp(void *obj, void *arg, int flags)
482 struct featuregroup *group1 = obj;
483 struct featuregroup *group2;
486 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
489 return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
490 case OBJ_PARTIAL_KEY:
492 return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
495 return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
501 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
503 return ao2_find(group_container, category, OBJ_KEY);
506 static void featuregroup_destructor(void *obj)
508 struct featuregroup *group = obj;
510 ast_free((char *) group->name);
511 ao2_cleanup(group->items);
514 static void *featuregroup_alloc(const char *cat)
516 struct featuregroup *group;
518 group = ao2_alloc(sizeof(*group), featuregroup_destructor);
523 group->name = ast_strdup(cat);
529 group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
530 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
539 struct features_config {
540 struct features_global_config *global;
541 struct ast_featuremap_config *featuremap;
542 struct ao2_container *applicationmap;
543 struct ao2_container *featuregroups;
546 static struct aco_type global_option = {
549 .category_match = ACO_WHITELIST,
550 .category = "^general$",
551 .item_offset = offsetof(struct features_config, global),
554 static struct aco_type featuremap_option = {
556 .name = "featuremap",
557 .category_match = ACO_WHITELIST,
558 .category = "^featuremap$",
559 .item_offset = offsetof(struct features_config, featuremap),
562 static struct aco_type applicationmap_option = {
564 .name = "applicationmap",
565 .category_match = ACO_WHITELIST,
566 .category = "^applicationmap$",
567 .item_offset = offsetof(struct features_config, applicationmap),
570 static struct aco_type featuregroup_option = {
572 .name = "featuregroup",
573 .category_match = ACO_BLACKLIST,
574 .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
575 .item_offset = offsetof(struct features_config, featuregroups),
576 .item_alloc = featuregroup_alloc,
577 .item_find = featuregroup_find,
580 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
581 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
582 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
583 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
585 static struct aco_file features_conf = {
586 .filename = "features.conf",
587 .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option),
590 AO2_GLOBAL_OBJ_STATIC(globals);
592 static void features_config_destructor(void *obj)
594 struct features_config *cfg = obj;
596 ao2_cleanup(cfg->global);
597 ao2_cleanup(cfg->featuremap);
598 ao2_cleanup(cfg->applicationmap);
599 ao2_cleanup(cfg->featuregroups);
602 static void featuremap_config_destructor(void *obj)
604 struct ast_featuremap_config *cfg = obj;
606 ast_string_field_free_memory(cfg);
609 static void global_config_destructor(void *obj)
611 struct features_global_config *cfg = obj;
613 ao2_cleanup(cfg->general);
614 ao2_cleanup(cfg->xfer);
615 ao2_cleanup(cfg->pickup);
618 static void general_destructor(void *obj)
620 struct ast_features_general_config *cfg = obj;
622 ast_string_field_free_memory(cfg);
625 static void xfer_destructor(void *obj)
627 struct ast_features_xfer_config *cfg = obj;
629 ast_string_field_free_memory(cfg);
632 static void pickup_destructor(void *obj)
634 struct ast_features_pickup_config *cfg = obj;
636 ast_string_field_free_memory(cfg);
639 static struct features_global_config *global_config_alloc(void)
641 RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
643 cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
648 cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
649 if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
653 cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
654 if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
658 cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
659 if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
667 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
669 return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
670 replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
671 applicationmap_sort, NULL);
676 * \brief Allocate the major configuration structure
678 * The parameter is used to determine if the applicationmap and featuregroup
679 * structures should be allocated. We only want to allocate these structures for
680 * the global features_config structure. For the datastores on channels, we don't
681 * need to allocate these structures because they are not used.
683 * \param allocate_applicationmap See previous explanation
684 * \retval NULL Failed to alloate configuration
685 * \retval non-NULL Allocated configuration
687 static struct features_config *__features_config_alloc(int allocate_applicationmap)
689 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
691 cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
696 cfg->global = global_config_alloc();;
701 cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
702 if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
706 if (allocate_applicationmap) {
707 cfg->applicationmap = applicationmap_alloc(1);
708 if (!cfg->applicationmap) {
712 cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
714 if (!cfg->featuregroups) {
724 static void *features_config_alloc(void)
726 return __features_config_alloc(1);
729 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
731 ast_string_fields_copy(dest, src);
732 dest->featuredigittimeout = src->featuredigittimeout;
735 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
737 ast_string_fields_copy(dest, src);
738 dest->transferdigittimeout = src->transferdigittimeout;
739 dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
740 dest->atxferloopdelay = src->atxferloopdelay;
741 dest->atxfercallbackretries = src->atxfercallbackretries;
742 dest->atxferdropcall = src->atxferdropcall;
745 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
747 ast_string_fields_copy(dest, src);
750 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
752 general_copy(dest->general, src->general);
753 xfer_copy(dest->xfer, src->xfer);
754 pickup_copy(dest->pickup, src->pickup);
757 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
759 ast_string_fields_copy(dest, src);
762 static void features_copy(struct features_config *dest, const struct features_config *src)
764 global_copy(dest->global, src->global);
765 featuremap_copy(dest->featuremap, src->featuremap);
767 /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
768 * is produced on the fly when ast_get_chan_applicationmap() is called
772 static struct features_config *features_config_dup(const struct features_config *orig)
774 struct features_config *dup;
776 dup = __features_config_alloc(0);
781 features_copy(dup, orig);
786 static int general_set(struct ast_features_general_config *general, const char *name,
791 if (!strcasecmp(name, "featuredigittimeout")) {
792 res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
793 } else if (!strcasecmp(name, "courtesytone")) {
794 ast_string_field_set(general, courtesytone, value);
795 } else if (!strcasecmp(name, "recordingfailsound")) {
796 ast_string_field_set(general, recordingfailsound, value);
798 /* Unrecognized option */
805 static int general_get(struct ast_features_general_config *general, const char *field,
806 char *buf, size_t len)
810 if (!strcasecmp(field, "featuredigittimeout")) {
811 snprintf(buf, len, "%u", general->featuredigittimeout);
812 } else if (!strcasecmp(field, "courtesytone")) {
813 ast_copy_string(buf, general->courtesytone, len);
814 } else if (!strcasecmp(field, "recordingfailsound")) {
815 ast_copy_string(buf, general->recordingfailsound, len);
817 /* Unrecognized option */
824 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
829 if (!strcasecmp(name, "transferdigittimeout")) {
830 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
831 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
832 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
833 } else if (!strcasecmp(name, "atxferloopdelay")) {
834 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
835 } else if (!strcasecmp(name, "atxfercallbackretries")) {
836 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
837 } else if (!strcasecmp(name, "atxferdropcall")) {
838 xfer->atxferdropcall = ast_true(value);
839 } else if (!strcasecmp(name, "xfersound")) {
840 ast_string_field_set(xfer, xfersound, value);
841 } else if (!strcasecmp(name, "xferfailsound")) {
842 ast_string_field_set(xfer, xferfailsound, value);
843 } else if (!strcasecmp(name, "atxferabort")) {
844 ast_string_field_set(xfer, atxferabort, value);
845 } else if (!strcasecmp(name, "atxfercomplete")) {
846 ast_string_field_set(xfer, atxfercomplete, value);
847 } else if (!strcasecmp(name, "atxferthreeway")) {
848 ast_string_field_set(xfer, atxferthreeway, value);
850 /* Unrecognized option */
857 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
858 char *buf, size_t len)
862 if (!strcasecmp(field, "transferdigittimeout")) {
863 snprintf(buf, len, "%u", xfer->transferdigittimeout);
864 } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
865 snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
866 } else if (!strcasecmp(field, "atxferloopdelay")) {
867 snprintf(buf, len, "%u", xfer->atxferloopdelay);
868 } else if (!strcasecmp(field, "atxfercallbackretries")) {
869 snprintf(buf, len, "%u", xfer->atxfercallbackretries);
870 } else if (!strcasecmp(field, "atxferdropcall")) {
871 snprintf(buf, len, "%u", xfer->atxferdropcall);
872 } else if (!strcasecmp(field, "xfersound")) {
873 ast_copy_string(buf, xfer->xfersound, len);
874 } else if (!strcasecmp(field, "xferfailsound")) {
875 ast_copy_string(buf, xfer->xferfailsound, len);
876 } else if (!strcasecmp(field, "atxferabort")) {
877 ast_copy_string(buf, xfer->atxferabort, len);
878 } else if (!strcasecmp(field, "atxfercomplete")) {
879 ast_copy_string(buf, xfer->atxfercomplete, len);
880 } else if (!strcasecmp(field, "atxferthreeway")) {
881 ast_copy_string(buf, xfer->atxferthreeway, len);
883 /* Unrecognized option */
890 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
895 if (!strcasecmp(name, "pickupsound")) {
896 ast_string_field_set(pickup, pickupsound, value);
897 } else if (!strcasecmp(name, "pickupfailsound")) {
898 ast_string_field_set(pickup, pickupfailsound, value);
899 } else if (!strcasecmp(name, "pickupexten")) {
900 ast_string_field_set(pickup, pickupexten, value);
902 /* Unrecognized option */
909 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
910 char *buf, size_t len)
914 if (!strcasecmp(field, "pickupsound")) {
915 ast_copy_string(buf, pickup->pickupsound, len);
916 } else if (!strcasecmp(field, "pickupfailsound")) {
917 ast_copy_string(buf, pickup->pickupfailsound, len);
918 } else if (!strcasecmp(field, "pickupexten")) {
919 ast_copy_string(buf, pickup->pickupexten, len);
921 /* Unrecognized option */
928 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
933 if (!strcasecmp(name, "blindxfer")) {
934 ast_string_field_set(featuremap, blindxfer, value);
935 } else if (!strcasecmp(name, "disconnect")) {
936 ast_string_field_set(featuremap, disconnect, value);
937 } else if (!strcasecmp(name, "automon")) {
938 ast_string_field_set(featuremap, automon, value);
939 } else if (!strcasecmp(name, "atxfer")) {
940 ast_string_field_set(featuremap, atxfer, value);
941 } else if (!strcasecmp(name, "automixmon")) {
942 ast_string_field_set(featuremap, automixmon, value);
943 } else if (!strcasecmp(name, "parkcall")) {
944 ast_string_field_set(featuremap, parkcall, value);
946 /* Unrecognized option */
953 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
954 char *buf, size_t len)
958 if (!strcasecmp(field, "blindxfer")) {
959 ast_copy_string(buf, featuremap->blindxfer, len);
960 } else if (!strcasecmp(field, "disconnect")) {
961 ast_copy_string(buf, featuremap->disconnect, len);
962 } else if (!strcasecmp(field, "automon")) {
963 ast_copy_string(buf, featuremap->automon, len);
964 } else if (!strcasecmp(field, "atxfer")) {
965 ast_copy_string(buf, featuremap->atxfer, len);
966 } else if (!strcasecmp(field, "automixmon")) {
967 ast_copy_string(buf, featuremap->automixmon, len);
968 } else if (!strcasecmp(field, "parkcall")) {
969 ast_copy_string(buf, featuremap->parkcall, len);
971 /* Unrecognized option */
978 static void feature_ds_destroy(void *data)
980 struct features_config *cfg = data;
984 static void *feature_ds_duplicate(void *data)
986 struct features_config *old_cfg = data;
988 return features_config_dup(old_cfg);
991 static const struct ast_datastore_info feature_ds_info = {
993 .destroy = feature_ds_destroy,
994 .duplicate = feature_ds_duplicate,
999 * \brief Find or create feature datastore on a channel
1001 * \pre chan is locked
1003 * \return the data on the FEATURE datastore, or NULL on error
1005 static struct features_config *get_feature_ds(struct ast_channel *chan)
1007 RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
1008 struct features_config *cfg;
1009 struct ast_datastore *ds;
1011 if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1017 orig = ao2_global_obj_ref(globals);
1022 cfg = features_config_dup(orig);
1027 if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1032 /* Give the datastore a reference to the config */
1036 ast_channel_datastore_add(chan, ds);
1041 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1043 struct ast_datastore *ds;
1045 if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1046 /* Hasn't been created yet. Trigger creation. */
1047 RAII_VAR(struct features_config *, cfg, get_feature_ds(chan), ao2_cleanup);
1048 ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1054 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
1056 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1059 cfg = get_feature_ds(chan);
1061 cfg = ao2_global_obj_ref(globals);
1068 ast_assert(cfg->global && cfg->global->general);
1070 ao2_ref(cfg->global->general, +1);
1071 return cfg->global->general;
1074 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
1076 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1079 cfg = get_feature_ds(chan);
1081 cfg = ao2_global_obj_ref(globals);
1088 ast_assert(cfg->global && cfg->global->xfer);
1090 ao2_ref(cfg->global->xfer, +1);
1091 return cfg->global->xfer;
1094 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
1096 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1099 cfg = get_feature_ds(chan);
1101 cfg = ao2_global_obj_ref(globals);
1108 ast_assert(cfg->global && cfg->global->pickup);
1110 ao2_ref(cfg->global->pickup, +1);
1111 return cfg->global->pickup;
1114 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
1116 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1119 cfg = get_feature_ds(chan);
1121 cfg = ao2_global_obj_ref(globals);
1128 ast_assert(cfg->featuremap != NULL);
1130 ao2_ref(cfg->featuremap, +1);
1131 return cfg->featuremap;
1134 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1136 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1139 cfg = get_feature_ds(chan);
1141 cfg = ao2_global_obj_ref(globals);
1148 return featuremap_get(cfg->featuremap, feature, buf, len);
1151 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1153 RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1154 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1156 if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1160 /* Dang, must be in the application map */
1161 applicationmap = ast_get_chan_applicationmap(chan);
1162 if (!applicationmap) {
1166 item = ao2_find(applicationmap, feature, OBJ_KEY);
1171 ast_copy_string(buf, item->dtmf, len);
1175 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
1176 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1177 unsigned int activate_on_self)
1179 struct ast_applicationmap_item *item;
1181 item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1183 if (!item || ast_string_field_init(item, 64)) {
1187 ast_string_field_set(item, name, name);
1188 ast_string_field_set(item, app, app);
1189 ast_string_field_set(item, app_data, app_data);
1190 ast_string_field_set(item, moh_class, moh_class);
1191 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1192 item->activate_on_self = activate_on_self;
1197 static int add_item(void *obj, void *arg, int flags)
1199 struct featuregroup_item *fg_item = obj;
1200 struct ao2_container *applicationmap = arg;
1201 RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1203 /* If there's no DTMF override, then we can just link
1204 * the applicationmap item directly. Otherwise, we need
1205 * to create a copy with the DTMF override in place and
1208 if (ast_strlen_zero(fg_item->dtmf_override)) {
1209 ao2_ref(fg_item->appmap_item, +1);
1210 appmap_item = fg_item->appmap_item;
1212 appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1213 fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1214 fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1215 fg_item->appmap_item->activate_on_self);
1222 ao2_link(applicationmap, appmap_item);
1226 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
1228 RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1229 struct ao2_container *applicationmap;
1238 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1241 ao2_ref(cfg->applicationmap, +1);
1242 return cfg->applicationmap;
1245 group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1246 if (ast_strlen_zero(group_names)) {
1250 applicationmap = applicationmap_alloc(0);
1251 if (!applicationmap) {
1255 while ((name = strsep(&group_names, "#"))) {
1256 RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1259 RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1262 ao2_link(applicationmap, item);
1264 ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1265 name, ast_channel_name(chan));
1268 ao2_callback(group->items, 0, add_item, applicationmap);
1272 if (ao2_container_count(applicationmap) == 0) {
1273 ao2_cleanup(applicationmap);
1277 return applicationmap;
1280 static int applicationmap_handler(const struct aco_option *opt,
1281 struct ast_variable *var, void *obj)
1283 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1284 struct ao2_container *applicationmap = obj;
1285 AST_DECLARE_APP_ARGS(args,
1287 AST_APP_ARG(activate_on);
1289 AST_APP_ARG(app_data);
1290 AST_APP_ARG(moh_class);
1292 char *parse = ast_strdupa(var->value);
1295 unsigned int activate_on_self;
1297 AST_STANDARD_APP_ARGS(args, parse);
1299 if (ast_strlen_zero(args.dtmf) ||
1300 ast_strlen_zero(args.activate_on) ||
1301 ast_strlen_zero(args.app)) {
1302 ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1306 /* features.conf used to have an "activated_by" portion
1307 * in addition to activate_on. Get rid of whatever may be
1310 slash = strchr(args.activate_on, '/');
1315 /* Two syntaxes allowed for applicationmap:
1316 * Old: foo = *1,self,NoOp,Boo!,default
1317 * New: foo = *1,self,NoOp(Boo!),default
1319 * We need to handle both
1321 paren = strchr(args.app, '(');
1326 args.moh_class = args.app_data;
1328 close_paren = strrchr(paren, ')');
1330 *close_paren = '\0';
1332 args.app_data = paren;
1334 /* Re-check that the application is not empty */
1335 if (ast_strlen_zero(args.app)) {
1336 ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1339 } else if (strchr(args.app_data, '"')) {
1340 args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1343 /* Allow caller and callee to be specified for backwards compatibility */
1344 if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1345 activate_on_self = 1;
1346 } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1347 activate_on_self = 0;
1349 ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1350 args.activate_on, var->name);
1354 ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1355 args.dtmf, args.app, args.app_data, args.moh_class);
1357 item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1358 args.moh_class, args.dtmf, activate_on_self);
1364 if (!ao2_link(applicationmap, item)) {
1371 static int featuregroup_handler(const struct aco_option *opt,
1372 struct ast_variable *var, void *obj)
1374 RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
1375 struct featuregroup *group = obj;
1377 item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
1378 if (!item || ast_string_field_init(item, 32)) {
1382 ast_string_field_set(item, appmap_item_name, var->name);
1383 ast_string_field_set(item, dtmf_override, var->value);
1385 if (!ao2_link(group->items, item)) {
1389 /* We wait to look up the application map item in the preapply callback */
1394 static int general_handler(const struct aco_option *opt,
1395 struct ast_variable *var, void *obj)
1397 struct features_global_config *global = obj;
1398 struct ast_features_general_config *general = global->general;
1400 return general_set(general, var->name, var->value);
1403 static int xfer_handler(const struct aco_option *opt,
1404 struct ast_variable *var, void *obj)
1406 struct features_global_config *global = obj;
1407 struct ast_features_xfer_config *xfer = global->xfer;
1409 return xfer_set(xfer, var->name, var->value);
1412 static int pickup_handler(const struct aco_option *opt,
1413 struct ast_variable *var, void *obj)
1415 struct features_global_config *global = obj;
1416 struct ast_features_pickup_config *pickup = global->pickup;
1418 return pickup_set(pickup, var->name, var->value);
1421 static int unsupported_handler(const struct aco_option *opt,
1422 struct ast_variable *var, void *obj)
1424 ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1428 static int featuremap_handler(const struct aco_option *opt,
1429 struct ast_variable *var, void *obj)
1431 struct ast_featuremap_config *featuremap = obj;
1433 return featuremap_set(featuremap, var->name, var->value);
1436 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1438 struct ast_applicationmap_item *appmap_item;
1439 struct featuregroup_item *fg_item = obj;
1441 struct ao2_container *applicationmap = data;
1443 appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1449 fg_item->appmap_item = appmap_item;
1454 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1456 struct featuregroup *group = obj;
1459 ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1462 ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1466 return *err ? CMP_STOP : 0;
1469 static int features_pre_apply_config(void);
1471 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1472 .files = ACO_FILES(&features_conf),
1473 .pre_apply_config = features_pre_apply_config,
1476 static int features_pre_apply_config(void)
1478 struct features_config *cfg = aco_pending_config(&cfg_info);
1481 /* Now that the entire config has been processed, we can check that the featuregroup
1482 * items refer to actual applicationmap items.
1485 ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
1490 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1491 char *buf, size_t len)
1494 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1495 SCOPED_CHANNELLOCK(lock, chan);
1497 if (!strcasecmp(data, "inherit")) {
1498 struct ast_datastore *ds = get_feature_chan_ds(chan);
1499 unsigned int inherit = ds ? ds->inheritance : 0;
1501 snprintf(buf, len, "%s", inherit ? "yes" : "no");
1505 cfg = get_feature_ds(chan);
1510 res = general_get(cfg->global->general, data, buf, len) &&
1511 xfer_get(cfg->global->xfer, data, buf, len) &&
1512 pickup_get(cfg->global->pickup, data, buf, len);
1515 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1521 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1525 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1526 SCOPED_CHANNELLOCK(lock, chan);
1528 if (!strcasecmp(data, "inherit")) {
1529 struct ast_datastore *ds = get_feature_chan_ds(chan);
1531 ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1536 if (!(cfg = get_feature_ds(chan))) {
1540 res = general_set(cfg->global->general, data, value) &&
1541 xfer_set(cfg->global->xfer, data, value) &&
1542 pickup_set(cfg->global->pickup, data, value);
1545 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1551 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1552 char *buf, size_t len)
1555 SCOPED_CHANNELLOCK(lock, chan);
1557 res = ast_get_builtin_feature(chan, data, buf, len);
1560 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1566 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1570 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1571 SCOPED_CHANNELLOCK(lock, chan);
1573 if (!(cfg = get_feature_ds(chan))) {
1577 res = featuremap_set(cfg->featuremap, data, value);
1579 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1586 static struct ast_custom_function feature_function = {
1588 .read = feature_read,
1589 .write = feature_write
1592 static struct ast_custom_function featuremap_function = {
1593 .name = "FEATUREMAP",
1594 .read = featuremap_read,
1595 .write = featuremap_write
1598 static int load_config(void)
1600 if (aco_info_init(&cfg_info)) {
1601 ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1605 aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1606 __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
1607 aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1608 DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
1609 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1610 DEFAULT_COURTESY_TONE, general_handler, 0);
1612 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1613 __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
1614 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1615 __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
1616 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1617 __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
1618 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1619 __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
1620 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1621 __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
1622 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1623 DEFAULT_XFERSOUND, xfer_handler, 0);
1624 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1625 DEFAULT_XFERFAILSOUND, xfer_handler, 0);
1626 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1627 DEFAULT_ATXFER_ABORT, xfer_handler, 0);
1628 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1629 DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
1630 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1631 DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
1633 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1634 DEFAULT_PICKUPEXTEN, pickup_handler, 0);
1635 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1636 DEFAULT_PICKUPSOUND, pickup_handler, 0);
1637 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1638 DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
1640 aco_option_register_custom(&cfg_info, "context", ACO_EXACT, global_options,
1641 "", unsupported_handler, 0);
1642 aco_option_register_custom(&cfg_info, "parkext", ACO_EXACT, global_options,
1643 "", unsupported_handler, 0);
1644 aco_option_register_custom(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1645 "", unsupported_handler, 0);
1646 aco_option_register_custom(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1647 "", unsupported_handler, 0);
1648 aco_option_register_custom(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1649 "", unsupported_handler, 0);
1650 aco_option_register_custom(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1651 "", unsupported_handler, 0);
1652 aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, global_options,
1653 "", unsupported_handler, 0);
1654 aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, global_options,
1655 "", unsupported_handler, 0);
1656 aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1657 "", unsupported_handler, 0);
1658 aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1659 "", unsupported_handler, 0);
1660 aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1661 "", unsupported_handler, 0);
1662 aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1663 "", unsupported_handler, 0);
1664 aco_option_register_custom(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1665 "", unsupported_handler, 0);
1666 aco_option_register_custom(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1667 "", unsupported_handler, 0);
1668 aco_option_register_custom(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1669 "", unsupported_handler, 0);
1670 aco_option_register_custom(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1671 "", unsupported_handler, 0);
1672 aco_option_register_custom(&cfg_info, "adsipark", ACO_EXACT, global_options,
1673 "", unsupported_handler, 0);
1675 aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1676 DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
1677 aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1678 DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
1679 aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1680 DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
1681 aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1682 DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
1683 aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1684 DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
1685 aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1686 DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
1688 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
1689 "", applicationmap_handler, 0);
1691 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
1692 "", featuregroup_handler, 0);
1694 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1695 ast_log(LOG_ERROR, "Failed to process features.conf configuration!\n");
1696 aco_info_destroy(&cfg_info);
1697 ao2_global_obj_release(globals);
1704 static int print_featuregroup(void *obj, void *arg, int flags)
1706 struct featuregroup_item *item = obj;
1707 struct ast_cli_args *a = arg;
1709 ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1710 S_OR(item->dtmf_override, item->appmap_item->dtmf));
1715 static int print_featuregroups(void *obj, void *arg, int flags)
1717 struct featuregroup *group = obj;
1718 struct ast_cli_args *a = arg;
1720 ast_cli(a->fd, "===> Group: %s\n", group->name);
1722 ao2_callback(group->items, 0, print_featuregroup, a);
1726 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1728 static int print_applicationmap(void *obj, void *arg, int flags)
1730 struct ast_applicationmap_item *item = obj;
1731 struct ast_cli_args *a = arg;
1733 ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1738 * \brief CLI command to list configured features
1743 * \retval CLI_SUCCESS on success.
1744 * \retval NULL when tab completion is used.
1746 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1748 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1753 e->command = "features show";
1755 "Usage: features show\n"
1756 " Lists configured features\n";
1762 cfg = ao2_global_obj_ref(globals);
1764 ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1765 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1767 ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1768 ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1769 ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1770 ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1771 ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1772 ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1773 ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1775 ast_cli(a->fd, "\n");
1776 ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1777 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1778 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1779 ast_cli(a->fd, "(none)\n");
1781 ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1784 ast_cli(a->fd, "\nFeature Groups:\n");
1785 ast_cli(a->fd, "---------------\n");
1786 if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1787 ast_cli(a->fd, "(none)\n");
1789 ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1792 ast_cli(a->fd, "\n");
1797 static struct ast_cli_entry cli_features_config[] = {
1798 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1801 void ast_features_config_shutdown(void)
1803 ast_custom_function_unregister(&featuremap_function);
1804 ast_custom_function_unregister(&feature_function);
1805 ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1806 aco_info_destroy(&cfg_info);
1807 ao2_global_obj_release(globals);
1810 int ast_features_config_reload(void)
1812 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1818 int ast_features_config_init(void)
1822 res = load_config();
1823 res |= __ast_custom_function_register(&feature_function, NULL);
1824 res |= __ast_custom_function_register(&featuremap_function, NULL);
1825 res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1828 ast_features_config_shutdown();