2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Mark Michelson <mmichelson@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 #include "asterisk/features_config.h"
22 #include "asterisk/config_options.h"
23 #include "asterisk/datastore.h"
24 #include "asterisk/channel.h"
25 #include "asterisk/pbx.h"
26 #include "asterisk/app.h"
27 #include "asterisk/cli.h"
30 <configInfo name="features" language="en_US">
31 <synopsis>Features Configuration</synopsis>
32 <configFile name="features.conf">
33 <configObject name="globals">
36 <configOption name="featuredigittimeout" default="1000">
37 <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
39 <configOption name="courtesytone">
40 <synopsis>Sound to play when automon or automixmon is activated</synopsis>
42 <configOption name="recordingfailsound">
43 <synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
45 <configOption name="transferdigittimeout" default="3">
46 <synopsis>Seconds allowed between digit presses when dialing a transfer destination</synopsis>
48 <configOption name="atxfernoanswertimeout" default="15">
49 <synopsis>Seconds to wait for attended transfer destination to answer</synopsis>
51 <configOption name="atxferdropcall" default="no">
52 <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
54 <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
55 re-call the transferrer if the call to the transfer target fails. If the call to the
56 transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
57 milliseconds and then attempt to dial the transfer target again. This process will
58 repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
59 the transferrer have occurred.</para>
60 <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
61 to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
62 hang up all channels involved in the transfer.</para>
65 <configOption name="atxferloopdelay" default="10">
66 <synopsis>Seconds to wait between attempts to re-dial transfer destination</synopsis>
67 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
69 <configOption name="atxfercallbackretries" default="2">
70 <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
71 <see-also><ref type="configOption">atxferdropcall</ref></see-also>
73 <configOption name="xfersound" default="beep">
74 <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
76 <para>This sound will play to the transferrer and transfer target channels when
77 an attended transfer completes. This sound is also played to channels when performing
78 an AMI <literal>Bridge</literal> action.</para>
81 <configOption name="xferfailsound" default="beeperr">
82 <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
84 <configOption name="atxferabort" default="*1">
85 <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
87 <para>This option is only available to the transferrer during an attended
88 transfer operation. Aborting a transfer results in the transfer being cancelled and
89 the original parties in the call being re-bridged.</para>
92 <configOption name="atxfercomplete" default="*2">
93 <synopsis>Digits to dial to complete an attended transfer</synopsis>
95 <para>This option is only available to the transferrer during an attended
96 transfer operation. Completing the transfer with a DTMF sequence is functionally
97 equivalent to hanging up the transferrer channel during an attended transfer. The
98 result is that the transfer target and transferees are bridged.</para>
101 <configOption name="atxferthreeway" default="*3">
102 <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
104 <para>This option is only available to the transferrer during an attended
105 transfer operation. Pressing this DTMF sequence will result in the transferrer,
106 the transferees, and the transfer target all being in a single bridge together.</para>
109 <configOption name="atxferswap" default="*4">
110 <synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
112 <para>This option is only available to the transferrer during an attended
113 transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
114 which party he is bridged with. For instance, if the transferrer is currently bridged with
115 the transfer target, then pressing this DTMF sequence will cause the transferrer to be
116 bridged with the transferees.</para>
119 <configOption name="pickupexten" default="*8">
120 <synopsis>Digits used for picking up ringing calls</synopsis>
122 <para>In order for the pickup attempt to be successful, the party attempting to
123 pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
124 common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
125 have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
126 <replaceable>callgroup</replaceable>.</para>
129 <configOption name="pickupsound">
130 <synopsis>Sound to play to picker when a call is picked up</synopsis>
132 <configOption name="pickupfailsound">
133 <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
136 <configObject name="featuremap">
137 <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
138 <configOption name="atxfer">
139 <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
141 <para>The transferee parties will be placed on hold and the
142 transferrer may dial an extension to reach a transfer target. During an
143 attended transfer, the transferrer may consult with the transfer target
144 before completing the transfer. Once the transferrer has hung up or pressed
145 the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
146 and transfer target will be bridged.</para>
149 <configOption name="blindxfer" default="#">
150 <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
152 <para>The transferee parties will be placed on hold and the
153 transferrer may dial an extension to reach a transfer target. During a
154 blind transfer, as soon as the transfer target is dialed, the transferrer
158 <configOption name="disconnect" default="*">
159 <synopsis>DTMF sequence to disconnect the current call</synopsis>
161 <para>Entering this DTMF sequence will cause the bridge to end, no
162 matter the number of parties present</para>
165 <configOption name="parkcall">
166 <synopsis>DTMF sequence to park a call</synopsis>
168 <para>The parking lot used to park the call is determined by using either the
169 <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
170 the channel (provided by the channel driver) if the variable is not present. If
171 no configured value on the channel is present, then <literal>"default"</literal>
172 is used. The call is parked in the next available space in the parking lot.</para>
175 <configOption name="automon">
176 <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
178 <para>This will cause the channel that pressed the DTMF sequence
179 to be monitored by the <literal>Monitor</literal> application. The
180 format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
181 channel variable. If this variable is not specified, then <literal>wav</literal> is the
182 default. The filename is constructed in the following manner:</para>
184 <para> prefix-timestamp-filename</para>
186 <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
187 channel variable or <literal>auto</literal> if the variable is not set. The timestamp
188 is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
189 channel variable or the callerID of the channels if the variable is not set.</para>
192 <configOption name="automixmon">
193 <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
195 <para>Operation of the automixmon is similar to the <literal> automon </literal>
196 feature, with the following exceptions:
197 <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
198 <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
199 There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
201 <see-also><ref type="configOption">automon</ref></see-also>
204 <configObject name="applicationmap">
205 <synopsis>Section for defining custom feature invocations during a call</synopsis>
207 <para>The applicationmap is an area where new custom features can be created. Items
208 defined in the applicationmap are not automatically accessible to bridged parties. Access
209 to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
210 The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
211 either applicationmap item names or featuregroup names.</para>
213 <configOption name="^.*$" regex="true">
214 <synopsis>A custom feature to invoke during a bridged call</synopsis>
216 <para>Each item listed here is a comma-separated list of parameters that determine
217 how a feature may be invoked during a call</para>
218 <para> Example:</para>
219 <para> eggs = *5,self,Playback(hello-world),default</para>
220 <para>This would create a feature called <literal>eggs</literal> that could be invoked
221 during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
222 sequence would then trigger the <literal>Playback</literal> application to play the
223 <literal>hello-world</literal> file. The application invocation would happen on the
224 party that pressed the DTMF sequence since <literal>self</literal> is specified. The
225 other parties in the bridge would hear the <literal>default</literal> music on hold
226 class during the playback.</para>
227 <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
228 is also allowed. The following applicationmap lines are functionally identical:</para>
229 <para> eggs = *5,self,Playback(hello-world),default</para>
230 <para> eggs = *5,self,Playback,hello-world,default</para>
231 <para> eggs = *5,self,Playback,"hello-world",default</para>
234 <parameter name="dtmf" required="true">
235 <para>The DTMF sequence used to trigger the option</para>
237 <parameter name="activate_on" required="true">
238 <para>The party that the feature will be invoked on</para>
240 <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
241 <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
244 <parameter name="app" required="true">
245 <para>The dialplan application to run when the DTMF sequence is pressed</para>
246 <argument name="app_args" required="false">
247 <para>The arguments to the dialplan application to run</para>
250 <parameter name="moh_class" required="false">
251 <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
256 <configObject name="featuregroup">
257 <synopsis>Groupings of items from the applicationmap</synopsis>
259 <para>Feature groups allow for multiple applicationmap items to be
260 grouped together. Like with individual applicationmap items, feature groups
261 can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
262 In addition to creating groupings, the feature group section allows for the
263 DTMF sequence used to invoke an applicationmap item to be overridden with
264 a different sequence.</para>
266 <configOption name="^.*$" regex="true">
267 <synopsis>Applicationmap item to place in the feature group</synopsis>
269 <para>Each item here must be a name of an item in the applicationmap. The
270 argument may either be a new DTMF sequence to use for the item or it
271 may be left blank in order to use the DTMF sequence specified in the
272 applicationmap. For example:</para>
273 <para> eggs => *1</para>
274 <para> bacon =></para>
275 <para>would result in the applicationmap items <literal>eggs</literal> and
276 <literal>bacon</literal> being in the featuregroup. The former would have its
277 default DTMF trigger overridden with <literal>*1</literal> and the latter would
278 have the DTMF value specified in the applicationmap.</para>
284 <function name="FEATURE" language="en_US">
286 Get or set a feature option on a channel.
289 <parameter name="option_name" required="true">
290 <para>The allowed values are:</para>
292 <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
293 <enum name="featuredigittimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='featuredigittimeout']/synopsis/text())" /></para></enum>
294 <enum name="transferdigittimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferdigittimeout']/synopsis/text())" /></para></enum>
295 <enum name="atxfernoanswertimeout"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfernoanswertimeout']/synopsis/text())" /></para></enum>
296 <enum name="atxferdropcall"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferdropcall']/synopsis/text())" /></para></enum>
297 <enum name="atxferloopdelay"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferloopdelay']/synopsis/text())" /></para></enum>
298 <enum name="atxfercallbackretries"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfercallbackretries']/synopsis/text())" /></para></enum>
299 <enum name="xfersound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='xfersound']/synopsis/text())" /></para></enum>
300 <enum name="xferfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='xferfailsound']/synopsis/text())" /></para></enum>
301 <enum name="atxferabort"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferabort']/synopsis/text())" /></para></enum>
302 <enum name="atxfercomplete"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxfercomplete']/synopsis/text())" /></para></enum>
303 <enum name="atxferthreeway"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='atxferthreeway']/synopsis/text())" /></para></enum>
304 <enum name="pickupexten"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupexten']/synopsis/text())" /></para></enum>
305 <enum name="pickupsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupsound']/synopsis/text())" /></para></enum>
306 <enum name="pickupfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='pickupfailsound']/synopsis/text())" /></para></enum>
307 <enum name="courtesytone"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='courtesytone']/synopsis/text())" /></para></enum>
308 <enum name="recordingfailsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='recordingfailsound']/synopsis/text())" /></para></enum>
313 <para>When this function is used as a read, it will get the current
314 value of the specified feature option for this channel. It will be
315 the value of this option configured in features.conf if a channel specific
316 value has not been set. This function can also be used to set a channel
317 specific value for the supported feature options.</para>
320 <ref type="function">FEATUREMAP</ref>
323 <function name="FEATUREMAP" language="en_US">
325 Get or set a feature map to a given value on a specific channel.
328 <parameter name="feature_name" required="true">
329 <para>The allowed values are:</para>
331 <enum name="atxfer"><para>Attended Transfer</para></enum>
332 <enum name="blindxfer"><para>Blind Transfer</para></enum>
333 <enum name="automon"><para>Auto Monitor</para></enum>
334 <enum name="disconnect"><para>Call Disconnect</para></enum>
335 <enum name="parkcall"><para>Park Call</para></enum>
336 <enum name="automixmon"><para>Auto MixMonitor</para></enum>
341 <para>When this function is used as a read, it will get the current
342 digit sequence mapped to the specified feature for this channel. This
343 value will be the one configured in features.conf if a channel specific
344 value has not been set. This function can also be used to set a channel
345 specific value for a feature mapping.</para>
348 <ref type="function">FEATURE</ref>
352 /*! Default general options */
353 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
354 #define DEFAULT_COURTESY_TONE ""
355 #define DEFAULT_RECORDING_FAIL_SOUND ""
357 /*! Default xfer options */
358 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3
359 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15
360 #define DEFAULT_ATXFER_DROP_CALL 0
361 #define DEFAULT_ATXFER_LOOP_DELAY 10
362 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
363 #define DEFAULT_XFERSOUND "beep"
364 #define DEFAULT_XFERFAILSOUND "beeperr"
365 #define DEFAULT_ATXFER_ABORT "*1"
366 #define DEFAULT_ATXFER_COMPLETE "*2"
367 #define DEFAULT_ATXFER_THREEWAY "*3"
368 #define DEFAULT_ATXFER_SWAP "*4"
370 /*! Default pickup options */
371 #define DEFAULT_PICKUPEXTEN "*8"
372 #define DEFAULT_PICKUPSOUND ""
373 #define DEFAULT_PICKUPFAILSOUND ""
375 /*! Default featuremap options */
376 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
377 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
378 #define DEFAULT_FEATUREMAP_AUTOMON ""
379 #define DEFAULT_FEATUREMAP_ATXFER ""
380 #define DEFAULT_FEATUREMAP_PARKCALL ""
381 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
384 * \brief Configuration from the "general" section of features.conf
386 struct features_global_config {
387 struct ast_features_general_config *general;
388 struct ast_features_xfer_config *xfer;
389 struct ast_features_pickup_config *pickup;
392 static void ast_applicationmap_item_destructor(void *obj)
394 struct ast_applicationmap_item *item = obj;
396 ast_string_field_free_memory(item);
399 static int applicationmap_sort(const void *obj, const void *arg, int flags)
401 const struct ast_applicationmap_item *item1 = obj;
402 const struct ast_applicationmap_item *item2;
405 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
408 return strcasecmp(item1->name, key2);
409 case OBJ_PARTIAL_KEY:
411 return strncasecmp(item1->name, key2, strlen(key2));
415 return strcasecmp(item1->name, item2->name);
420 * \brief Entry in the container of featuregroups
422 struct featuregroup_item {
423 AST_DECLARE_STRING_FIELDS(
424 /*! The name of the applicationmap item that we are referring to */
425 AST_STRING_FIELD(appmap_item_name);
426 /*! Custom DTMF override to use instead of the default for the applicationmap item */
427 AST_STRING_FIELD(dtmf_override);
429 /*! The applicationmap item that is being referred to */
430 struct ast_applicationmap_item *appmap_item;
433 static void featuregroup_item_destructor(void *obj)
435 struct featuregroup_item *item = obj;
437 ast_string_field_free_memory(item);
438 ao2_cleanup(item->appmap_item);
441 static int group_item_sort(const void *obj, const void *arg, int flags)
443 const struct featuregroup_item *item1 = obj;
444 const struct featuregroup_item *item2;
447 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
450 return strcasecmp(item1->appmap_item_name, key2);
451 case OBJ_PARTIAL_KEY:
453 return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
456 return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
463 * \brief Featuregroup representation
465 struct featuregroup {
466 /*! The name of the featuregroup */
468 /*! A container of featuregroup_items */
469 struct ao2_container *items;
472 static int featuregroup_hash(const void *obj, int flags)
474 const struct featuregroup *group;
477 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
480 return ast_str_case_hash(key);
481 case OBJ_PARTIAL_KEY:
487 return ast_str_case_hash(group->name);
491 static int featuregroup_cmp(void *obj, void *arg, int flags)
493 struct featuregroup *group1 = obj;
494 struct featuregroup *group2;
497 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
500 return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
501 case OBJ_PARTIAL_KEY:
503 return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
506 return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
512 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
514 return ao2_find(group_container, category, OBJ_KEY);
517 static void featuregroup_destructor(void *obj)
519 struct featuregroup *group = obj;
521 ast_free((char *) group->name);
522 ao2_cleanup(group->items);
525 static void *featuregroup_alloc(const char *cat)
527 struct featuregroup *group;
529 group = ao2_alloc(sizeof(*group), featuregroup_destructor);
534 group->name = ast_strdup(cat);
540 group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
541 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
550 /* Used for deprecated parking configuration */
551 struct dummy_config {
555 struct features_config {
556 struct features_global_config *global;
557 struct ast_featuremap_config *featuremap;
558 struct dummy_config *parkinglots;
559 struct ao2_container *applicationmap;
560 struct ao2_container *featuregroups;
563 static struct aco_type global_option = {
566 .category_match = ACO_WHITELIST,
567 .category = "^general$",
568 .item_offset = offsetof(struct features_config, global),
571 static struct aco_type featuremap_option = {
573 .name = "featuremap",
574 .category_match = ACO_WHITELIST,
575 .category = "^featuremap$",
576 .item_offset = offsetof(struct features_config, featuremap),
579 static struct aco_type applicationmap_option = {
581 .name = "applicationmap",
582 .category_match = ACO_WHITELIST,
583 .category = "^applicationmap$",
584 .item_offset = offsetof(struct features_config, applicationmap),
587 static struct aco_type featuregroup_option = {
589 .name = "featuregroup",
590 .category_match = ACO_BLACKLIST,
591 .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
592 .item_offset = offsetof(struct features_config, featuregroups),
593 .item_alloc = featuregroup_alloc,
594 .item_find = featuregroup_find,
597 static struct aco_type parkinglot_option = {
599 .name = "parkinglot",
600 .category_match = ACO_WHITELIST,
601 .category = "^parkinglot_.*$",
602 .item_offset = offsetof(struct features_config, parkinglots),
606 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
607 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
608 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
609 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
610 static struct aco_type *parkinglot_options[] = ACO_TYPES(&parkinglot_option);
612 static struct aco_file features_conf = {
613 .filename = "features.conf",
614 .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option, &parkinglot_option),
617 AO2_GLOBAL_OBJ_STATIC(globals);
619 static void features_config_destructor(void *obj)
621 struct features_config *cfg = obj;
623 ao2_cleanup(cfg->global);
624 ao2_cleanup(cfg->featuremap);
625 ao2_cleanup(cfg->applicationmap);
626 ao2_cleanup(cfg->featuregroups);
629 static void featuremap_config_destructor(void *obj)
631 struct ast_featuremap_config *cfg = obj;
633 ast_string_field_free_memory(cfg);
636 static void global_config_destructor(void *obj)
638 struct features_global_config *cfg = obj;
640 ao2_cleanup(cfg->general);
641 ao2_cleanup(cfg->xfer);
642 ao2_cleanup(cfg->pickup);
645 static void general_destructor(void *obj)
647 struct ast_features_general_config *cfg = obj;
649 ast_string_field_free_memory(cfg);
652 static void xfer_destructor(void *obj)
654 struct ast_features_xfer_config *cfg = obj;
656 ast_string_field_free_memory(cfg);
659 static void pickup_destructor(void *obj)
661 struct ast_features_pickup_config *cfg = obj;
663 ast_string_field_free_memory(cfg);
666 static struct features_global_config *global_config_alloc(void)
668 RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
670 cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
675 cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
676 if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
680 cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
681 if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
685 cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
686 if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
694 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
696 return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
697 replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
698 applicationmap_sort, NULL);
703 * \brief Allocate the major configuration structure
705 * The parameter is used to determine if the applicationmap and featuregroup
706 * structures should be allocated. We only want to allocate these structures for
707 * the global features_config structure. For the datastores on channels, we don't
708 * need to allocate these structures because they are not used.
710 * \param allocate_applicationmap See previous explanation
711 * \retval NULL Failed to alloate configuration
712 * \retval non-NULL Allocated configuration
714 static struct features_config *__features_config_alloc(int allocate_applicationmap)
716 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
718 cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
723 cfg->global = global_config_alloc();;
728 cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
729 if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
733 cfg->parkinglots = ao2_alloc(sizeof(*cfg->parkinglots), NULL);
734 if (!cfg->parkinglots) {
738 if (allocate_applicationmap) {
739 cfg->applicationmap = applicationmap_alloc(1);
740 if (!cfg->applicationmap) {
744 cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
746 if (!cfg->featuregroups) {
756 static void *features_config_alloc(void)
758 return __features_config_alloc(1);
761 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
763 ast_string_fields_copy(dest, src);
764 dest->featuredigittimeout = src->featuredigittimeout;
767 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
769 ast_string_fields_copy(dest, src);
770 dest->transferdigittimeout = src->transferdigittimeout;
771 dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
772 dest->atxferloopdelay = src->atxferloopdelay;
773 dest->atxfercallbackretries = src->atxfercallbackretries;
774 dest->atxferdropcall = src->atxferdropcall;
777 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
779 ast_string_fields_copy(dest, src);
782 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
784 general_copy(dest->general, src->general);
785 xfer_copy(dest->xfer, src->xfer);
786 pickup_copy(dest->pickup, src->pickup);
789 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
791 ast_string_fields_copy(dest, src);
794 static void features_copy(struct features_config *dest, const struct features_config *src)
796 global_copy(dest->global, src->global);
797 featuremap_copy(dest->featuremap, src->featuremap);
799 /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
800 * is produced on the fly when ast_get_chan_applicationmap() is called
804 static struct features_config *features_config_dup(const struct features_config *orig)
806 struct features_config *dup;
808 dup = __features_config_alloc(0);
813 features_copy(dup, orig);
818 static int general_set(struct ast_features_general_config *general, const char *name,
823 if (!strcasecmp(name, "featuredigittimeout")) {
824 res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
825 } else if (!strcasecmp(name, "courtesytone")) {
826 ast_string_field_set(general, courtesytone, value);
827 } else if (!strcasecmp(name, "recordingfailsound")) {
828 ast_string_field_set(general, recordingfailsound, value);
830 /* Unrecognized option */
837 static int general_get(struct ast_features_general_config *general, const char *field,
838 char *buf, size_t len)
842 if (!strcasecmp(field, "featuredigittimeout")) {
843 snprintf(buf, len, "%u", general->featuredigittimeout);
844 } else if (!strcasecmp(field, "courtesytone")) {
845 ast_copy_string(buf, general->courtesytone, len);
846 } else if (!strcasecmp(field, "recordingfailsound")) {
847 ast_copy_string(buf, general->recordingfailsound, len);
849 /* Unrecognized option */
856 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
861 if (!strcasecmp(name, "transferdigittimeout")) {
862 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
863 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
864 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
865 } else if (!strcasecmp(name, "atxferloopdelay")) {
866 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
867 } else if (!strcasecmp(name, "atxfercallbackretries")) {
868 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
869 } else if (!strcasecmp(name, "atxferdropcall")) {
870 xfer->atxferdropcall = ast_true(value);
871 } else if (!strcasecmp(name, "xfersound")) {
872 ast_string_field_set(xfer, xfersound, value);
873 } else if (!strcasecmp(name, "xferfailsound")) {
874 ast_string_field_set(xfer, xferfailsound, value);
875 } else if (!strcasecmp(name, "atxferabort")) {
876 ast_string_field_set(xfer, atxferabort, value);
877 } else if (!strcasecmp(name, "atxfercomplete")) {
878 ast_string_field_set(xfer, atxfercomplete, value);
879 } else if (!strcasecmp(name, "atxferthreeway")) {
880 ast_string_field_set(xfer, atxferthreeway, value);
881 } else if (!strcasecmp(name, "atxferswap")) {
882 ast_string_field_set(xfer, atxferswap, value);
884 /* Unrecognized option */
891 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
892 char *buf, size_t len)
896 if (!strcasecmp(field, "transferdigittimeout")) {
897 snprintf(buf, len, "%u", xfer->transferdigittimeout);
898 } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
899 snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
900 } else if (!strcasecmp(field, "atxferloopdelay")) {
901 snprintf(buf, len, "%u", xfer->atxferloopdelay);
902 } else if (!strcasecmp(field, "atxfercallbackretries")) {
903 snprintf(buf, len, "%u", xfer->atxfercallbackretries);
904 } else if (!strcasecmp(field, "atxferdropcall")) {
905 snprintf(buf, len, "%u", xfer->atxferdropcall);
906 } else if (!strcasecmp(field, "xfersound")) {
907 ast_copy_string(buf, xfer->xfersound, len);
908 } else if (!strcasecmp(field, "xferfailsound")) {
909 ast_copy_string(buf, xfer->xferfailsound, len);
910 } else if (!strcasecmp(field, "atxferabort")) {
911 ast_copy_string(buf, xfer->atxferabort, len);
912 } else if (!strcasecmp(field, "atxfercomplete")) {
913 ast_copy_string(buf, xfer->atxfercomplete, len);
914 } else if (!strcasecmp(field, "atxferthreeway")) {
915 ast_copy_string(buf, xfer->atxferthreeway, len);
916 } else if (!strcasecmp(field, "atxferswap")) {
917 ast_copy_string(buf, xfer->atxferswap, len);
919 /* Unrecognized option */
926 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
931 if (!strcasecmp(name, "pickupsound")) {
932 ast_string_field_set(pickup, pickupsound, value);
933 } else if (!strcasecmp(name, "pickupfailsound")) {
934 ast_string_field_set(pickup, pickupfailsound, value);
935 } else if (!strcasecmp(name, "pickupexten")) {
936 ast_string_field_set(pickup, pickupexten, value);
938 /* Unrecognized option */
945 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
946 char *buf, size_t len)
950 if (!strcasecmp(field, "pickupsound")) {
951 ast_copy_string(buf, pickup->pickupsound, len);
952 } else if (!strcasecmp(field, "pickupfailsound")) {
953 ast_copy_string(buf, pickup->pickupfailsound, len);
954 } else if (!strcasecmp(field, "pickupexten")) {
955 ast_copy_string(buf, pickup->pickupexten, len);
957 /* Unrecognized option */
964 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
969 if (!strcasecmp(name, "blindxfer")) {
970 ast_string_field_set(featuremap, blindxfer, value);
971 } else if (!strcasecmp(name, "disconnect")) {
972 ast_string_field_set(featuremap, disconnect, value);
973 } else if (!strcasecmp(name, "automon")) {
974 ast_string_field_set(featuremap, automon, value);
975 } else if (!strcasecmp(name, "atxfer")) {
976 ast_string_field_set(featuremap, atxfer, value);
977 } else if (!strcasecmp(name, "automixmon")) {
978 ast_string_field_set(featuremap, automixmon, value);
979 } else if (!strcasecmp(name, "parkcall")) {
980 ast_string_field_set(featuremap, parkcall, value);
982 /* Unrecognized option */
989 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
990 char *buf, size_t len)
994 if (!strcasecmp(field, "blindxfer")) {
995 ast_copy_string(buf, featuremap->blindxfer, len);
996 } else if (!strcasecmp(field, "disconnect")) {
997 ast_copy_string(buf, featuremap->disconnect, len);
998 } else if (!strcasecmp(field, "automon")) {
999 ast_copy_string(buf, featuremap->automon, len);
1000 } else if (!strcasecmp(field, "atxfer")) {
1001 ast_copy_string(buf, featuremap->atxfer, len);
1002 } else if (!strcasecmp(field, "automixmon")) {
1003 ast_copy_string(buf, featuremap->automixmon, len);
1004 } else if (!strcasecmp(field, "parkcall")) {
1005 ast_copy_string(buf, featuremap->parkcall, len);
1007 /* Unrecognized option */
1014 static void feature_ds_destroy(void *data)
1016 struct features_config *cfg = data;
1020 static void *feature_ds_duplicate(void *data)
1022 struct features_config *old_cfg = data;
1024 return features_config_dup(old_cfg);
1027 static const struct ast_datastore_info feature_ds_info = {
1029 .destroy = feature_ds_destroy,
1030 .duplicate = feature_ds_duplicate,
1035 * \brief Find or create feature datastore on a channel
1037 * \pre chan is locked
1039 * \return the data on the FEATURE datastore, or NULL on error
1041 static struct features_config *get_feature_ds(struct ast_channel *chan)
1043 RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
1044 struct features_config *cfg;
1045 struct ast_datastore *ds;
1047 if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1053 orig = ao2_global_obj_ref(globals);
1058 cfg = features_config_dup(orig);
1063 if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1068 /* Give the datastore a reference to the config */
1072 ast_channel_datastore_add(chan, ds);
1077 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1079 struct ast_datastore *ds;
1081 if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1082 /* Hasn't been created yet. Trigger creation. */
1083 RAII_VAR(struct features_config *, cfg, get_feature_ds(chan), ao2_cleanup);
1084 ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1090 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
1092 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1095 cfg = get_feature_ds(chan);
1097 cfg = ao2_global_obj_ref(globals);
1104 ast_assert(cfg->global && cfg->global->general);
1106 ao2_ref(cfg->global->general, +1);
1107 return cfg->global->general;
1110 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
1112 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1115 cfg = get_feature_ds(chan);
1117 cfg = ao2_global_obj_ref(globals);
1124 ast_assert(cfg->global && cfg->global->xfer);
1126 ao2_ref(cfg->global->xfer, +1);
1127 return cfg->global->xfer;
1130 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
1132 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1135 cfg = get_feature_ds(chan);
1137 cfg = ao2_global_obj_ref(globals);
1144 ast_assert(cfg->global && cfg->global->pickup);
1146 ao2_ref(cfg->global->pickup, +1);
1147 return cfg->global->pickup;
1150 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
1152 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1155 cfg = get_feature_ds(chan);
1157 cfg = ao2_global_obj_ref(globals);
1164 ast_assert(cfg->featuremap != NULL);
1166 ao2_ref(cfg->featuremap, +1);
1167 return cfg->featuremap;
1170 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1172 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1175 cfg = get_feature_ds(chan);
1177 cfg = ao2_global_obj_ref(globals);
1184 return featuremap_get(cfg->featuremap, feature, buf, len);
1187 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1189 RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1190 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1192 if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1196 /* Dang, must be in the application map */
1197 applicationmap = ast_get_chan_applicationmap(chan);
1198 if (!applicationmap) {
1202 item = ao2_find(applicationmap, feature, OBJ_KEY);
1207 ast_copy_string(buf, item->dtmf, len);
1211 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
1212 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1213 unsigned int activate_on_self)
1215 struct ast_applicationmap_item *item;
1217 item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1219 if (!item || ast_string_field_init(item, 64)) {
1223 ast_string_field_set(item, name, name);
1224 ast_string_field_set(item, app, app);
1225 ast_string_field_set(item, app_data, app_data);
1226 ast_string_field_set(item, moh_class, moh_class);
1227 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1228 item->activate_on_self = activate_on_self;
1233 static int add_item(void *obj, void *arg, int flags)
1235 struct featuregroup_item *fg_item = obj;
1236 struct ao2_container *applicationmap = arg;
1237 RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1239 /* If there's no DTMF override, then we can just link
1240 * the applicationmap item directly. Otherwise, we need
1241 * to create a copy with the DTMF override in place and
1244 if (ast_strlen_zero(fg_item->dtmf_override)) {
1245 ao2_ref(fg_item->appmap_item, +1);
1246 appmap_item = fg_item->appmap_item;
1248 appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1249 fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1250 fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1251 fg_item->appmap_item->activate_on_self);
1258 ao2_link(applicationmap, appmap_item);
1262 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
1264 RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1265 struct ao2_container *applicationmap;
1274 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1277 ao2_ref(cfg->applicationmap, +1);
1278 return cfg->applicationmap;
1281 group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1282 if (ast_strlen_zero(group_names)) {
1286 applicationmap = applicationmap_alloc(0);
1287 if (!applicationmap) {
1291 while ((name = strsep(&group_names, "#"))) {
1292 RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1295 RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1298 ao2_link(applicationmap, item);
1300 ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1301 name, ast_channel_name(chan));
1304 ao2_callback(group->items, 0, add_item, applicationmap);
1308 if (ao2_container_count(applicationmap) == 0) {
1309 ao2_cleanup(applicationmap);
1313 return applicationmap;
1316 static int applicationmap_handler(const struct aco_option *opt,
1317 struct ast_variable *var, void *obj)
1319 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1320 struct ao2_container *applicationmap = obj;
1321 AST_DECLARE_APP_ARGS(args,
1323 AST_APP_ARG(activate_on);
1325 AST_APP_ARG(app_data);
1326 AST_APP_ARG(moh_class);
1328 char *parse = ast_strdupa(var->value);
1331 unsigned int activate_on_self;
1333 AST_STANDARD_APP_ARGS(args, parse);
1335 if (ast_strlen_zero(args.dtmf) ||
1336 ast_strlen_zero(args.activate_on) ||
1337 ast_strlen_zero(args.app)) {
1338 ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1342 /* features.conf used to have an "activated_by" portion
1343 * in addition to activate_on. Get rid of whatever may be
1346 slash = strchr(args.activate_on, '/');
1351 /* Some applications do not require arguments. */
1352 if (!args.app_data) {
1356 /* Two syntaxes allowed for applicationmap:
1357 * Old: foo = *1,self,NoOp,Boo!,default
1358 * New: foo = *1,self,NoOp(Boo!),default
1360 * We need to handle both
1362 paren = strchr(args.app, '(');
1367 args.moh_class = args.app_data;
1369 close_paren = strrchr(paren, ')');
1371 *close_paren = '\0';
1373 args.app_data = paren;
1375 /* Re-check that the application is not empty */
1376 if (ast_strlen_zero(args.app)) {
1377 ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1380 } else if (strchr(args.app_data, '"')) {
1381 args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1384 /* Allow caller and callee to be specified for backwards compatibility */
1385 if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1386 activate_on_self = 1;
1387 } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1388 activate_on_self = 0;
1390 ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1391 args.activate_on, var->name);
1395 ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1396 args.dtmf, args.app, args.app_data, args.moh_class);
1398 item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1399 args.moh_class, args.dtmf, activate_on_self);
1405 if (!ao2_link(applicationmap, item)) {
1412 static int featuregroup_handler(const struct aco_option *opt,
1413 struct ast_variable *var, void *obj)
1415 RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
1416 struct featuregroup *group = obj;
1418 item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
1419 if (!item || ast_string_field_init(item, 32)) {
1423 ast_string_field_set(item, appmap_item_name, var->name);
1424 ast_string_field_set(item, dtmf_override, var->value);
1426 if (!ao2_link(group->items, item)) {
1430 /* We wait to look up the application map item in the preapply callback */
1435 static int general_handler(const struct aco_option *opt,
1436 struct ast_variable *var, void *obj)
1438 struct features_global_config *global = obj;
1439 struct ast_features_general_config *general = global->general;
1441 return general_set(general, var->name, var->value);
1444 static int xfer_handler(const struct aco_option *opt,
1445 struct ast_variable *var, void *obj)
1447 struct features_global_config *global = obj;
1448 struct ast_features_xfer_config *xfer = global->xfer;
1450 return xfer_set(xfer, var->name, var->value);
1453 static int pickup_handler(const struct aco_option *opt,
1454 struct ast_variable *var, void *obj)
1456 struct features_global_config *global = obj;
1457 struct ast_features_pickup_config *pickup = global->pickup;
1459 return pickup_set(pickup, var->name, var->value);
1462 static int parking_warning = 0;
1463 static int unsupported_handler(const struct aco_option *opt,
1464 struct ast_variable *var, void *obj)
1466 if (!parking_warning) {
1467 ast_log(LOG_WARNING, "Parkinglots are no longer configurable in features.conf; "
1468 "parking is now handled by res_parking.conf\n");
1469 parking_warning = 1;
1471 ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1475 static int featuremap_handler(const struct aco_option *opt,
1476 struct ast_variable *var, void *obj)
1478 struct ast_featuremap_config *featuremap = obj;
1480 return featuremap_set(featuremap, var->name, var->value);
1483 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1485 struct ast_applicationmap_item *appmap_item;
1486 struct featuregroup_item *fg_item = obj;
1488 struct ao2_container *applicationmap = data;
1490 appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1496 fg_item->appmap_item = appmap_item;
1501 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1503 struct featuregroup *group = obj;
1506 ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1509 ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1513 return *err ? CMP_STOP : 0;
1516 static int features_pre_apply_config(void);
1518 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1519 .files = ACO_FILES(&features_conf),
1520 .pre_apply_config = features_pre_apply_config,
1523 static int features_pre_apply_config(void)
1525 struct features_config *cfg = aco_pending_config(&cfg_info);
1528 /* Now that the entire config has been processed, we can check that the featuregroup
1529 * items refer to actual applicationmap items.
1532 ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
1537 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1538 char *buf, size_t len)
1541 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1542 SCOPED_CHANNELLOCK(lock, chan);
1544 if (!strcasecmp(data, "inherit")) {
1545 struct ast_datastore *ds = get_feature_chan_ds(chan);
1546 unsigned int inherit = ds ? ds->inheritance : 0;
1548 snprintf(buf, len, "%s", inherit ? "yes" : "no");
1552 cfg = get_feature_ds(chan);
1557 res = general_get(cfg->global->general, data, buf, len) &&
1558 xfer_get(cfg->global->xfer, data, buf, len) &&
1559 pickup_get(cfg->global->pickup, data, buf, len);
1562 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1568 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1572 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1573 SCOPED_CHANNELLOCK(lock, chan);
1575 if (!strcasecmp(data, "inherit")) {
1576 struct ast_datastore *ds = get_feature_chan_ds(chan);
1578 ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1583 if (!(cfg = get_feature_ds(chan))) {
1587 res = general_set(cfg->global->general, data, value) &&
1588 xfer_set(cfg->global->xfer, data, value) &&
1589 pickup_set(cfg->global->pickup, data, value);
1592 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1598 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1599 char *buf, size_t len)
1602 SCOPED_CHANNELLOCK(lock, chan);
1604 res = ast_get_builtin_feature(chan, data, buf, len);
1607 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1613 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1617 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1618 SCOPED_CHANNELLOCK(lock, chan);
1620 if (!(cfg = get_feature_ds(chan))) {
1624 res = featuremap_set(cfg->featuremap, data, value);
1626 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1633 static struct ast_custom_function feature_function = {
1635 .read = feature_read,
1636 .write = feature_write
1639 static struct ast_custom_function featuremap_function = {
1640 .name = "FEATUREMAP",
1641 .read = featuremap_read,
1642 .write = featuremap_write
1645 static int load_config(void)
1647 if (aco_info_init(&cfg_info)) {
1648 ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1652 aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1653 __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
1654 aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1655 DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
1656 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1657 DEFAULT_COURTESY_TONE, general_handler, 0);
1659 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1660 __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
1661 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1662 __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
1663 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1664 __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
1665 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1666 __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
1667 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1668 __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
1669 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1670 DEFAULT_XFERSOUND, xfer_handler, 0);
1671 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1672 DEFAULT_XFERFAILSOUND, xfer_handler, 0);
1673 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1674 DEFAULT_ATXFER_ABORT, xfer_handler, 0);
1675 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1676 DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
1677 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1678 DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
1679 aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
1680 DEFAULT_ATXFER_SWAP, xfer_handler, 0);
1682 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1683 DEFAULT_PICKUPEXTEN, pickup_handler, 0);
1684 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1685 DEFAULT_PICKUPSOUND, pickup_handler, 0);
1686 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1687 DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
1689 aco_option_register_custom_nodoc(&cfg_info, "context", ACO_EXACT, global_options,
1690 "", unsupported_handler, 0);
1691 aco_option_register_custom_nodoc(&cfg_info, "parkext", ACO_EXACT, global_options,
1692 "", unsupported_handler, 0);
1693 aco_option_register_custom_nodoc(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1694 "", unsupported_handler, 0);
1695 aco_option_register_custom_nodoc(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1696 "", unsupported_handler, 0);
1697 aco_option_register_custom_nodoc(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1698 "", unsupported_handler, 0);
1699 aco_option_register_custom_nodoc(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1700 "", unsupported_handler, 0);
1701 aco_option_register_custom_nodoc(&cfg_info, "parkpos", ACO_EXACT, global_options,
1702 "", unsupported_handler, 0);
1703 aco_option_register_custom_nodoc(&cfg_info, "findslot", ACO_EXACT, global_options,
1704 "", unsupported_handler, 0);
1705 aco_option_register_custom_nodoc(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1706 "", unsupported_handler, 0);
1707 aco_option_register_custom_nodoc(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1708 "", unsupported_handler, 0);
1709 aco_option_register_custom_nodoc(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1710 "", unsupported_handler, 0);
1711 aco_option_register_custom_nodoc(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1712 "", unsupported_handler, 0);
1713 aco_option_register_custom_nodoc(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1714 "", unsupported_handler, 0);
1715 aco_option_register_custom_nodoc(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1716 "", unsupported_handler, 0);
1717 aco_option_register_custom_nodoc(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1718 "", unsupported_handler, 0);
1719 aco_option_register_custom_nodoc(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1720 "", unsupported_handler, 0);
1721 aco_option_register_custom_nodoc(&cfg_info, "adsipark", ACO_EXACT, global_options,
1722 "", unsupported_handler, 0);
1724 aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1725 DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
1726 aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1727 DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
1728 aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1729 DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
1730 aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1731 DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
1732 aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1733 DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
1734 aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1735 DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
1737 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
1738 "", applicationmap_handler, 0);
1740 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
1741 "", featuregroup_handler, 0);
1743 aco_option_register_custom_nodoc(&cfg_info, "^.*$", ACO_REGEX, parkinglot_options,
1744 "", unsupported_handler, 0);
1746 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1747 RAII_VAR(struct features_config *, features_cfg, __features_config_alloc(0), ao2_cleanup);
1749 if (aco_set_defaults(&global_option, "general", features_cfg->global) ||
1750 aco_set_defaults(&featuremap_option, "featuremap", features_cfg->featuremap)) {
1751 ast_log(LOG_ERROR, "Failed to load features.conf and failed to initialize defaults.\n");
1755 ast_log(LOG_NOTICE, "Could not load features config; using defaults\n");
1756 ao2_global_obj_replace_unref(globals, features_cfg);
1762 static int print_featuregroup(void *obj, void *arg, int flags)
1764 struct featuregroup_item *item = obj;
1765 struct ast_cli_args *a = arg;
1767 ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1768 S_OR(item->dtmf_override, item->appmap_item->dtmf));
1773 static int print_featuregroups(void *obj, void *arg, int flags)
1775 struct featuregroup *group = obj;
1776 struct ast_cli_args *a = arg;
1778 ast_cli(a->fd, "===> Group: %s\n", group->name);
1780 ao2_callback(group->items, 0, print_featuregroup, a);
1784 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1786 static int print_applicationmap(void *obj, void *arg, int flags)
1788 struct ast_applicationmap_item *item = obj;
1789 struct ast_cli_args *a = arg;
1791 ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1796 * \brief CLI command to list configured features
1801 * \retval CLI_SUCCESS on success.
1802 * \retval NULL when tab completion is used.
1804 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1806 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1811 e->command = "features show";
1813 "Usage: features show\n"
1814 " Lists configured features\n";
1820 cfg = ao2_global_obj_ref(globals);
1822 ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1823 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1825 ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1826 ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1827 ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1828 ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1829 ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1830 ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1831 ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1833 ast_cli(a->fd, "\n");
1834 ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1835 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1836 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1837 ast_cli(a->fd, "(none)\n");
1839 ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1842 ast_cli(a->fd, "\nFeature Groups:\n");
1843 ast_cli(a->fd, "---------------\n");
1844 if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1845 ast_cli(a->fd, "(none)\n");
1847 ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1850 ast_cli(a->fd, "\n");
1855 static struct ast_cli_entry cli_features_config[] = {
1856 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1859 void ast_features_config_shutdown(void)
1861 ast_custom_function_unregister(&featuremap_function);
1862 ast_custom_function_unregister(&feature_function);
1863 ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1864 aco_info_destroy(&cfg_info);
1865 ao2_global_obj_release(globals);
1868 int ast_features_config_reload(void)
1870 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1876 int ast_features_config_init(void)
1880 res = load_config();
1881 res |= __ast_custom_function_register(&feature_function, NULL);
1882 res |= __ast_custom_function_register(&featuremap_function, NULL);
1883 res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1886 ast_features_config_shutdown();