bridge_features: Support One touch Monitor/MixMonitor
[asterisk/asterisk.git] / main / features_config.c
index f8bdb1c..66fdbfa 100644 (file)
 #include "asterisk/app.h"
 #include "asterisk/cli.h"
 
-/* BUGBUG XML Documentation is still needed for configuration options */
 /*** DOCUMENTATION
+       <configInfo name="features" language="en_US">
+               <synopsis>Features Configuration</synopsis>
+               <configFile name="features.conf">
+                       <configObject name="globals">
+                               <synopsis>
+                               </synopsis>
+                               <configOption name="featuredigittimeout" default="1000">
+                                       <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
+                               </configOption>
+                               <configOption name="courtesytone">
+                                       <synopsis>Sound to play when automon or automixmon is activated</synopsis>
+                               </configOption>
+                               <configOption name="recordingfailsound">
+                                       <synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
+                               </configOption>
+                               <configOption name="transferdigittimeout" default="3000">
+                                       <synopsis>Milliseconds allowed between digit presses when dialing a transfer destination</synopsis>
+                               </configOption>
+                               <configOption name="atxfernoanswertimeout" default="15000">
+                                       <synopsis>Milliseconds to wait for attended transfer destination to answer</synopsis>
+                               </configOption>
+                               <configOption name="atxferdropcall" default="no">
+                                       <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
+                                       <description>
+                                               <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
+                                               re-call the transferrer if the call to the transfer target fails. If the call to the
+                                               transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
+                                               milliseconds and then attempt to dial the transfer target again. This process will
+                                               repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
+                                               the transferrer have occurred.</para>
+                                               <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
+                                               to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
+                                               hang up all channels involved in the transfer.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="atxferloopdelay" default="10000">
+                                       <synopsis>Milliseconds to wait between attempts to re-dial transfer destination</synopsis>
+                                       <see-also><ref type="configOption">atxferdropcall</ref></see-also>
+                               </configOption>
+                               <configOption name="atxfercallbackretries" default="2">
+                                       <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
+                                       <see-also><ref type="configOption">atxferdropcall</ref></see-also>
+                               </configOption>
+                               <configOption name="xfersound" default="beep">
+                                       <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
+                                       <description>
+                                               <para>This sound will play to the transferrer and transfer target channels when
+                                               an attended transfer completes. This sound is also played to channels when performing
+                                               an AMI <literal>Bridge</literal> action.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="xferfailsound" default="beeperr">
+                                       <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
+                               </configOption>
+                               <configOption name="atxferabort" default="*1">
+                                       <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
+                                       <description>
+                                               <para>This option is only available to the transferrer during an attended
+                                               transfer operation. Aborting a transfer results in the transfer being cancelled and
+                                               the original parties in the call being re-bridged.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="atxfercomplete" default="*2">
+                                       <synopsis>Digits to dial to complete an attended transfer</synopsis>
+                                       <description>
+                                               <para>This option is only available to the transferrer during an attended
+                                               transfer operation. Completing the transfer with a DTMF sequence is functionally
+                                               equivalent to hanging up the transferrer channel during an attended transfer. The
+                                               result is that the transfer target and transferees are bridged.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="atxferthreeway" default="*3">
+                                       <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
+                                       <description>
+                                               <para>This option is only available to the transferrer during an attended
+                                               transfer operation. Pressing this DTMF sequence will result in the transferrer,
+                                               the transferees, and the transfer target all being in a single bridge together.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="pickupexten" default="*8">
+                                       <synopsis>Digits used for picking up ringing calls</synopsis>
+                                       <description>
+                                               <para>In order for the pickup attempt to be successful, the party attempting to
+                                               pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
+                                               common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
+                                               have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
+                                               <replaceable>callgroup</replaceable>.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="pickupsound">
+                                       <synopsis>Sound to play to picker when a call is picked up</synopsis>
+                               </configOption>
+                               <configOption name="pickupfailsound">
+                                       <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
+                               </configOption>
+                       </configObject>
+                       <configObject name="featuremap">
+                               <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
+                               <configOption name="atxfer">
+                                       <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
+                                       <description>
+                                               <para>The transferee parties will be placed on hold and the
+                                               transferrer may dial an extension to reach a transfer target. During an
+                                               attended transfer, the transferrer may consult with the transfer target
+                                               before completing the transfer. Once the transferrer has hung up or pressed
+                                               the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
+                                               and transfer target will be bridged.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="blindxfer" default="#">
+                                       <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
+                                       <description>
+                                               <para>The transferee parties will be placed on hold and the
+                                               transferrer may dial an extension to reach a transfer target. During a
+                                               blind transfer, as soon as the transfer target is dialed, the transferrer
+                                               is hung up.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="disconnect" default="*">
+                                       <synopsis>DTMF sequence to disconnect the current call</synopsis>
+                                       <description>
+                                               <para>Entering this DTMF sequence will cause the bridge to end, no
+                                               matter the number of parties present</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="parkcall">
+                                       <synopsis>DTMF sequence to park a call</synopsis>
+                                       <description>
+                                               <para>The parking lot used to park the call is determined by using either the
+                                               <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
+                                               the channel (provided by the channel driver) if the variable is not present. If
+                                               no configured value on the channel is present, then <literal>"default"</literal>
+                                               is used. The call is parked in the next available space in the parking lot.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="automon">
+                                       <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
+                                       <description>
+                                               <para>This will cause the channel that pressed the DTMF sequence
+                                               to be monitored by the <literal>Monitor</literal> application. The
+                                               format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
+                                               channel variable. If this variable is not specified, then <literal>wav</literal> is the
+                                               default. The filename is constructed in the following manner:</para>
+                                                       
+                                               <para>    prefix-timestamp-filename</para>
+
+                                               <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
+                                               channel variable or <literal>auto</literal> if the variable is not set. The timestamp
+                                               is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
+                                               channel variable or the callerID of the channels if the variable is not set.</para>
+                                       </description>
+                               </configOption>
+                               <configOption name="automixmon">
+                                       <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
+                                       <description>
+                                               <para>Operation of the automixmon is similar to the <literal> automon </literal>
+                                               feature, with the following exceptions:
+                                                       <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
+                                                       <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
+                                                       There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
+                                       </description>
+                                       <see-also><ref type="configOption">automon</ref></see-also>
+                               </configOption>
+                       </configObject>
+                       <configObject name="applicationmap">
+                               <synopsis>Section for defining custom feature invocations during a call</synopsis>
+                               <description>
+                                       <para>The applicationmap is an area where new custom features can be created. Items
+                                       defined in the applicationmap are not automatically accessible to bridged parties. Access
+                                       to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
+                                       The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
+                                       either applicationmap item names or featuregroup names.</para>
+                               </description>
+                               <configOption name="^.*$" regex="true">
+                                       <synopsis>A custom feature to invoke during a bridged call</synopsis>
+                                       <description>
+                                               <para>Each item listed here is a comma-separated list of parameters that determine
+                                               how a feature may be invoked during a call</para>
+                                               <para>    Example:</para>
+                                               <para>    eggs = *5,self,Playback(hello-world),default</para>
+                                               <para>This would create a feature called <literal>eggs</literal> that could be invoked
+                                               during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
+                                               sequence would then trigger the <literal>Playback</literal> application to play the
+                                               <literal>hello-world</literal> file. The application invocation would happen on the
+                                               party that pressed the DTMF sequence since <literal>self</literal> is specified. The
+                                               other parties in the bridge would hear the <literal>default</literal> music on hold
+                                               class during the playback.</para>
+                                               <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
+                                               is also allowed. The following applicationmap lines are functionally identical:</para>
+                                               <para>    eggs = *5,self,Playback(hello-world),default</para>
+                                               <para>    eggs = *5,self,Playback,hello-world,default</para>
+                                               <para>    eggs = *5,self,Playback,"hello-world",default</para>
+                                       </description>
+                                       <syntax argsep=",">
+                                               <parameter name="dtmf" required="true">
+                                                       <para>The DTMF sequence used to trigger the option</para>
+                                               </parameter>
+                                               <parameter name="activate_on" required="true">
+                                                       <para>The party that the feature will be invoked on</para>
+                                                       <optionlist>
+                                                               <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
+                                                               <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
+                                                       </optionlist>
+                                               </parameter>
+                                               <parameter name="app" required="true">
+                                                       <para>The dialplan application to run when the DTMF sequence is pressed</para>
+                                                       <argument name="app_args" required="false">
+                                                               <para>The arguments to the dialplan application to run</para>
+                                                       </argument>
+                                               </parameter>
+                                               <parameter name="moh_class" required="false">
+                                                       <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
+                                               </parameter>
+                                       </syntax>
+                               </configOption>
+                       </configObject>
+                       <configObject name="featuregroup">
+                               <synopsis>Groupings of items from the applicationmap</synopsis>
+                               <description>
+                                       <para>Feature groups allow for multiple applicationmap items to be
+                                       grouped together. Like with individual applicationmap items, feature groups
+                                       can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
+                                       In addition to creating groupings, the feature group section allows for the
+                                       DTMF sequence used to invoke an applicationmap item to be overridden with
+                                       a different sequence.</para>
+                               </description>
+                               <configOption name="^.*$" regex="true">
+                                       <synopsis>Applicationmap item to place in the feature group</synopsis>
+                                       <description>
+                                               <para>Each item here must be a name of an item in the applicationmap. The
+                                               argument may either be a new DTMF sequence to use for the item or it
+                                               may be left blank in order to use the DTMF sequence specified in the
+                                               applicationmap. For example:</para>
+                                               <para>  eggs => *1</para>
+                                               <para>  bacon =></para>
+                                               <para>would result in the applicationmap items <literal>eggs</literal> and
+                                               <literal>bacon</literal> being in the featuregroup. The former would have its
+                                               default DTMF trigger overridden with <literal>*1</literal> and the latter would
+                                               have the DTMF value specified in the applicationmap.</para>
+                                       </description>
+                               </configOption>
+                       </configObject>
+               </configFile>
+       </configInfo>
        <function name="FEATURE" language="en_US">
                <synopsis>
                        Get or set a feature option on a channel.
                                <para>The allowed values are:</para>
                                <enumlist>
                                        <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
-                                       <enum name="featuredigittimeout"><para>Milliseconds allowed between digits when entering a feature code.</para></enum>
-                                       <enum name="transferdigittimeout"><para>Milliseconds allowed between digits when dialing a transfer destination</para></enum>
-                                       <enum name="atxfernoanswertimeout"><para>Milliseconds to wait for transfer destination to answer</para></enum>
-                                       <enum name="atxferdropcall"><para>Hang up the call entirely if the attended transfer fails</para></enum>
-                                       <enum name="atxferloopdelay"><para>Milliseconds to wait between attempts to re-dial transfer destination</para></enum>
-                                       <enum name="atxfercallbackretries"><para>Number of times to re-attempt dialing a transfer destination</para></enum>
-                                       <enum name="xfersound"><para>Sound to play to a transferee when a transfer completes</para></enum>
-                                       <enum name="xferfailsound"><para>Sound to play to a transferee when a transfer fails</para></enum>
-                                       <enum name="atxferabort"><para>Digits to dial to abort an attended transfer attempt</para></enum>
-                                       <enum name="atxfercomplete"><para>Digits to dial to complete an attended transfer</para></enum>
-                                       <enum name="atxferthreeway"><para>Digits to dial to change an attended transfer into a three-way call</para></enum>
-                                       <enum name="pickupexten"><para>Digits used for picking up ringing calls</para></enum>
-                                       <enum name="pickupsound"><para>Sound to play to picker when a call is picked up</para></enum>
-                                       <enum name="pickupfailsound"><para>Sound to play to picker when a call cannot be picked up</para></enum>
-                                       <enum name="courtesytone"><para>Sound to play when automon or automixmon is activated</para></enum>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
+                                       <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>
                                </enumlist>
                        </parameter>
                </syntax>
  ***/
 /*! Default general options */
 #define DEFAULT_FEATURE_DIGIT_TIMEOUT               1000
+#define DEFAULT_COURTESY_TONE                       ""
+#define DEFAULT_RECORDING_FAIL_SOUND                ""
 
 /*! Default xfer options */
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT              3000
@@ -281,7 +527,7 @@ static void *featuregroup_alloc(const char *cat)
        }
 
        group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
-                       AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, group_item_sort, NULL);
+               AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
        if (!group->items) {
                ao2_cleanup(group);
                return NULL;
@@ -418,10 +664,11 @@ static struct features_global_config *global_config_alloc(void)
        return cfg;
 }
 
-static struct ao2_container *applicationmap_alloc(void)
+static struct ao2_container *applicationmap_alloc(int replace_duplicates)
 {
        return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
-                       AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, applicationmap_sort, NULL);
+               replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
+               applicationmap_sort, NULL);
 }
 
 /*!
@@ -457,7 +704,7 @@ static struct features_config *__features_config_alloc(int allocate_applicationm
        }
 
        if (allocate_applicationmap) {
-               cfg->applicationmap = applicationmap_alloc();
+               cfg->applicationmap = applicationmap_alloc(1);
                if (!cfg->applicationmap) {
                        return NULL;
                }
@@ -545,6 +792,8 @@ static int general_set(struct ast_features_general_config *general, const char *
                res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
        } else if (!strcasecmp(name, "courtesytone")) {
                ast_string_field_set(general, courtesytone, value);
+       } else if (!strcasecmp(name, "recordingfailsound")) {
+               ast_string_field_set(general, recordingfailsound, value);
        } else {
                /* Unrecognized option */
                res = -1;
@@ -562,6 +811,8 @@ static int general_get(struct ast_features_general_config *general, const char *
                snprintf(buf, len, "%u", general->featuredigittimeout);
        } else if (!strcasecmp(field, "courtesytone")) {
                ast_copy_string(buf, general->courtesytone, len);
+       } else if (!strcasecmp(field, "recordingfailsound")) {
+               ast_copy_string(buf, general->recordingfailsound, len);
        } else {
                /* Unrecognized option */
                res = -1;
@@ -901,13 +1152,13 @@ int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, si
 {
        RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
        RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
+
        if (!ast_get_builtin_feature(chan, feature, buf, len)) {
                return 0;
        }
 
        /* Dang, must be in the application map */
        applicationmap = ast_get_chan_applicationmap(chan);
-
        if (!applicationmap) {
                return -1;
        }
@@ -968,10 +1219,7 @@ static int add_item(void *obj, void *arg, int flags)
                return 0;
        }
 
-       if (!ao2_link(applicationmap, appmap_item)) {
-               ast_log(LOG_WARNING, "Unable to add applicationmap item %s. Possible duplicate\n",
-                               fg_item->appmap_item_name);
-       }
+       ao2_link(applicationmap, appmap_item);
        return 0;
 }
 
@@ -999,17 +1247,22 @@ struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
                return NULL;
        }
 
-       applicationmap = applicationmap_alloc();
+       applicationmap = applicationmap_alloc(0);
        if (!applicationmap) {
                return NULL;
        }
 
        while ((name = strsep(&group_names, "#"))) {
                RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
+
                if (!group) {
                        RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
-                       if (item && !ao2_link(applicationmap, item)) {
-                               ast_log(LOG_WARNING, "Unable to add applicationmap item %s. Possible duplicate.\n", item->name);
+
+                       if (item) {
+                               ao2_link(applicationmap, item);
+                       } else {
+                               ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
+                                       name, ast_channel_name(chan));
                        }
                } else {
                        ao2_callback(group->items, 0, add_item, applicationmap);
@@ -1109,7 +1362,7 @@ static int applicationmap_handler(const struct aco_option *opt,
        }
 
        if (!ao2_link(applicationmap, item)) {
-               ast_log(LOG_WARNING, "Unable to add applicationmap item %s. Possible duplicate\n", item->name);
+               return -1;
        }
 
        return 0;
@@ -1130,7 +1383,7 @@ static int featuregroup_handler(const struct aco_option *opt,
        ast_string_field_set(item, dtmf_override, var->value);
 
        if (!ao2_link(group->items, item)) {
-               ast_log(LOG_WARNING, "Unable to add featuregroup item %s. Possible duplicate\n", item->appmap_item_name);
+               return -1;
        }
 
        /* We wait to look up the application map item in the preapply callback */
@@ -1165,6 +1418,13 @@ static int pickup_handler(const struct aco_option *opt,
        return pickup_set(pickup, var->name, var->value);
 }
 
+static int unsupported_handler(const struct aco_option *opt,
+               struct ast_variable *var, void *obj)
+{
+       ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
+       return 0;
+}
+
 static int featuremap_handler(const struct aco_option *opt,
                struct ast_variable *var, void *obj)
 {
@@ -1335,17 +1595,19 @@ static struct ast_custom_function featuremap_function = {
        .write = featuremap_write
 };
 
-static int load_config(int reload)
+static int load_config(void)
 {
-       if (!reload && aco_info_init(&cfg_info)) {
+       if (aco_info_init(&cfg_info)) {
                ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
                return -1;
        }
 
        aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
                        __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
+       aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
+                       DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
        aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
-                       __stringify(DEFAULT_COURTESY_TONE), general_handler, 0);
+                       DEFAULT_COURTESY_TONE, general_handler, 0);
 
        aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
                        __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
@@ -1375,6 +1637,41 @@ static int load_config(int reload)
        aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
                        DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
 
+       aco_option_register_custom(&cfg_info, "context", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkext", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkinghints", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkingtime", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+       aco_option_register_custom(&cfg_info, "adsipark", ACO_EXACT, global_options,
+                       "", unsupported_handler, 0);
+
        aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
                        DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
        aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
@@ -1396,10 +1693,8 @@ static int load_config(int reload)
 
        if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
                ast_log(LOG_ERROR, "Failed to process features.conf configuration!\n");
-               if (!reload) {
-                       aco_info_destroy(&cfg_info);
-                       ao2_global_obj_release(globals);
-               }
+               aco_info_destroy(&cfg_info);
+               ao2_global_obj_release(globals);
                return -1;
        }
 
@@ -1514,14 +1809,17 @@ void ast_features_config_shutdown(void)
 
 int ast_features_config_reload(void)
 {
-       return load_config(1);
+       if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
+               return -1;
+       }
+       return 0;
 }
 
 int ast_features_config_init(void)
 {
        int res;
 
-       res = load_config(0);
+       res = load_config();
        res |= __ast_custom_function_register(&feature_function, NULL);
        res |= __ast_custom_function_register(&featuremap_function, NULL);
        res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));