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>
135 <configOption name="transferdialattempts" default="3">
136 <synopsis>Number of dial attempts allowed when attempting a transfer</synopsis>
138 <configOption name="transferretrysound" default="pbx-invalid">
139 <synopsis>Sound that is played when an incorrect extension is dialed and the transferer should try again.</synopsis>
141 <configOption name="transferinvalidsound" default="privacy-incorrect">
142 <synopsis>Sound that is played when an incorrect extension is dialed and the transferer has no attempts remaining.</synopsis>
145 <configObject name="featuremap">
146 <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
147 <configOption name="atxfer">
148 <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
150 <para>The transferee parties will be placed on hold and the
151 transferrer may dial an extension to reach a transfer target. During an
152 attended transfer, the transferrer may consult with the transfer target
153 before completing the transfer. Once the transferrer has hung up or pressed
154 the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
155 and transfer target will be bridged.</para>
158 <configOption name="blindxfer" default="#">
159 <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
161 <para>The transferee parties will be placed on hold and the
162 transferrer may dial an extension to reach a transfer target. During a
163 blind transfer, as soon as the transfer target is dialed, the transferrer
167 <configOption name="disconnect" default="*">
168 <synopsis>DTMF sequence to disconnect the current call</synopsis>
170 <para>Entering this DTMF sequence will cause the bridge to end, no
171 matter the number of parties present</para>
174 <configOption name="parkcall">
175 <synopsis>DTMF sequence to park a call</synopsis>
177 <para>The parking lot used to park the call is determined by using either the
178 <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
179 the channel (provided by the channel driver) if the variable is not present. If
180 no configured value on the channel is present, then <literal>"default"</literal>
181 is used. The call is parked in the next available space in the parking lot.</para>
184 <configOption name="automon">
185 <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
187 <para>This will cause the channel that pressed the DTMF sequence
188 to be monitored by the <literal>Monitor</literal> application. The
189 format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
190 channel variable. If this variable is not specified, then <literal>wav</literal> is the
191 default. The filename is constructed in the following manner:</para>
193 <para> prefix-timestamp-filename</para>
195 <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
196 channel variable or <literal>auto</literal> if the variable is not set. The timestamp
197 is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
198 channel variable or the callerID of the channels if the variable is not set.</para>
201 <configOption name="automixmon">
202 <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
204 <para>Operation of the automixmon is similar to the <literal> automon </literal>
205 feature, with the following exceptions:
206 <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
207 <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
208 There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
210 <see-also><ref type="configOption">automon</ref></see-also>
213 <configObject name="applicationmap">
214 <synopsis>Section for defining custom feature invocations during a call</synopsis>
216 <para>The applicationmap is an area where new custom features can be created. Items
217 defined in the applicationmap are not automatically accessible to bridged parties. Access
218 to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
219 The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
220 either applicationmap item names or featuregroup names.</para>
222 <configOption name="^.*$" regex="true">
223 <synopsis>A custom feature to invoke during a bridged call</synopsis>
225 <para>Each item listed here is a comma-separated list of parameters that determine
226 how a feature may be invoked during a call</para>
227 <para> Example:</para>
228 <para> eggs = *5,self,Playback(hello-world),default</para>
229 <para>This would create a feature called <literal>eggs</literal> that could be invoked
230 during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
231 sequence would then trigger the <literal>Playback</literal> application to play the
232 <literal>hello-world</literal> file. The application invocation would happen on the
233 party that pressed the DTMF sequence since <literal>self</literal> is specified. The
234 other parties in the bridge would hear the <literal>default</literal> music on hold
235 class during the playback.</para>
236 <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
237 is also allowed. The following applicationmap lines are functionally identical:</para>
238 <para> eggs = *5,self,Playback(hello-world),default</para>
239 <para> eggs = *5,self,Playback,hello-world,default</para>
240 <para> eggs = *5,self,Playback,"hello-world",default</para>
243 <parameter name="dtmf" required="true">
244 <para>The DTMF sequence used to trigger the option</para>
246 <parameter name="activate_on" required="true">
247 <para>The party that the feature will be invoked on</para>
249 <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
250 <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
253 <parameter name="app" required="true">
254 <para>The dialplan application to run when the DTMF sequence is pressed</para>
255 <argument name="app_args" required="false">
256 <para>The arguments to the dialplan application to run</para>
259 <parameter name="moh_class" required="false">
260 <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
265 <configObject name="featuregroup">
266 <synopsis>Groupings of items from the applicationmap</synopsis>
268 <para>Feature groups allow for multiple applicationmap items to be
269 grouped together. Like with individual applicationmap items, feature groups
270 can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
271 In addition to creating groupings, the feature group section allows for the
272 DTMF sequence used to invoke an applicationmap item to be overridden with
273 a different sequence.</para>
275 <configOption name="^.*$" regex="true">
276 <synopsis>Applicationmap item to place in the feature group</synopsis>
278 <para>Each item here must be a name of an item in the applicationmap. The
279 argument may either be a new DTMF sequence to use for the item or it
280 may be left blank in order to use the DTMF sequence specified in the
281 applicationmap. For example:</para>
282 <para> eggs => *1</para>
283 <para> bacon =></para>
284 <para>would result in the applicationmap items <literal>eggs</literal> and
285 <literal>bacon</literal> being in the featuregroup. The former would have its
286 default DTMF trigger overridden with <literal>*1</literal> and the latter would
287 have the DTMF value specified in the applicationmap.</para>
293 <function name="FEATURE" language="en_US">
295 Get or set a feature option on a channel.
298 <parameter name="option_name" required="true">
299 <para>The allowed values are:</para>
301 <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
302 <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>
303 <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>
304 <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>
305 <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>
306 <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>
307 <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>
308 <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>
309 <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>
310 <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>
311 <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>
312 <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>
313 <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>
314 <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>
315 <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>
316 <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>
317 <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>
318 <enum name="transferdialattempts"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferdialattempts']/synopsis/text())" /></para></enum>
319 <enum name="transferretrysound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferretrysound']/synopsis/text())" /></para></enum>
320 <enum name="transferinvalidsound"><para><xi:include xpointer="xpointer(/docs/configInfo[@name='features']/configFile[@name='features.conf']/configObject[@name='globals']/configOption[@name='transferinvalidsound']/synopsis/text())" /></para></enum>
325 <para>When this function is used as a read, it will get the current
326 value of the specified feature option for this channel. It will be
327 the value of this option configured in features.conf if a channel specific
328 value has not been set. This function can also be used to set a channel
329 specific value for the supported feature options.</para>
332 <ref type="function">FEATUREMAP</ref>
335 <function name="FEATUREMAP" language="en_US">
337 Get or set a feature map to a given value on a specific channel.
340 <parameter name="feature_name" required="true">
341 <para>The allowed values are:</para>
343 <enum name="atxfer"><para>Attended Transfer</para></enum>
344 <enum name="blindxfer"><para>Blind Transfer</para></enum>
345 <enum name="automon"><para>Auto Monitor</para></enum>
346 <enum name="disconnect"><para>Call Disconnect</para></enum>
347 <enum name="parkcall"><para>Park Call</para></enum>
348 <enum name="automixmon"><para>Auto MixMonitor</para></enum>
353 <para>When this function is used as a read, it will get the current
354 digit sequence mapped to the specified feature for this channel. This
355 value will be the one configured in features.conf if a channel specific
356 value has not been set. This function can also be used to set a channel
357 specific value for a feature mapping.</para>
360 <ref type="function">FEATURE</ref>
364 /*! Default general options */
365 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
366 #define DEFAULT_COURTESY_TONE ""
367 #define DEFAULT_RECORDING_FAIL_SOUND ""
369 /*! Default xfer options */
370 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3
371 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15
372 #define DEFAULT_ATXFER_DROP_CALL 0
373 #define DEFAULT_ATXFER_LOOP_DELAY 10
374 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
375 #define DEFAULT_XFERSOUND "beep"
376 #define DEFAULT_XFERFAILSOUND "beeperr"
377 #define DEFAULT_ATXFER_ABORT "*1"
378 #define DEFAULT_ATXFER_COMPLETE "*2"
379 #define DEFAULT_ATXFER_THREEWAY "*3"
380 #define DEFAULT_ATXFER_SWAP "*4"
381 #define DEFAULT_TRANSFER_DIAL_ATTEMPTS 3
382 #define DEFAULT_TRANSFER_RETRY_SOUND "pbx-invalid"
383 #define DEFAULT_TRANSFER_INVALID_SOUND "privacy-incorrect"
385 /*! Default pickup options */
386 #define DEFAULT_PICKUPEXTEN "*8"
387 #define DEFAULT_PICKUPSOUND ""
388 #define DEFAULT_PICKUPFAILSOUND ""
390 /*! Default featuremap options */
391 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
392 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
393 #define DEFAULT_FEATUREMAP_AUTOMON ""
394 #define DEFAULT_FEATUREMAP_ATXFER ""
395 #define DEFAULT_FEATUREMAP_PARKCALL ""
396 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
399 * \brief Configuration from the "general" section of features.conf
401 struct features_global_config {
402 struct ast_features_general_config *general;
403 struct ast_features_xfer_config *xfer;
404 struct ast_features_pickup_config *pickup;
407 static void ast_applicationmap_item_destructor(void *obj)
409 struct ast_applicationmap_item *item = obj;
411 ast_string_field_free_memory(item);
414 static int applicationmap_sort(const void *obj, const void *arg, int flags)
416 const struct ast_applicationmap_item *item1 = obj;
417 const struct ast_applicationmap_item *item2;
420 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
423 return strcasecmp(item1->name, key2);
424 case OBJ_PARTIAL_KEY:
426 return strncasecmp(item1->name, key2, strlen(key2));
430 return strcasecmp(item1->name, item2->name);
435 * \brief Entry in the container of featuregroups
437 struct featuregroup_item {
438 AST_DECLARE_STRING_FIELDS(
439 /*! The name of the applicationmap item that we are referring to */
440 AST_STRING_FIELD(appmap_item_name);
441 /*! Custom DTMF override to use instead of the default for the applicationmap item */
442 AST_STRING_FIELD(dtmf_override);
444 /*! The applicationmap item that is being referred to */
445 struct ast_applicationmap_item *appmap_item;
448 static void featuregroup_item_destructor(void *obj)
450 struct featuregroup_item *item = obj;
452 ast_string_field_free_memory(item);
453 ao2_cleanup(item->appmap_item);
456 static int group_item_sort(const void *obj, const void *arg, int flags)
458 const struct featuregroup_item *item1 = obj;
459 const struct featuregroup_item *item2;
462 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
465 return strcasecmp(item1->appmap_item_name, key2);
466 case OBJ_PARTIAL_KEY:
468 return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
471 return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
478 * \brief Featuregroup representation
480 struct featuregroup {
481 /*! The name of the featuregroup */
483 /*! A container of featuregroup_items */
484 struct ao2_container *items;
487 static int featuregroup_hash(const void *obj, int flags)
489 const struct featuregroup *group;
492 switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
495 return ast_str_case_hash(key);
496 case OBJ_PARTIAL_KEY:
502 return ast_str_case_hash(group->name);
506 static int featuregroup_cmp(void *obj, void *arg, int flags)
508 struct featuregroup *group1 = obj;
509 struct featuregroup *group2;
512 switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
515 return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
516 case OBJ_PARTIAL_KEY:
518 return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
521 return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
527 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
529 return ao2_find(group_container, category, OBJ_KEY);
532 static void featuregroup_destructor(void *obj)
534 struct featuregroup *group = obj;
536 ast_free((char *) group->name);
537 ao2_cleanup(group->items);
540 static void *featuregroup_alloc(const char *cat)
542 struct featuregroup *group;
544 group = ao2_alloc(sizeof(*group), featuregroup_destructor);
549 group->name = ast_strdup(cat);
555 group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
556 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
565 /* Used for deprecated parking configuration */
566 struct dummy_config {
570 struct features_config {
571 struct features_global_config *global;
572 struct ast_featuremap_config *featuremap;
573 struct dummy_config *parkinglots;
574 struct ao2_container *applicationmap;
575 struct ao2_container *featuregroups;
578 static struct aco_type global_option = {
581 .category_match = ACO_WHITELIST,
582 .category = "^general$",
583 .item_offset = offsetof(struct features_config, global),
586 static struct aco_type featuremap_option = {
588 .name = "featuremap",
589 .category_match = ACO_WHITELIST,
590 .category = "^featuremap$",
591 .item_offset = offsetof(struct features_config, featuremap),
594 static struct aco_type applicationmap_option = {
596 .name = "applicationmap",
597 .category_match = ACO_WHITELIST,
598 .category = "^applicationmap$",
599 .item_offset = offsetof(struct features_config, applicationmap),
602 static struct aco_type featuregroup_option = {
604 .name = "featuregroup",
605 .category_match = ACO_BLACKLIST,
606 .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
607 .item_offset = offsetof(struct features_config, featuregroups),
608 .item_alloc = featuregroup_alloc,
609 .item_find = featuregroup_find,
612 static struct aco_type parkinglot_option = {
614 .name = "parkinglot",
615 .category_match = ACO_WHITELIST,
616 .category = "^parkinglot_.*$",
617 .item_offset = offsetof(struct features_config, parkinglots),
621 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
622 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
623 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
624 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
625 static struct aco_type *parkinglot_options[] = ACO_TYPES(&parkinglot_option);
627 static struct aco_file features_conf = {
628 .filename = "features.conf",
629 .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option, &parkinglot_option),
632 AO2_GLOBAL_OBJ_STATIC(globals);
634 static void features_config_destructor(void *obj)
636 struct features_config *cfg = obj;
638 ao2_cleanup(cfg->global);
639 ao2_cleanup(cfg->featuremap);
640 ao2_cleanup(cfg->parkinglots);
641 ao2_cleanup(cfg->applicationmap);
642 ao2_cleanup(cfg->featuregroups);
645 static void featuremap_config_destructor(void *obj)
647 struct ast_featuremap_config *cfg = obj;
649 ast_string_field_free_memory(cfg);
652 static void global_config_destructor(void *obj)
654 struct features_global_config *cfg = obj;
656 ao2_cleanup(cfg->general);
657 ao2_cleanup(cfg->xfer);
658 ao2_cleanup(cfg->pickup);
661 static void general_destructor(void *obj)
663 struct ast_features_general_config *cfg = obj;
665 ast_string_field_free_memory(cfg);
668 static void xfer_destructor(void *obj)
670 struct ast_features_xfer_config *cfg = obj;
672 ast_string_field_free_memory(cfg);
675 static void pickup_destructor(void *obj)
677 struct ast_features_pickup_config *cfg = obj;
679 ast_string_field_free_memory(cfg);
682 static struct features_global_config *global_config_alloc(void)
684 RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
686 cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
691 cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
692 if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
696 cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
697 if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
701 cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
702 if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
710 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
712 return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
713 replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
714 applicationmap_sort, NULL);
719 * \brief Allocate the major configuration structure
721 * The parameter is used to determine if the applicationmap and featuregroup
722 * structures should be allocated. We only want to allocate these structures for
723 * the global features_config structure. For the datastores on channels, we don't
724 * need to allocate these structures because they are not used.
726 * \param allocate_applicationmap See previous explanation
727 * \retval NULL Failed to alloate configuration
728 * \retval non-NULL Allocated configuration
730 static struct features_config *__features_config_alloc(int allocate_applicationmap)
732 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
734 cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
739 cfg->global = global_config_alloc();
744 cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
745 if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
749 cfg->parkinglots = ao2_alloc(sizeof(*cfg->parkinglots), NULL);
750 if (!cfg->parkinglots) {
754 if (allocate_applicationmap) {
755 cfg->applicationmap = applicationmap_alloc(1);
756 if (!cfg->applicationmap) {
760 cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
762 if (!cfg->featuregroups) {
772 static void *features_config_alloc(void)
774 return __features_config_alloc(1);
777 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
779 ast_string_fields_copy(dest, src);
780 dest->featuredigittimeout = src->featuredigittimeout;
783 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
785 ast_string_fields_copy(dest, src);
786 dest->transferdigittimeout = src->transferdigittimeout;
787 dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
788 dest->atxferloopdelay = src->atxferloopdelay;
789 dest->atxfercallbackretries = src->atxfercallbackretries;
790 dest->atxferdropcall = src->atxferdropcall;
791 dest->transferdialattempts = src->transferdialattempts;
794 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
796 ast_string_fields_copy(dest, src);
799 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
801 general_copy(dest->general, src->general);
802 xfer_copy(dest->xfer, src->xfer);
803 pickup_copy(dest->pickup, src->pickup);
806 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
808 ast_string_fields_copy(dest, src);
811 static void features_copy(struct features_config *dest, const struct features_config *src)
813 global_copy(dest->global, src->global);
814 featuremap_copy(dest->featuremap, src->featuremap);
816 /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
817 * is produced on the fly when ast_get_chan_applicationmap() is called
818 * NOTE: This does not apply to the global cfg->applicationmap and cfg->featuresgroups
822 static struct features_config *features_config_dup(const struct features_config *orig)
824 struct features_config *dup;
826 dup = __features_config_alloc(0);
831 features_copy(dup, orig);
836 static int general_set(struct ast_features_general_config *general, const char *name,
841 if (!strcasecmp(name, "featuredigittimeout")) {
842 res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
843 } else if (!strcasecmp(name, "courtesytone")) {
844 ast_string_field_set(general, courtesytone, value);
845 } else if (!strcasecmp(name, "recordingfailsound")) {
846 ast_string_field_set(general, recordingfailsound, value);
848 /* Unrecognized option */
855 static int general_get(struct ast_features_general_config *general, const char *field,
856 char *buf, size_t len)
860 if (!strcasecmp(field, "featuredigittimeout")) {
861 snprintf(buf, len, "%u", general->featuredigittimeout);
862 } else if (!strcasecmp(field, "courtesytone")) {
863 ast_copy_string(buf, general->courtesytone, len);
864 } else if (!strcasecmp(field, "recordingfailsound")) {
865 ast_copy_string(buf, general->recordingfailsound, len);
867 /* Unrecognized option */
874 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
879 if (!strcasecmp(name, "transferdigittimeout")) {
880 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
881 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
882 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
883 } else if (!strcasecmp(name, "atxferloopdelay")) {
884 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
885 } else if (!strcasecmp(name, "atxfercallbackretries")) {
886 res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
887 } else if (!strcasecmp(name, "atxferdropcall")) {
888 xfer->atxferdropcall = ast_true(value);
889 } else if (!strcasecmp(name, "xfersound")) {
890 ast_string_field_set(xfer, xfersound, value);
891 } else if (!strcasecmp(name, "xferfailsound")) {
892 ast_string_field_set(xfer, xferfailsound, value);
893 } else if (!strcasecmp(name, "atxferabort")) {
894 ast_string_field_set(xfer, atxferabort, value);
895 } else if (!strcasecmp(name, "atxfercomplete")) {
896 ast_string_field_set(xfer, atxfercomplete, value);
897 } else if (!strcasecmp(name, "atxferthreeway")) {
898 ast_string_field_set(xfer, atxferthreeway, value);
899 } else if (!strcasecmp(name, "atxferswap")) {
900 ast_string_field_set(xfer, atxferswap, value);
901 } else if (!strcasecmp(name, "transferdialattempts")) {
902 res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdialattempts);
903 } else if (!strcasecmp(name, "transferretrysound")) {
904 ast_string_field_set(xfer, transferretrysound, value);
905 } else if (!strcasecmp(name, "transferinvalidsound")) {
906 ast_string_field_set(xfer, transferinvalidsound, value);
908 /* Unrecognized option */
915 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
916 char *buf, size_t len)
920 if (!strcasecmp(field, "transferdigittimeout")) {
921 snprintf(buf, len, "%u", xfer->transferdigittimeout);
922 } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
923 snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
924 } else if (!strcasecmp(field, "atxferloopdelay")) {
925 snprintf(buf, len, "%u", xfer->atxferloopdelay);
926 } else if (!strcasecmp(field, "atxfercallbackretries")) {
927 snprintf(buf, len, "%u", xfer->atxfercallbackretries);
928 } else if (!strcasecmp(field, "atxferdropcall")) {
929 snprintf(buf, len, "%u", xfer->atxferdropcall);
930 } else if (!strcasecmp(field, "xfersound")) {
931 ast_copy_string(buf, xfer->xfersound, len);
932 } else if (!strcasecmp(field, "xferfailsound")) {
933 ast_copy_string(buf, xfer->xferfailsound, len);
934 } else if (!strcasecmp(field, "atxferabort")) {
935 ast_copy_string(buf, xfer->atxferabort, len);
936 } else if (!strcasecmp(field, "atxfercomplete")) {
937 ast_copy_string(buf, xfer->atxfercomplete, len);
938 } else if (!strcasecmp(field, "atxferthreeway")) {
939 ast_copy_string(buf, xfer->atxferthreeway, len);
940 } else if (!strcasecmp(field, "atxferswap")) {
941 ast_copy_string(buf, xfer->atxferswap, len);
942 } else if (!strcasecmp(field, "transferdialattempts")) {
943 snprintf(buf, len, "%u", xfer->transferdialattempts);
944 } else if (!strcasecmp(field, "transferretrysound")) {
945 ast_copy_string(buf, xfer->transferretrysound, len);
946 } else if (!strcasecmp(field, "transferinvalidsound")) {
947 ast_copy_string(buf, xfer->transferinvalidsound, len);
949 /* Unrecognized option */
956 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
961 if (!strcasecmp(name, "pickupsound")) {
962 ast_string_field_set(pickup, pickupsound, value);
963 } else if (!strcasecmp(name, "pickupfailsound")) {
964 ast_string_field_set(pickup, pickupfailsound, value);
965 } else if (!strcasecmp(name, "pickupexten")) {
966 ast_string_field_set(pickup, pickupexten, value);
968 /* Unrecognized option */
975 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
976 char *buf, size_t len)
980 if (!strcasecmp(field, "pickupsound")) {
981 ast_copy_string(buf, pickup->pickupsound, len);
982 } else if (!strcasecmp(field, "pickupfailsound")) {
983 ast_copy_string(buf, pickup->pickupfailsound, len);
984 } else if (!strcasecmp(field, "pickupexten")) {
985 ast_copy_string(buf, pickup->pickupexten, len);
987 /* Unrecognized option */
994 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
999 if (!strcasecmp(name, "blindxfer")) {
1000 ast_string_field_set(featuremap, blindxfer, value);
1001 } else if (!strcasecmp(name, "disconnect")) {
1002 ast_string_field_set(featuremap, disconnect, value);
1003 } else if (!strcasecmp(name, "automon")) {
1004 ast_string_field_set(featuremap, automon, value);
1005 } else if (!strcasecmp(name, "atxfer")) {
1006 ast_string_field_set(featuremap, atxfer, value);
1007 } else if (!strcasecmp(name, "automixmon")) {
1008 ast_string_field_set(featuremap, automixmon, value);
1009 } else if (!strcasecmp(name, "parkcall")) {
1010 ast_string_field_set(featuremap, parkcall, value);
1012 /* Unrecognized option */
1019 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
1020 char *buf, size_t len)
1024 if (!strcasecmp(field, "blindxfer")) {
1025 ast_copy_string(buf, featuremap->blindxfer, len);
1026 } else if (!strcasecmp(field, "disconnect")) {
1027 ast_copy_string(buf, featuremap->disconnect, len);
1028 } else if (!strcasecmp(field, "automon")) {
1029 ast_copy_string(buf, featuremap->automon, len);
1030 } else if (!strcasecmp(field, "atxfer")) {
1031 ast_copy_string(buf, featuremap->atxfer, len);
1032 } else if (!strcasecmp(field, "automixmon")) {
1033 ast_copy_string(buf, featuremap->automixmon, len);
1034 } else if (!strcasecmp(field, "parkcall")) {
1035 ast_copy_string(buf, featuremap->parkcall, len);
1037 /* Unrecognized option */
1044 static void feature_ds_destroy(void *data)
1046 struct features_config *cfg = data;
1050 static void *feature_ds_duplicate(void *data)
1052 struct features_config *old_cfg = data;
1054 return features_config_dup(old_cfg);
1057 static const struct ast_datastore_info feature_ds_info = {
1059 .destroy = feature_ds_destroy,
1060 .duplicate = feature_ds_duplicate,
1065 * \brief Find or create feature datastore on a channel
1067 * \pre chan is locked
1069 * \return the data on the FEATURE datastore, or NULL on error
1071 static struct features_config *get_feature_ds(struct ast_channel *chan)
1073 RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
1074 struct features_config *cfg;
1075 struct ast_datastore *ds;
1077 if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1083 orig = ao2_global_obj_ref(globals);
1088 cfg = features_config_dup(orig);
1093 if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1098 /* Give the datastore a reference to the config */
1102 ast_channel_datastore_add(chan, ds);
1107 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1109 struct ast_datastore *ds;
1111 if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1112 /* Hasn't been created yet. Trigger creation. */
1113 ao2_cleanup(get_feature_ds(chan));
1115 ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1121 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
1123 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1126 cfg = get_feature_ds(chan);
1128 cfg = ao2_global_obj_ref(globals);
1135 ast_assert(cfg->global && cfg->global->general);
1137 ao2_ref(cfg->global->general, +1);
1138 return cfg->global->general;
1141 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
1143 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1146 cfg = get_feature_ds(chan);
1148 cfg = ao2_global_obj_ref(globals);
1155 ast_assert(cfg->global && cfg->global->xfer);
1157 ao2_ref(cfg->global->xfer, +1);
1158 return cfg->global->xfer;
1161 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
1163 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1166 cfg = get_feature_ds(chan);
1168 cfg = ao2_global_obj_ref(globals);
1175 ast_assert(cfg->global && cfg->global->pickup);
1177 ao2_ref(cfg->global->pickup, +1);
1178 return cfg->global->pickup;
1181 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
1183 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1186 cfg = get_feature_ds(chan);
1188 cfg = ao2_global_obj_ref(globals);
1195 ast_assert(cfg->featuremap != NULL);
1197 ao2_ref(cfg->featuremap, +1);
1198 return cfg->featuremap;
1201 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1203 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1206 cfg = get_feature_ds(chan);
1208 cfg = ao2_global_obj_ref(globals);
1215 return featuremap_get(cfg->featuremap, feature, buf, len);
1218 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1220 RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1221 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1223 if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1227 /* Dang, must be in the application map */
1228 applicationmap = ast_get_chan_applicationmap(chan);
1229 if (!applicationmap) {
1233 item = ao2_find(applicationmap, feature, OBJ_KEY);
1238 ast_copy_string(buf, item->dtmf, len);
1242 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
1243 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1244 unsigned int activate_on_self)
1246 struct ast_applicationmap_item *item;
1248 item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1250 if (!item || ast_string_field_init(item, 64)) {
1254 ast_string_field_set(item, name, name);
1255 ast_string_field_set(item, app, app);
1256 ast_string_field_set(item, app_data, app_data);
1257 ast_string_field_set(item, moh_class, moh_class);
1258 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1259 item->activate_on_self = activate_on_self;
1264 static int add_item(void *obj, void *arg, int flags)
1266 struct featuregroup_item *fg_item = obj;
1267 struct ao2_container *applicationmap = arg;
1268 RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1270 /* If there's no DTMF override, then we can just link
1271 * the applicationmap item directly. Otherwise, we need
1272 * to create a copy with the DTMF override in place and
1275 if (ast_strlen_zero(fg_item->dtmf_override)) {
1276 ao2_ref(fg_item->appmap_item, +1);
1277 appmap_item = fg_item->appmap_item;
1279 appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1280 fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1281 fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1282 fg_item->appmap_item->activate_on_self);
1289 ao2_link(applicationmap, appmap_item);
1293 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
1295 RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1296 struct ao2_container *applicationmap;
1305 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1308 ao2_ref(cfg->applicationmap, +1);
1309 return cfg->applicationmap;
1312 group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1313 if (ast_strlen_zero(group_names)) {
1317 applicationmap = applicationmap_alloc(0);
1318 if (!applicationmap) {
1322 /* global config must be initialized */
1323 ast_assert(cfg->featuregroups != NULL);
1324 ast_assert(cfg->applicationmap != NULL);
1325 while ((name = strsep(&group_names, "#"))) {
1326 RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1329 RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1332 ao2_link(applicationmap, item);
1334 ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1335 name, ast_channel_name(chan));
1338 ao2_callback(group->items, 0, add_item, applicationmap);
1342 if (ao2_container_count(applicationmap) == 0) {
1343 ao2_cleanup(applicationmap);
1347 return applicationmap;
1350 static int applicationmap_handler(const struct aco_option *opt,
1351 struct ast_variable *var, void *obj)
1353 RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
1354 struct ao2_container *applicationmap = obj;
1355 AST_DECLARE_APP_ARGS(args,
1357 AST_APP_ARG(activate_on);
1359 AST_APP_ARG(app_data);
1360 AST_APP_ARG(moh_class);
1362 char *parse = ast_strdupa(var->value);
1365 unsigned int activate_on_self;
1367 AST_STANDARD_APP_ARGS(args, parse);
1369 if (ast_strlen_zero(args.dtmf) ||
1370 ast_strlen_zero(args.activate_on) ||
1371 ast_strlen_zero(args.app)) {
1372 ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1376 /* features.conf used to have an "activated_by" portion
1377 * in addition to activate_on. Get rid of whatever may be
1380 slash = strchr(args.activate_on, '/');
1385 /* Some applications do not require arguments. */
1386 if (!args.app_data) {
1390 /* Two syntaxes allowed for applicationmap:
1391 * Old: foo = *1,self,NoOp,Boo!,default
1392 * New: foo = *1,self,NoOp(Boo!),default
1394 * We need to handle both
1396 paren = strchr(args.app, '(');
1401 args.moh_class = args.app_data;
1403 close_paren = strrchr(paren, ')');
1405 *close_paren = '\0';
1407 args.app_data = paren;
1409 /* Re-check that the application is not empty */
1410 if (ast_strlen_zero(args.app)) {
1411 ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1414 } else if (strchr(args.app_data, '"')) {
1415 args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1418 /* Allow caller and callee to be specified for backwards compatibility */
1419 if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1420 activate_on_self = 1;
1421 } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1422 activate_on_self = 0;
1424 ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1425 args.activate_on, var->name);
1429 ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1430 args.dtmf, args.app, args.app_data, args.moh_class);
1432 item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1433 args.moh_class, args.dtmf, activate_on_self);
1439 if (!ao2_link(applicationmap, item)) {
1446 static int featuregroup_handler(const struct aco_option *opt,
1447 struct ast_variable *var, void *obj)
1449 RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
1450 struct featuregroup *group = obj;
1452 item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
1453 if (!item || ast_string_field_init(item, 32)) {
1457 ast_string_field_set(item, appmap_item_name, var->name);
1458 ast_string_field_set(item, dtmf_override, var->value);
1460 if (!ao2_link(group->items, item)) {
1464 /* We wait to look up the application map item in the preapply callback */
1469 static int general_handler(const struct aco_option *opt,
1470 struct ast_variable *var, void *obj)
1472 struct features_global_config *global = obj;
1473 struct ast_features_general_config *general = global->general;
1475 return general_set(general, var->name, var->value);
1478 static int xfer_handler(const struct aco_option *opt,
1479 struct ast_variable *var, void *obj)
1481 struct features_global_config *global = obj;
1482 struct ast_features_xfer_config *xfer = global->xfer;
1484 return xfer_set(xfer, var->name, var->value);
1487 static int pickup_handler(const struct aco_option *opt,
1488 struct ast_variable *var, void *obj)
1490 struct features_global_config *global = obj;
1491 struct ast_features_pickup_config *pickup = global->pickup;
1493 return pickup_set(pickup, var->name, var->value);
1496 static int parking_warning = 0;
1497 static int unsupported_handler(const struct aco_option *opt,
1498 struct ast_variable *var, void *obj)
1500 if (!parking_warning) {
1501 ast_log(LOG_WARNING, "Parkinglots are no longer configurable in features.conf; "
1502 "parking is now handled by res_parking.conf\n");
1503 parking_warning = 1;
1505 ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1509 static int featuremap_handler(const struct aco_option *opt,
1510 struct ast_variable *var, void *obj)
1512 struct ast_featuremap_config *featuremap = obj;
1514 return featuremap_set(featuremap, var->name, var->value);
1517 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1519 struct ast_applicationmap_item *appmap_item;
1520 struct featuregroup_item *fg_item = obj;
1522 struct ao2_container *applicationmap = data;
1524 appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1530 fg_item->appmap_item = appmap_item;
1535 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1537 struct featuregroup *group = obj;
1540 ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1543 ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1547 return *err ? CMP_STOP : 0;
1550 static int features_pre_apply_config(void);
1552 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1553 .files = ACO_FILES(&features_conf),
1554 .pre_apply_config = features_pre_apply_config,
1557 static int features_pre_apply_config(void)
1559 struct features_config *cfg = aco_pending_config(&cfg_info);
1562 /* Now that the entire config has been processed, we can check that the featuregroup
1563 * items refer to actual applicationmap items.
1566 /* global config must be initialized */
1567 ast_assert(cfg->featuregroups != NULL);
1568 ast_assert(cfg->applicationmap != NULL);
1569 ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
1574 static int internal_feature_read(struct ast_channel *chan, const char *cmd, char *data,
1575 char *buf, size_t len)
1578 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1579 SCOPED_CHANNELLOCK(lock, chan);
1581 if (!strcasecmp(data, "inherit")) {
1582 struct ast_datastore *ds = get_feature_chan_ds(chan);
1583 unsigned int inherit = ds ? ds->inheritance : 0;
1585 snprintf(buf, len, "%s", inherit ? "yes" : "no");
1589 cfg = get_feature_ds(chan);
1594 res = general_get(cfg->global->general, data, buf, len) &&
1595 xfer_get(cfg->global->xfer, data, buf, len) &&
1596 pickup_get(cfg->global->pickup, data, buf, len);
1599 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1605 static int internal_feature_write(struct ast_channel *chan, const char *cmd, char *data,
1609 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1610 SCOPED_CHANNELLOCK(lock, chan);
1612 if (!strcasecmp(data, "inherit")) {
1613 struct ast_datastore *ds = get_feature_chan_ds(chan);
1615 ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1620 if (!(cfg = get_feature_ds(chan))) {
1624 res = general_set(cfg->global->general, data, value) &&
1625 xfer_set(cfg->global->xfer, data, value) &&
1626 pickup_set(cfg->global->pickup, data, value);
1629 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1635 static int internal_featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1636 char *buf, size_t len)
1639 SCOPED_CHANNELLOCK(lock, chan);
1641 res = ast_get_builtin_feature(chan, data, buf, len);
1644 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1650 static int internal_featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1654 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1655 SCOPED_CHANNELLOCK(lock, chan);
1657 if (!(cfg = get_feature_ds(chan))) {
1661 res = featuremap_set(cfg->featuremap, data, value);
1663 ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1670 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1671 char *buf, size_t len)
1674 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1678 return internal_feature_read(chan, cmd, data, buf, len);
1681 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1685 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1689 return internal_feature_write(chan, cmd, data, value);
1692 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1693 char *buf, size_t len)
1696 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1700 return internal_featuremap_read(chan, cmd, data, buf, len);
1703 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1707 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1711 return internal_featuremap_write(chan, cmd, data, value);
1714 static struct ast_custom_function feature_function = {
1716 .read = feature_read,
1717 .write = feature_write
1720 static struct ast_custom_function featuremap_function = {
1721 .name = "FEATUREMAP",
1722 .read = featuremap_read,
1723 .write = featuremap_write
1726 static int load_config(void)
1728 if (aco_info_init(&cfg_info)) {
1729 ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1733 aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1734 __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
1735 aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1736 DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
1737 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1738 DEFAULT_COURTESY_TONE, general_handler, 0);
1740 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1741 __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
1742 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1743 __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
1744 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1745 __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
1746 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1747 __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
1748 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1749 __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
1750 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1751 DEFAULT_XFERSOUND, xfer_handler, 0);
1752 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1753 DEFAULT_XFERFAILSOUND, xfer_handler, 0);
1754 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1755 DEFAULT_ATXFER_ABORT, xfer_handler, 0);
1756 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1757 DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
1758 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1759 DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
1760 aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
1761 DEFAULT_ATXFER_SWAP, xfer_handler, 0);
1762 aco_option_register_custom(&cfg_info, "transferdialattempts", ACO_EXACT, global_options,
1763 __stringify(DEFAULT_TRANSFER_DIAL_ATTEMPTS), xfer_handler, 0);
1764 aco_option_register_custom(&cfg_info, "transferretrysound", ACO_EXACT, global_options,
1765 DEFAULT_TRANSFER_RETRY_SOUND, xfer_handler, 0);
1766 aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
1767 DEFAULT_TRANSFER_INVALID_SOUND, xfer_handler, 0);
1769 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1770 DEFAULT_PICKUPEXTEN, pickup_handler, 0);
1771 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1772 DEFAULT_PICKUPSOUND, pickup_handler, 0);
1773 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1774 DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
1776 aco_option_register_custom_nodoc(&cfg_info, "context", ACO_EXACT, global_options,
1777 "", unsupported_handler, 0);
1778 aco_option_register_custom_nodoc(&cfg_info, "parkext", ACO_EXACT, global_options,
1779 "", unsupported_handler, 0);
1780 aco_option_register_custom_nodoc(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1781 "", unsupported_handler, 0);
1782 aco_option_register_custom_nodoc(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1783 "", unsupported_handler, 0);
1784 aco_option_register_custom_nodoc(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1785 "", unsupported_handler, 0);
1786 aco_option_register_custom_nodoc(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1787 "", unsupported_handler, 0);
1788 aco_option_register_custom_nodoc(&cfg_info, "parkpos", ACO_EXACT, global_options,
1789 "", unsupported_handler, 0);
1790 aco_option_register_custom_nodoc(&cfg_info, "findslot", ACO_EXACT, global_options,
1791 "", unsupported_handler, 0);
1792 aco_option_register_custom_nodoc(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1793 "", unsupported_handler, 0);
1794 aco_option_register_custom_nodoc(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1795 "", unsupported_handler, 0);
1796 aco_option_register_custom_nodoc(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1797 "", unsupported_handler, 0);
1798 aco_option_register_custom_nodoc(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1799 "", unsupported_handler, 0);
1800 aco_option_register_custom_nodoc(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1801 "", unsupported_handler, 0);
1802 aco_option_register_custom_nodoc(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1803 "", unsupported_handler, 0);
1804 aco_option_register_custom_nodoc(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1805 "", unsupported_handler, 0);
1806 aco_option_register_custom_nodoc(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1807 "", unsupported_handler, 0);
1808 aco_option_register_custom_nodoc(&cfg_info, "adsipark", ACO_EXACT, global_options,
1809 "", unsupported_handler, 0);
1811 aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1812 DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
1813 aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1814 DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
1815 aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1816 DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
1817 aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1818 DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
1819 aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1820 DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
1821 aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1822 DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
1824 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
1825 "", applicationmap_handler, 0);
1827 aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
1828 "", featuregroup_handler, 0);
1830 aco_option_register_custom_nodoc(&cfg_info, "^.*$", ACO_REGEX, parkinglot_options,
1831 "", unsupported_handler, 0);
1833 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1834 RAII_VAR(struct features_config *, features_cfg, features_config_alloc(), ao2_cleanup);
1836 if (aco_set_defaults(&global_option, "general", features_cfg->global) ||
1837 aco_set_defaults(&featuremap_option, "featuremap", features_cfg->featuremap)) {
1838 ast_log(LOG_ERROR, "Failed to load features.conf and failed to initialize defaults.\n");
1842 ast_log(LOG_NOTICE, "Could not load features config; using defaults\n");
1843 ao2_global_obj_replace_unref(globals, features_cfg);
1849 static int print_featuregroup(void *obj, void *arg, int flags)
1851 struct featuregroup_item *item = obj;
1852 struct ast_cli_args *a = arg;
1854 ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1855 S_OR(item->dtmf_override, item->appmap_item->dtmf));
1860 static int print_featuregroups(void *obj, void *arg, int flags)
1862 struct featuregroup *group = obj;
1863 struct ast_cli_args *a = arg;
1865 ast_cli(a->fd, "===> Group: %s\n", group->name);
1867 ao2_callback(group->items, 0, print_featuregroup, a);
1871 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1873 static int print_applicationmap(void *obj, void *arg, int flags)
1875 struct ast_applicationmap_item *item = obj;
1876 struct ast_cli_args *a = arg;
1878 ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1883 * \brief CLI command to list configured features
1888 * \retval CLI_SUCCESS on success.
1889 * \retval NULL when tab completion is used.
1891 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1893 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1898 e->command = "features show";
1900 "Usage: features show\n"
1901 " Lists configured features\n";
1907 cfg = ao2_global_obj_ref(globals);
1909 ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1910 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1912 ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1913 ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1914 ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1915 ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1916 ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1917 ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1918 ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1920 ast_cli(a->fd, "\n");
1921 ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1922 ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1923 if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1924 ast_cli(a->fd, "(none)\n");
1926 ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1929 ast_cli(a->fd, "\nFeature Groups:\n");
1930 ast_cli(a->fd, "---------------\n");
1931 if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1932 ast_cli(a->fd, "(none)\n");
1934 ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1937 ast_cli(a->fd, "\n");
1942 static struct ast_cli_entry cli_features_config[] = {
1943 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1946 void ast_features_config_shutdown(void)
1948 ast_custom_function_unregister(&featuremap_function);
1949 ast_custom_function_unregister(&feature_function);
1950 ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1951 aco_info_destroy(&cfg_info);
1952 ao2_global_obj_release(globals);
1955 int ast_features_config_reload(void)
1957 /* Rearm the parking config options have moved warning. */
1958 parking_warning = 0;
1960 if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1966 int ast_features_config_init(void)
1970 res = load_config();
1971 res |= __ast_custom_function_register(&feature_function, NULL);
1972 res |= __ast_custom_function_register(&featuremap_function, NULL);
1973 res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1976 ast_features_config_shutdown();