features_config.c

Go to the documentation of this file.
00001 /*
00002 * Asterisk -- An open source telephony toolkit.
00003 *
00004 * Copyright (C) 2013, Digium, Inc.
00005 *
00006 * Mark Michelson <mmichelson@digium.com>
00007 *
00008 * See http://www.asterisk.org for more information about
00009 * the Asterisk project. Please do not directly contact
00010 * any of the maintainers of this project for assistance;
00011 * the project provides a web site, mailing lists and IRC
00012 * channels for your use.
00013 *
00014 * This program is free software, distributed under the terms of
00015 * the GNU General Public License Version 2. See the LICENSE file
00016 * at the top of the source tree.
00017 */
00018 
00019 #include "asterisk.h"
00020 
00021 #include "asterisk/features_config.h"
00022 #include "asterisk/config_options.h"
00023 #include "asterisk/datastore.h"
00024 #include "asterisk/channel.h"
00025 #include "asterisk/pbx.h"
00026 #include "asterisk/app.h"
00027 #include "asterisk/cli.h"
00028 
00029 /*** DOCUMENTATION
00030    <configInfo name="features" language="en_US">
00031       <synopsis>Features Configuration</synopsis>
00032       <configFile name="features.conf">
00033          <configObject name="globals">
00034             <synopsis>
00035             </synopsis>
00036             <configOption name="featuredigittimeout" default="1000">
00037                <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
00038             </configOption>
00039             <configOption name="courtesytone">
00040                <synopsis>Sound to play when automon or automixmon is activated</synopsis>
00041             </configOption>
00042             <configOption name="recordingfailsound">
00043                <synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
00044             </configOption>
00045             <configOption name="transferdigittimeout" default="3">
00046                <synopsis>Seconds allowed between digit presses when dialing a transfer destination</synopsis>
00047             </configOption>
00048             <configOption name="atxfernoanswertimeout" default="15">
00049                <synopsis>Seconds to wait for attended transfer destination to answer</synopsis>
00050             </configOption>
00051             <configOption name="atxferdropcall" default="no">
00052                <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
00053                <description>
00054                   <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
00055                   re-call the transferrer if the call to the transfer target fails. If the call to the
00056                   transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
00057                   milliseconds and then attempt to dial the transfer target again. This process will
00058                   repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
00059                   the transferrer have occurred.</para>
00060                   <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
00061                   to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
00062                   hang up all channels involved in the transfer.</para>
00063                </description>
00064             </configOption>
00065             <configOption name="atxferloopdelay" default="10">
00066                <synopsis>Seconds to wait between attempts to re-dial transfer destination</synopsis>
00067                <see-also><ref type="configOption">atxferdropcall</ref></see-also>
00068             </configOption>
00069             <configOption name="atxfercallbackretries" default="2">
00070                <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
00071                <see-also><ref type="configOption">atxferdropcall</ref></see-also>
00072             </configOption>
00073             <configOption name="xfersound" default="beep">
00074                <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
00075                <description>
00076                   <para>This sound will play to the transferrer and transfer target channels when
00077                   an attended transfer completes. This sound is also played to channels when performing
00078                   an AMI <literal>Bridge</literal> action.</para>
00079                </description>
00080             </configOption>
00081             <configOption name="xferfailsound" default="beeperr">
00082                <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
00083             </configOption>
00084             <configOption name="atxferabort" default="*1">
00085                <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
00086                <description>
00087                   <para>This option is only available to the transferrer during an attended
00088                   transfer operation. Aborting a transfer results in the transfer being cancelled and
00089                   the original parties in the call being re-bridged.</para>
00090                </description>
00091             </configOption>
00092             <configOption name="atxfercomplete" default="*2">
00093                <synopsis>Digits to dial to complete an attended transfer</synopsis>
00094                <description>
00095                   <para>This option is only available to the transferrer during an attended
00096                   transfer operation. Completing the transfer with a DTMF sequence is functionally
00097                   equivalent to hanging up the transferrer channel during an attended transfer. The
00098                   result is that the transfer target and transferees are bridged.</para>
00099                </description>
00100             </configOption>
00101             <configOption name="atxferthreeway" default="*3">
00102                <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
00103                <description>
00104                   <para>This option is only available to the transferrer during an attended
00105                   transfer operation. Pressing this DTMF sequence will result in the transferrer,
00106                   the transferees, and the transfer target all being in a single bridge together.</para>
00107                </description>
00108             </configOption>
00109             <configOption name="atxferswap" default="*4">
00110                <synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
00111                <description>
00112                   <para>This option is only available to the transferrer during an attended
00113                   transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
00114                   which party he is bridged with. For instance, if the transferrer is currently bridged with
00115                   the transfer target, then pressing this DTMF sequence will cause the transferrer to be
00116                   bridged with the transferees.</para>
00117                </description>
00118             </configOption>
00119             <configOption name="pickupexten" default="*8">
00120                <synopsis>Digits used for picking up ringing calls</synopsis>
00121                <description>
00122                   <para>In order for the pickup attempt to be successful, the party attempting to
00123                   pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
00124                   common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
00125                   have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
00126                   <replaceable>callgroup</replaceable>.</para>
00127                </description>
00128             </configOption>
00129             <configOption name="pickupsound">
00130                <synopsis>Sound to play to picker when a call is picked up</synopsis>
00131             </configOption>
00132             <configOption name="pickupfailsound">
00133                <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
00134             </configOption>
00135             <configOption name="transferdialattempts" default="3">
00136                <synopsis>Number of dial attempts allowed when attempting a transfer</synopsis>
00137             </configOption>
00138             <configOption name="transferretrysound" default="pbx-invalid">
00139                <synopsis>Sound that is played when an incorrect extension is dialed and the transferer should try again.</synopsis>
00140             </configOption>
00141             <configOption name="transferinvalidsound" default="privacy-incorrect">
00142                <synopsis>Sound that is played when an incorrect extension is dialed and the transferer has no attempts remaining.</synopsis>
00143             </configOption>
00144          </configObject>
00145          <configObject name="featuremap">
00146             <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
00147             <configOption name="atxfer">
00148                <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
00149                <description>
00150                   <para>The transferee parties will be placed on hold and the
00151                   transferrer may dial an extension to reach a transfer target. During an
00152                   attended transfer, the transferrer may consult with the transfer target
00153                   before completing the transfer. Once the transferrer has hung up or pressed
00154                   the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
00155                   and transfer target will be bridged.</para>
00156                </description>
00157             </configOption>
00158             <configOption name="blindxfer" default="#">
00159                <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
00160                <description>
00161                   <para>The transferee parties will be placed on hold and the
00162                   transferrer may dial an extension to reach a transfer target. During a
00163                   blind transfer, as soon as the transfer target is dialed, the transferrer
00164                   is hung up.</para>
00165                </description>
00166             </configOption>
00167             <configOption name="disconnect" default="*">
00168                <synopsis>DTMF sequence to disconnect the current call</synopsis>
00169                <description>
00170                   <para>Entering this DTMF sequence will cause the bridge to end, no
00171                   matter the number of parties present</para>
00172                </description>
00173             </configOption>
00174             <configOption name="parkcall">
00175                <synopsis>DTMF sequence to park a call</synopsis>
00176                <description>
00177                   <para>The parking lot used to park the call is determined by using either the
00178                   <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
00179                   the channel (provided by the channel driver) if the variable is not present. If
00180                   no configured value on the channel is present, then <literal>"default"</literal>
00181                   is used. The call is parked in the next available space in the parking lot.</para>
00182                </description>
00183             </configOption>
00184             <configOption name="automon">
00185                <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
00186                <description>
00187                   <para>This will cause the channel that pressed the DTMF sequence
00188                   to be monitored by the <literal>Monitor</literal> application. The
00189                   format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
00190                   channel variable. If this variable is not specified, then <literal>wav</literal> is the
00191                   default. The filename is constructed in the following manner:</para>
00192 
00193                   <para>    prefix-timestamp-filename</para>
00194 
00195                   <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
00196                   channel variable or <literal>auto</literal> if the variable is not set. The timestamp
00197                   is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
00198                   channel variable or the callerID of the channels if the variable is not set.</para>
00199                </description>
00200             </configOption>
00201             <configOption name="automixmon">
00202                <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
00203                <description>
00204                   <para>Operation of the automixmon is similar to the <literal> automon </literal>
00205                   feature, with the following exceptions:
00206                      <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
00207                      <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
00208                      There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
00209                </description>
00210                <see-also><ref type="configOption">automon</ref></see-also>
00211             </configOption>
00212          </configObject>
00213          <configObject name="applicationmap">
00214             <synopsis>Section for defining custom feature invocations during a call</synopsis>
00215             <description>
00216                <para>The applicationmap is an area where new custom features can be created. Items
00217                defined in the applicationmap are not automatically accessible to bridged parties. Access
00218                to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
00219                The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
00220                either applicationmap item names or featuregroup names.</para>
00221             </description>
00222             <configOption name="^.*$" regex="true">
00223                <synopsis>A custom feature to invoke during a bridged call</synopsis>
00224                <description>
00225                   <para>Each item listed here is a comma-separated list of parameters that determine
00226                   how a feature may be invoked during a call</para>
00227                   <para>    Example:</para>
00228                   <para>    eggs = *5,self,Playback(hello-world),default</para>
00229                   <para>This would create a feature called <literal>eggs</literal> that could be invoked
00230                   during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
00231                   sequence would then trigger the <literal>Playback</literal> application to play the
00232                   <literal>hello-world</literal> file. The application invocation would happen on the
00233                   party that pressed the DTMF sequence since <literal>self</literal> is specified. The
00234                   other parties in the bridge would hear the <literal>default</literal> music on hold
00235                   class during the playback.</para>
00236                   <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
00237                   is also allowed. The following applicationmap lines are functionally identical:</para>
00238                   <para>    eggs = *5,self,Playback(hello-world),default</para>
00239                   <para>    eggs = *5,self,Playback,hello-world,default</para>
00240                   <para>    eggs = *5,self,Playback,"hello-world",default</para>
00241                </description>
00242                <syntax argsep=",">
00243                   <parameter name="dtmf" required="true">
00244                      <para>The DTMF sequence used to trigger the option</para>
00245                   </parameter>
00246                   <parameter name="activate_on" required="true">
00247                      <para>The party that the feature will be invoked on</para>
00248                      <optionlist>
00249                         <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
00250                         <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
00251                      </optionlist>
00252                   </parameter>
00253                   <parameter name="app" required="true">
00254                      <para>The dialplan application to run when the DTMF sequence is pressed</para>
00255                      <argument name="app_args" required="false">
00256                         <para>The arguments to the dialplan application to run</para>
00257                      </argument>
00258                   </parameter>
00259                   <parameter name="moh_class" required="false">
00260                      <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
00261                   </parameter>
00262                </syntax>
00263             </configOption>
00264          </configObject>
00265          <configObject name="featuregroup">
00266             <synopsis>Groupings of items from the applicationmap</synopsis>
00267             <description>
00268                <para>Feature groups allow for multiple applicationmap items to be
00269                grouped together. Like with individual applicationmap items, feature groups
00270                can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
00271                In addition to creating groupings, the feature group section allows for the
00272                DTMF sequence used to invoke an applicationmap item to be overridden with
00273                a different sequence.</para>
00274             </description>
00275             <configOption name="^.*$" regex="true">
00276                <synopsis>Applicationmap item to place in the feature group</synopsis>
00277                <description>
00278                   <para>Each item here must be a name of an item in the applicationmap. The
00279                   argument may either be a new DTMF sequence to use for the item or it
00280                   may be left blank in order to use the DTMF sequence specified in the
00281                   applicationmap. For example:</para>
00282                   <para>   eggs => *1</para>
00283                   <para>   bacon =></para>
00284                   <para>would result in the applicationmap items <literal>eggs</literal> and
00285                   <literal>bacon</literal> being in the featuregroup. The former would have its
00286                   default DTMF trigger overridden with <literal>*1</literal> and the latter would
00287                   have the DTMF value specified in the applicationmap.</para>
00288                </description>
00289             </configOption>
00290          </configObject>
00291       </configFile>
00292    </configInfo>
00293    <function name="FEATURE" language="en_US">
00294       <synopsis>
00295          Get or set a feature option on a channel.
00296       </synopsis>
00297       <syntax>
00298          <parameter name="option_name" required="true">
00299             <para>The allowed values are:</para>
00300             <enumlist>
00301                <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
00302                <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>
00303                <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>
00304                <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>
00305                <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>
00306                <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>
00307                <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>
00308                <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>
00309                <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>
00310                <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>
00311                <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>
00312                <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>
00313                <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>
00314                <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>
00315                <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>
00316                <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>
00317                <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>
00318                <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>
00319                <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>
00320                <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>
00321             </enumlist>
00322          </parameter>
00323       </syntax>
00324       <description>
00325          <para>When this function is used as a read, it will get the current
00326          value of the specified feature option for this channel.  It will be
00327          the value of this option configured in features.conf if a channel specific
00328          value has not been set.  This function can also be used to set a channel
00329          specific value for the supported feature options.</para>
00330       </description>
00331       <see-also>
00332          <ref type="function">FEATUREMAP</ref>
00333       </see-also>
00334    </function>
00335    <function name="FEATUREMAP" language="en_US">
00336       <synopsis>
00337          Get or set a feature map to a given value on a specific channel.
00338       </synopsis>
00339       <syntax>
00340          <parameter name="feature_name" required="true">
00341             <para>The allowed values are:</para>
00342             <enumlist>
00343                <enum name="atxfer"><para>Attended Transfer</para></enum>
00344                <enum name="blindxfer"><para>Blind Transfer</para></enum>
00345                <enum name="automon"><para>Auto Monitor</para></enum>
00346                <enum name="disconnect"><para>Call Disconnect</para></enum>
00347                <enum name="parkcall"><para>Park Call</para></enum>
00348                <enum name="automixmon"><para>Auto MixMonitor</para></enum>
00349             </enumlist>
00350          </parameter>
00351       </syntax>
00352       <description>
00353          <para>When this function is used as a read, it will get the current
00354          digit sequence mapped to the specified feature for this channel.  This
00355          value will be the one configured in features.conf if a channel specific
00356          value has not been set.  This function can also be used to set a channel
00357          specific value for a feature mapping.</para>
00358       </description>
00359       <see-also>
00360          <ref type="function">FEATURE</ref>
00361       </see-also>
00362    </function>
00363  ***/
00364 /*! Default general options */
00365 #define DEFAULT_FEATURE_DIGIT_TIMEOUT               1000
00366 #define DEFAULT_COURTESY_TONE                       ""
00367 #define DEFAULT_RECORDING_FAIL_SOUND                ""
00368 
00369 /*! Default xfer options */
00370 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT              3
00371 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER  15
00372 #define DEFAULT_ATXFER_DROP_CALL                    0
00373 #define DEFAULT_ATXFER_LOOP_DELAY                   10
00374 #define DEFAULT_ATXFER_CALLBACK_RETRIES             2
00375 #define DEFAULT_XFERSOUND                           "beep"
00376 #define DEFAULT_XFERFAILSOUND                       "beeperr"
00377 #define DEFAULT_ATXFER_ABORT                        "*1"
00378 #define DEFAULT_ATXFER_COMPLETE                     "*2"
00379 #define DEFAULT_ATXFER_THREEWAY                     "*3"
00380 #define DEFAULT_ATXFER_SWAP                         "*4"
00381 #define DEFAULT_TRANSFER_DIAL_ATTEMPTS              3
00382 #define DEFAULT_TRANSFER_RETRY_SOUND                "pbx-invalid"
00383 #define DEFAULT_TRANSFER_INVALID_SOUND              "privacy-incorrect"
00384 
00385 /*! Default pickup options */
00386 #define DEFAULT_PICKUPEXTEN                         "*8"
00387 #define DEFAULT_PICKUPSOUND                         ""
00388 #define DEFAULT_PICKUPFAILSOUND                     ""
00389 
00390 /*! Default featuremap options */
00391 #define DEFAULT_FEATUREMAP_BLINDXFER                "#"
00392 #define DEFAULT_FEATUREMAP_DISCONNECT               "*"
00393 #define DEFAULT_FEATUREMAP_AUTOMON                  ""
00394 #define DEFAULT_FEATUREMAP_ATXFER                   ""
00395 #define DEFAULT_FEATUREMAP_PARKCALL                 ""
00396 #define DEFAULT_FEATUREMAP_AUTOMIXMON               ""
00397 
00398 /*!
00399  * \brief Configuration from the "general" section of features.conf
00400  */
00401 struct features_global_config {
00402    struct ast_features_general_config *general;
00403    struct ast_features_xfer_config *xfer;
00404    struct ast_features_pickup_config *pickup;
00405 };
00406 
00407 static void ast_applicationmap_item_destructor(void *obj)
00408 {
00409    struct ast_applicationmap_item *item = obj;
00410 
00411    ast_string_field_free_memory(item);
00412 }
00413 
00414 static int applicationmap_sort(const void *obj, const void *arg, int flags)
00415 {
00416    const struct ast_applicationmap_item *item1 = obj;
00417    const struct ast_applicationmap_item *item2;
00418    const char *key2;
00419 
00420    switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00421    case OBJ_KEY:
00422       key2 = arg;
00423       return strcasecmp(item1->name, key2);
00424    case OBJ_PARTIAL_KEY:
00425       key2 = arg;
00426       return strncasecmp(item1->name, key2, strlen(key2));
00427    default:
00428    case OBJ_POINTER:
00429       item2 = arg;
00430       return strcasecmp(item1->name, item2->name);
00431    }
00432 }
00433 
00434 /*!
00435  * \brief Entry in the container of featuregroups
00436  */
00437 struct featuregroup_item {
00438    AST_DECLARE_STRING_FIELDS(
00439       /*! The name of the applicationmap item that we are referring to */
00440       AST_STRING_FIELD(appmap_item_name);
00441       /*! Custom DTMF override to use instead of the default for the applicationmap item */
00442       AST_STRING_FIELD(dtmf_override);
00443    );
00444    /*! The applicationmap item that is being referred to */
00445    struct ast_applicationmap_item *appmap_item;
00446 };
00447 
00448 static void featuregroup_item_destructor(void *obj)
00449 {
00450    struct featuregroup_item *item = obj;
00451 
00452    ast_string_field_free_memory(item);
00453    ao2_cleanup(item->appmap_item);
00454 }
00455 
00456 static int group_item_sort(const void *obj, const void *arg, int flags)
00457 {
00458    const struct featuregroup_item *item1 = obj;
00459    const struct featuregroup_item *item2;
00460    const char *key2;
00461 
00462    switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00463    case OBJ_KEY:
00464       key2 = arg;
00465       return strcasecmp(item1->appmap_item_name, key2);
00466    case OBJ_PARTIAL_KEY:
00467       key2 = arg;
00468       return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
00469    case OBJ_POINTER:
00470       item2 = arg;
00471       return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
00472    default:
00473       return CMP_STOP;
00474    }
00475 }
00476 
00477 /*!
00478  * \brief Featuregroup representation
00479  */
00480 struct featuregroup {
00481    /*! The name of the featuregroup */
00482    const char *name;
00483    /*! A container of featuregroup_items */
00484    struct ao2_container *items;
00485 };
00486 
00487 static int featuregroup_hash(const void *obj, int flags)
00488 {
00489    const struct featuregroup *group;
00490    const char *key;
00491 
00492    switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00493    case OBJ_KEY:
00494       key = obj;
00495       return ast_str_case_hash(key);
00496    case OBJ_PARTIAL_KEY:
00497       ast_assert(0);
00498       return 0;
00499    case OBJ_POINTER:
00500    default:
00501       group = obj;
00502       return ast_str_case_hash(group->name);
00503    }
00504 }
00505 
00506 static int featuregroup_cmp(void *obj, void *arg, int flags)
00507 {
00508    struct featuregroup *group1 = obj;
00509    struct featuregroup *group2;
00510    const char *key2;
00511 
00512    switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
00513    case OBJ_KEY:
00514       key2 = arg;
00515       return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
00516    case OBJ_PARTIAL_KEY:
00517       key2 = arg;
00518       return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
00519    case OBJ_POINTER:
00520       group2 = arg;
00521       return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
00522    default:
00523       return CMP_STOP;
00524    }
00525 }
00526 
00527 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
00528 {
00529    return ao2_find(group_container, category, OBJ_KEY);
00530 }
00531 
00532 static void featuregroup_destructor(void *obj)
00533 {
00534    struct featuregroup *group = obj;
00535 
00536    ast_free((char *) group->name);
00537    ao2_cleanup(group->items);
00538 }
00539 
00540 static void *featuregroup_alloc(const char *cat)
00541 {
00542    struct featuregroup *group;
00543 
00544    group = ao2_alloc(sizeof(*group), featuregroup_destructor);
00545    if (!group) {
00546       return NULL;
00547    }
00548 
00549    group->name = ast_strdup(cat);
00550    if (!group->name) {
00551       ao2_cleanup(group);
00552       return NULL;
00553    }
00554 
00555    group->items = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
00556       AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, group_item_sort, NULL);
00557    if (!group->items) {
00558       ao2_cleanup(group);
00559       return NULL;
00560    }
00561 
00562    return group;
00563 }
00564 
00565 /* Used for deprecated parking configuration */
00566 struct dummy_config {
00567    char dummy;
00568 };
00569 
00570 struct features_config {
00571    struct features_global_config *global;
00572    struct ast_featuremap_config *featuremap;
00573    struct dummy_config *parkinglots;
00574    struct ao2_container *applicationmap;
00575    struct ao2_container *featuregroups;
00576 };
00577 
00578 static struct aco_type global_option = {
00579    .type = ACO_GLOBAL,
00580    .name = "globals",
00581    .category_match = ACO_WHITELIST,
00582    .category = "^general$",
00583    .item_offset = offsetof(struct features_config, global),
00584 };
00585 
00586 static struct aco_type featuremap_option = {
00587    .type = ACO_GLOBAL,
00588    .name = "featuremap",
00589    .category_match = ACO_WHITELIST,
00590    .category = "^featuremap$",
00591    .item_offset = offsetof(struct features_config, featuremap),
00592 };
00593 
00594 static struct aco_type applicationmap_option = {
00595    .type = ACO_GLOBAL,
00596    .name = "applicationmap",
00597    .category_match = ACO_WHITELIST,
00598    .category = "^applicationmap$",
00599    .item_offset = offsetof(struct features_config, applicationmap),
00600 };
00601 
00602 static struct aco_type featuregroup_option = {
00603    .type = ACO_ITEM,
00604    .name = "featuregroup",
00605    .category_match = ACO_BLACKLIST,
00606    .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
00607    .item_offset = offsetof(struct features_config, featuregroups),
00608    .item_alloc = featuregroup_alloc,
00609    .item_find = featuregroup_find,
00610 };
00611 
00612 static struct aco_type parkinglot_option = {
00613    .type = ACO_GLOBAL,
00614    .name = "parkinglot",
00615    .category_match = ACO_WHITELIST,
00616    .category = "^parkinglot_.*$",
00617    .item_offset = offsetof(struct features_config, parkinglots),
00618    .hidden = 1,
00619 };
00620 
00621 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
00622 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
00623 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
00624 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
00625 static struct aco_type *parkinglot_options[] = ACO_TYPES(&parkinglot_option);
00626 
00627 static struct aco_file features_conf = {
00628    .filename = "features.conf",
00629    .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option, &parkinglot_option),
00630 };
00631 
00632 AO2_GLOBAL_OBJ_STATIC(globals);
00633 
00634 static void features_config_destructor(void *obj)
00635 {
00636    struct features_config *cfg = obj;
00637 
00638    ao2_cleanup(cfg->global);
00639    ao2_cleanup(cfg->featuremap);
00640    ao2_cleanup(cfg->parkinglots);
00641    ao2_cleanup(cfg->applicationmap);
00642    ao2_cleanup(cfg->featuregroups);
00643 }
00644 
00645 static void featuremap_config_destructor(void *obj)
00646 {
00647    struct ast_featuremap_config *cfg = obj;
00648 
00649    ast_string_field_free_memory(cfg);
00650 }
00651 
00652 static void global_config_destructor(void *obj)
00653 {
00654    struct features_global_config *cfg = obj;
00655 
00656    ao2_cleanup(cfg->general);
00657    ao2_cleanup(cfg->xfer);
00658    ao2_cleanup(cfg->pickup);
00659 }
00660 
00661 static void general_destructor(void *obj)
00662 {
00663    struct ast_features_general_config *cfg = obj;
00664 
00665    ast_string_field_free_memory(cfg);
00666 }
00667 
00668 static void xfer_destructor(void *obj)
00669 {
00670    struct ast_features_xfer_config *cfg = obj;
00671 
00672    ast_string_field_free_memory(cfg);
00673 }
00674 
00675 static void pickup_destructor(void *obj)
00676 {
00677    struct ast_features_pickup_config *cfg = obj;
00678 
00679    ast_string_field_free_memory(cfg);
00680 }
00681 
00682 static struct features_global_config *global_config_alloc(void)
00683 {
00684    RAII_VAR(struct features_global_config *, cfg, NULL, ao2_cleanup);
00685 
00686    cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
00687    if (!cfg) {
00688       return NULL;
00689    }
00690 
00691    cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
00692    if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
00693       return NULL;
00694    }
00695 
00696    cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
00697    if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
00698       return NULL;
00699    }
00700 
00701    cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
00702    if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
00703       return NULL;
00704    }
00705 
00706    ao2_ref(cfg, +1);
00707    return cfg;
00708 }
00709 
00710 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
00711 {
00712    return ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK,
00713       replace_duplicates ? AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE : AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW,
00714       applicationmap_sort, NULL);
00715 }
00716 
00717 /*!
00718  * \internal
00719  * \brief Allocate the major configuration structure
00720  *
00721  * The parameter is used to determine if the applicationmap and featuregroup
00722  * structures should be allocated. We only want to allocate these structures for
00723  * the global features_config structure. For the datastores on channels, we don't
00724  * need to allocate these structures because they are not used.
00725  *
00726  * \param allocate_applicationmap See previous explanation
00727  * \retval NULL Failed to alloate configuration
00728  * \retval non-NULL Allocated configuration
00729  */
00730 static struct features_config *__features_config_alloc(int allocate_applicationmap)
00731 {
00732    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
00733 
00734    cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
00735    if (!cfg) {
00736       return NULL;
00737    }
00738 
00739    cfg->global = global_config_alloc();
00740    if (!cfg->global) {
00741       return NULL;
00742    }
00743 
00744    cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
00745    if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
00746       return NULL;
00747    }
00748 
00749    cfg->parkinglots = ao2_alloc(sizeof(*cfg->parkinglots), NULL);
00750    if (!cfg->parkinglots) {
00751       return NULL;
00752    }
00753 
00754    if (allocate_applicationmap) {
00755       cfg->applicationmap = applicationmap_alloc(1);
00756       if (!cfg->applicationmap) {
00757          return NULL;
00758       }
00759 
00760       cfg->featuregroups = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 11, featuregroup_hash,
00761          featuregroup_cmp);
00762       if (!cfg->featuregroups) {
00763          return NULL;
00764       }
00765    }
00766 
00767    ao2_ref(cfg, +1);
00768    return cfg;
00769 
00770 }
00771 
00772 static void *features_config_alloc(void)
00773 {
00774    return __features_config_alloc(1);
00775 }
00776 
00777 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
00778 {
00779    ast_string_fields_copy(dest, src);
00780    dest->featuredigittimeout = src->featuredigittimeout;
00781 }
00782 
00783 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
00784 {
00785    ast_string_fields_copy(dest, src);
00786    dest->transferdigittimeout = src->transferdigittimeout;
00787    dest->atxfernoanswertimeout = src->atxfernoanswertimeout;
00788    dest->atxferloopdelay = src->atxferloopdelay;
00789    dest->atxfercallbackretries = src->atxfercallbackretries;
00790    dest->atxferdropcall = src->atxferdropcall;
00791    dest->transferdialattempts = src->transferdialattempts;
00792 }
00793 
00794 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
00795 {
00796    ast_string_fields_copy(dest, src);
00797 }
00798 
00799 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
00800 {
00801    general_copy(dest->general, src->general);
00802    xfer_copy(dest->xfer, src->xfer);
00803    pickup_copy(dest->pickup, src->pickup);
00804 }
00805 
00806 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
00807 {
00808    ast_string_fields_copy(dest, src);
00809 }
00810 
00811 static void features_copy(struct features_config *dest, const struct features_config *src)
00812 {
00813    global_copy(dest->global, src->global);
00814    featuremap_copy(dest->featuremap, src->featuremap);
00815 
00816    /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
00817     * is produced on the fly when ast_get_chan_applicationmap() is called
00818     * NOTE: This does not apply to the global cfg->applicationmap and cfg->featuresgroups
00819     */
00820 }
00821 
00822 static struct features_config *features_config_dup(const struct features_config *orig)
00823 {
00824    struct features_config *dup;
00825 
00826    dup = __features_config_alloc(0);
00827    if (!dup) {
00828       return NULL;
00829    }
00830 
00831    features_copy(dup, orig);
00832 
00833    return dup;
00834 }
00835 
00836 static int general_set(struct ast_features_general_config *general, const char *name,
00837       const char *value)
00838 {
00839    int res = 0;
00840 
00841    if (!strcasecmp(name, "featuredigittimeout")) {
00842       res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
00843    } else if (!strcasecmp(name, "courtesytone")) {
00844       ast_string_field_set(general, courtesytone, value);
00845    } else if (!strcasecmp(name, "recordingfailsound")) {
00846       ast_string_field_set(general, recordingfailsound, value);
00847    } else {
00848       /* Unrecognized option */
00849       res = -1;
00850    }
00851 
00852    return res;
00853 }
00854 
00855 static int general_get(struct ast_features_general_config *general, const char *field,
00856       char *buf, size_t len)
00857 {
00858    int res = 0;
00859 
00860    if (!strcasecmp(field, "featuredigittimeout")) {
00861       snprintf(buf, len, "%u", general->featuredigittimeout);
00862    } else if (!strcasecmp(field, "courtesytone")) {
00863       ast_copy_string(buf, general->courtesytone, len);
00864    } else if (!strcasecmp(field, "recordingfailsound")) {
00865       ast_copy_string(buf, general->recordingfailsound, len);
00866    } else {
00867       /* Unrecognized option */
00868       res = -1;
00869    }
00870 
00871    return res;
00872 }
00873 
00874 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
00875       const char *value)
00876 {
00877    int res = 0;
00878 
00879    if (!strcasecmp(name, "transferdigittimeout")) {
00880       res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
00881    } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
00882       res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
00883    } else if (!strcasecmp(name, "atxferloopdelay")) {
00884       res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
00885    } else if (!strcasecmp(name, "atxfercallbackretries")) {
00886       res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
00887    } else if (!strcasecmp(name, "atxferdropcall")) {
00888       xfer->atxferdropcall = ast_true(value);
00889    } else if (!strcasecmp(name, "xfersound")) {
00890       ast_string_field_set(xfer, xfersound, value);
00891    } else if (!strcasecmp(name, "xferfailsound")) {
00892       ast_string_field_set(xfer, xferfailsound, value);
00893    } else if (!strcasecmp(name, "atxferabort")) {
00894       ast_string_field_set(xfer, atxferabort, value);
00895    } else if (!strcasecmp(name, "atxfercomplete")) {
00896       ast_string_field_set(xfer, atxfercomplete, value);
00897    } else if (!strcasecmp(name, "atxferthreeway")) {
00898       ast_string_field_set(xfer, atxferthreeway, value);
00899    } else if (!strcasecmp(name, "atxferswap")) {
00900       ast_string_field_set(xfer, atxferswap, value);
00901    } else if (!strcasecmp(name, "transferdialattempts")) {
00902       res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdialattempts);
00903    } else if (!strcasecmp(name, "transferretrysound")) {
00904       ast_string_field_set(xfer, transferretrysound, value);
00905    } else if (!strcasecmp(name, "transferinvalidsound")) {
00906       ast_string_field_set(xfer, transferinvalidsound, value);
00907    } else {
00908       /* Unrecognized option */
00909       res = -1;
00910    }
00911 
00912    return res;
00913 }
00914 
00915 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
00916       char *buf, size_t len)
00917 {
00918    int res = 0;
00919 
00920    if (!strcasecmp(field, "transferdigittimeout")) {
00921       snprintf(buf, len, "%u", xfer->transferdigittimeout);
00922    } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
00923       snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
00924    } else if (!strcasecmp(field, "atxferloopdelay")) {
00925       snprintf(buf, len, "%u", xfer->atxferloopdelay);
00926    } else if (!strcasecmp(field, "atxfercallbackretries")) {
00927       snprintf(buf, len, "%u", xfer->atxfercallbackretries);
00928    } else if (!strcasecmp(field, "atxferdropcall")) {
00929       snprintf(buf, len, "%u", xfer->atxferdropcall);
00930    } else if (!strcasecmp(field, "xfersound")) {
00931       ast_copy_string(buf, xfer->xfersound, len);
00932    } else if (!strcasecmp(field, "xferfailsound")) {
00933       ast_copy_string(buf, xfer->xferfailsound, len);
00934    } else if (!strcasecmp(field, "atxferabort")) {
00935       ast_copy_string(buf, xfer->atxferabort, len);
00936    } else if (!strcasecmp(field, "atxfercomplete")) {
00937       ast_copy_string(buf, xfer->atxfercomplete, len);
00938    } else if (!strcasecmp(field, "atxferthreeway")) {
00939       ast_copy_string(buf, xfer->atxferthreeway, len);
00940    } else if (!strcasecmp(field, "atxferswap")) {
00941       ast_copy_string(buf, xfer->atxferswap, len);
00942    } else if (!strcasecmp(field, "transferdialattempts")) {
00943       snprintf(buf, len, "%u", xfer->transferdialattempts);
00944    } else if (!strcasecmp(field, "transferretrysound")) {
00945       ast_copy_string(buf, xfer->transferretrysound, len);
00946    } else if (!strcasecmp(field, "transferinvalidsound")) {
00947       ast_copy_string(buf, xfer->transferinvalidsound, len);
00948    } else {
00949       /* Unrecognized option */
00950       res = -1;
00951    }
00952 
00953    return res;
00954 }
00955 
00956 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
00957       const char *value)
00958 {
00959    int res = 0;
00960 
00961    if (!strcasecmp(name, "pickupsound")) {
00962       ast_string_field_set(pickup, pickupsound, value);
00963    } else if (!strcasecmp(name, "pickupfailsound")) {
00964       ast_string_field_set(pickup, pickupfailsound, value);
00965    } else if (!strcasecmp(name, "pickupexten")) {
00966       ast_string_field_set(pickup, pickupexten, value);
00967    } else {
00968       /* Unrecognized option */
00969       res = -1;
00970    }
00971 
00972    return res;
00973 }
00974 
00975 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
00976       char *buf, size_t len)
00977 {
00978    int res = 0;
00979 
00980    if (!strcasecmp(field, "pickupsound")) {
00981       ast_copy_string(buf, pickup->pickupsound, len);
00982    } else if (!strcasecmp(field, "pickupfailsound")) {
00983       ast_copy_string(buf, pickup->pickupfailsound, len);
00984    } else if (!strcasecmp(field, "pickupexten")) {
00985       ast_copy_string(buf, pickup->pickupexten, len);
00986    } else {
00987       /* Unrecognized option */
00988       res = -1;
00989    }
00990 
00991    return res;
00992 }
00993 
00994 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
00995       const char *value)
00996 {
00997    int res = 0;
00998 
00999    if (!strcasecmp(name, "blindxfer")) {
01000       ast_string_field_set(featuremap, blindxfer, value);
01001    } else if (!strcasecmp(name, "disconnect")) {
01002       ast_string_field_set(featuremap, disconnect, value);
01003    } else if (!strcasecmp(name, "automon")) {
01004       ast_string_field_set(featuremap, automon, value);
01005    } else if (!strcasecmp(name, "atxfer")) {
01006       ast_string_field_set(featuremap, atxfer, value);
01007    } else if (!strcasecmp(name, "automixmon")) {
01008       ast_string_field_set(featuremap, automixmon, value);
01009    } else if (!strcasecmp(name, "parkcall")) {
01010       ast_string_field_set(featuremap, parkcall, value);
01011    } else {
01012       /* Unrecognized option */
01013       res = -1;
01014    }
01015 
01016    return res;
01017 }
01018 
01019 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
01020       char *buf, size_t len)
01021 {
01022    int res = 0;
01023 
01024    if (!strcasecmp(field, "blindxfer")) {
01025       ast_copy_string(buf, featuremap->blindxfer, len);
01026    } else if (!strcasecmp(field, "disconnect")) {
01027       ast_copy_string(buf, featuremap->disconnect, len);
01028    } else if (!strcasecmp(field, "automon")) {
01029       ast_copy_string(buf, featuremap->automon, len);
01030    } else if (!strcasecmp(field, "atxfer")) {
01031       ast_copy_string(buf, featuremap->atxfer, len);
01032    } else if (!strcasecmp(field, "automixmon")) {
01033       ast_copy_string(buf, featuremap->automixmon, len);
01034    } else if (!strcasecmp(field, "parkcall")) {
01035       ast_copy_string(buf, featuremap->parkcall, len);
01036    } else {
01037       /* Unrecognized option */
01038       res = -1;
01039    }
01040 
01041    return res;
01042 }
01043 
01044 static void feature_ds_destroy(void *data)
01045 {
01046    struct features_config *cfg = data;
01047    ao2_cleanup(cfg);
01048 }
01049 
01050 static void *feature_ds_duplicate(void *data)
01051 {
01052    struct features_config *old_cfg = data;
01053 
01054    return features_config_dup(old_cfg);
01055 }
01056 
01057 static const struct ast_datastore_info feature_ds_info = {
01058    .type = "FEATURE",
01059    .destroy = feature_ds_destroy,
01060    .duplicate = feature_ds_duplicate,
01061 };
01062 
01063 /*!
01064  * \internal
01065  * \brief Find or create feature datastore on a channel
01066  *
01067  * \pre chan is locked
01068  *
01069  * \return the data on the FEATURE datastore, or NULL on error
01070  */
01071 static struct features_config *get_feature_ds(struct ast_channel *chan)
01072 {
01073    RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
01074    struct features_config *cfg;
01075    struct ast_datastore *ds;
01076 
01077    if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
01078       cfg = ds->data;
01079       ao2_ref(cfg, +1);
01080       return cfg;
01081    }
01082 
01083    orig = ao2_global_obj_ref(globals);
01084    if (!orig) {
01085       return NULL;
01086    }
01087 
01088    cfg = features_config_dup(orig);
01089    if (!cfg) {
01090       return NULL;
01091    }
01092 
01093    if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
01094       ao2_cleanup(cfg);
01095       return NULL;
01096    }
01097 
01098    /* Give the datastore a reference to the config */
01099    ao2_ref(cfg, +1);
01100    ds->data = cfg;
01101 
01102    ast_channel_datastore_add(chan, ds);
01103 
01104    return cfg;
01105 }
01106 
01107 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
01108 {
01109    struct ast_datastore *ds;
01110 
01111    if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
01112       /* Hasn't been created yet.  Trigger creation. */
01113       ao2_cleanup(get_feature_ds(chan));
01114 
01115       ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
01116    }
01117 
01118    return ds;
01119 }
01120 
01121 struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan)
01122 {
01123    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01124 
01125    if (chan) {
01126       cfg = get_feature_ds(chan);
01127    } else {
01128       cfg = ao2_global_obj_ref(globals);
01129    }
01130 
01131    if (!cfg) {
01132       return NULL;
01133    }
01134 
01135    ast_assert(cfg->global && cfg->global->general);
01136 
01137    ao2_ref(cfg->global->general, +1);
01138    return cfg->global->general;
01139 }
01140 
01141 struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan)
01142 {
01143    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01144 
01145    if (chan) {
01146       cfg = get_feature_ds(chan);
01147    } else {
01148       cfg = ao2_global_obj_ref(globals);
01149    }
01150 
01151    if (!cfg) {
01152       return NULL;
01153    }
01154 
01155    ast_assert(cfg->global && cfg->global->xfer);
01156 
01157    ao2_ref(cfg->global->xfer, +1);
01158    return cfg->global->xfer;
01159 }
01160 
01161 struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan)
01162 {
01163    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01164 
01165    if (chan) {
01166       cfg = get_feature_ds(chan);
01167    } else {
01168       cfg = ao2_global_obj_ref(globals);
01169    }
01170 
01171    if (!cfg) {
01172       return NULL;
01173    }
01174 
01175    ast_assert(cfg->global && cfg->global->pickup);
01176 
01177    ao2_ref(cfg->global->pickup, +1);
01178    return cfg->global->pickup;
01179 }
01180 
01181 struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan)
01182 {
01183    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01184 
01185    if (chan) {
01186       cfg = get_feature_ds(chan);
01187    } else {
01188       cfg = ao2_global_obj_ref(globals);
01189    }
01190 
01191    if (!cfg) {
01192       return NULL;
01193    }
01194 
01195    ast_assert(cfg->featuremap != NULL);
01196 
01197    ao2_ref(cfg->featuremap, +1);
01198    return cfg->featuremap;
01199 }
01200 
01201 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
01202 {
01203    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01204 
01205    if (chan) {
01206       cfg = get_feature_ds(chan);
01207    } else {
01208       cfg = ao2_global_obj_ref(globals);
01209    }
01210 
01211    if (!cfg) {
01212       return -1;
01213    }
01214 
01215    return featuremap_get(cfg->featuremap, feature, buf, len);
01216 }
01217 
01218 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
01219 {
01220    RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
01221    RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
01222 
01223    if (!ast_get_builtin_feature(chan, feature, buf, len)) {
01224       return 0;
01225    }
01226 
01227    /* Dang, must be in the application map */
01228    applicationmap = ast_get_chan_applicationmap(chan);
01229    if (!applicationmap) {
01230       return -1;
01231    }
01232 
01233    item = ao2_find(applicationmap, feature, OBJ_KEY);
01234    if (!item) {
01235       return -1;
01236    }
01237 
01238    ast_copy_string(buf, item->dtmf, len);
01239    return 0;
01240 }
01241 
01242 static struct ast_applicationmap_item *applicationmap_item_alloc(const char *name,
01243       const char *app, const char *app_data, const char *moh_class, const char *dtmf,
01244       unsigned int activate_on_self)
01245 {
01246    struct ast_applicationmap_item *item;
01247 
01248    item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
01249 
01250    if (!item || ast_string_field_init(item, 64)) {
01251       return NULL;
01252    }
01253 
01254    ast_string_field_set(item, name, name);
01255    ast_string_field_set(item, app, app);
01256    ast_string_field_set(item, app_data, app_data);
01257    ast_string_field_set(item, moh_class, moh_class);
01258    ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
01259    item->activate_on_self = activate_on_self;
01260 
01261    return item;
01262 }
01263 
01264 static int add_item(void *obj, void *arg, int flags)
01265 {
01266    struct featuregroup_item *fg_item = obj;
01267    struct ao2_container *applicationmap = arg;
01268    RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
01269 
01270    /* If there's no DTMF override, then we can just link
01271     * the applicationmap item directly. Otherwise, we need
01272     * to create a copy with the DTMF override in place and
01273     * link that instead
01274     */
01275    if (ast_strlen_zero(fg_item->dtmf_override)) {
01276       ao2_ref(fg_item->appmap_item, +1);
01277       appmap_item = fg_item->appmap_item;
01278    } else {
01279       appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
01280             fg_item->appmap_item->app, fg_item->appmap_item->app_data,
01281             fg_item->appmap_item->moh_class, fg_item->dtmf_override,
01282             fg_item->appmap_item->activate_on_self);
01283    }
01284 
01285    if (!appmap_item) {
01286       return 0;
01287    }
01288 
01289    ao2_link(applicationmap, appmap_item);
01290    return 0;
01291 }
01292 
01293 struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan)
01294 {
01295    RAII_VAR(struct features_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01296    struct ao2_container *applicationmap;
01297    char *group_names;
01298    char *name;
01299 
01300    if (!cfg) {
01301       return NULL;
01302    }
01303 
01304    if (!chan) {
01305       if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
01306          return NULL;
01307       }
01308       ao2_ref(cfg->applicationmap, +1);
01309       return cfg->applicationmap;
01310    }
01311 
01312    group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
01313    if (ast_strlen_zero(group_names)) {
01314       return NULL;
01315    }
01316 
01317    applicationmap = applicationmap_alloc(0);
01318    if (!applicationmap) {
01319       return NULL;
01320    }
01321 
01322    /* global config must be initialized */
01323    ast_assert(cfg->featuregroups != NULL);
01324    ast_assert(cfg->applicationmap != NULL);
01325    while ((name = strsep(&group_names, "#"))) {
01326       RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
01327 
01328       if (!group) {
01329          RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
01330 
01331          if (item) {
01332             ao2_link(applicationmap, item);
01333          } else {
01334             ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
01335                name, ast_channel_name(chan));
01336          }
01337       } else {
01338          ao2_callback(group->items, 0, add_item, applicationmap);
01339       }
01340    }
01341 
01342    if (ao2_container_count(applicationmap) == 0) {
01343       ao2_cleanup(applicationmap);
01344       return NULL;
01345    }
01346 
01347    return applicationmap;
01348 }
01349 
01350 static int applicationmap_handler(const struct aco_option *opt,
01351       struct ast_variable *var, void *obj)
01352 {
01353    RAII_VAR(struct ast_applicationmap_item *, item, NULL, ao2_cleanup);
01354    struct ao2_container *applicationmap = obj;
01355    AST_DECLARE_APP_ARGS(args,
01356       AST_APP_ARG(dtmf);
01357       AST_APP_ARG(activate_on);
01358       AST_APP_ARG(app);
01359       AST_APP_ARG(app_data);
01360       AST_APP_ARG(moh_class);
01361    );
01362    char *parse = ast_strdupa(var->value);
01363    char *slash;
01364    char *paren;
01365    unsigned int activate_on_self;
01366 
01367    AST_STANDARD_APP_ARGS(args, parse);
01368 
01369    if (ast_strlen_zero(args.dtmf) ||
01370          ast_strlen_zero(args.activate_on) ||
01371          ast_strlen_zero(args.app)) {
01372       ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
01373       return -1;
01374    }
01375 
01376    /* features.conf used to have an "activated_by" portion
01377     * in addition to activate_on. Get rid of whatever may be
01378     * there
01379     */
01380    slash = strchr(args.activate_on, '/');
01381    if (slash) {
01382       *slash = '\0';
01383    }
01384 
01385    /* Some applications do not require arguments. */
01386    if (!args.app_data) {
01387       args.app_data = "";
01388    }
01389 
01390    /* Two syntaxes allowed for applicationmap:
01391     * Old: foo = *1,self,NoOp,Boo!,default
01392     * New: foo = *1,self,NoOp(Boo!),default
01393     *
01394     * We need to handle both
01395     */
01396    paren = strchr(args.app, '(');
01397    if (paren) {
01398       /* New syntax */
01399       char *close_paren;
01400 
01401       args.moh_class = args.app_data;
01402       *paren++ = '\0';
01403       close_paren = strrchr(paren, ')');
01404       if (close_paren) {
01405          *close_paren = '\0';
01406       }
01407       args.app_data = paren;
01408 
01409       /* Re-check that the application is not empty */
01410       if (ast_strlen_zero(args.app)) {
01411          ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
01412          return -1;
01413       }
01414    } else if (strchr(args.app_data, '"')) {
01415       args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
01416    }
01417 
01418    /* Allow caller and callee to be specified for backwards compatibility */
01419    if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
01420       activate_on_self = 1;
01421    } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
01422       activate_on_self = 0;
01423    } else {
01424       ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
01425          args.activate_on, var->name);
01426       return -1;
01427    }
01428 
01429    ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
01430          args.dtmf, args.app, args.app_data, args.moh_class);
01431 
01432    item = applicationmap_item_alloc(var->name, args.app, args.app_data,
01433          args.moh_class, args.dtmf, activate_on_self);
01434 
01435    if (!item) {
01436       return -1;
01437    }
01438 
01439    if (!ao2_link(applicationmap, item)) {
01440       return -1;
01441    }
01442 
01443    return 0;
01444 }
01445 
01446 static int featuregroup_handler(const struct aco_option *opt,
01447       struct ast_variable *var, void *obj)
01448 {
01449    RAII_VAR(struct featuregroup_item *, item, NULL, ao2_cleanup);
01450    struct featuregroup *group = obj;
01451 
01452    item = ao2_alloc(sizeof(*item), featuregroup_item_destructor);
01453    if (!item || ast_string_field_init(item, 32)) {
01454       return -1;
01455    }
01456 
01457    ast_string_field_set(item, appmap_item_name, var->name);
01458    ast_string_field_set(item, dtmf_override, var->value);
01459 
01460    if (!ao2_link(group->items, item)) {
01461       return -1;
01462    }
01463 
01464    /* We wait to look up the application map item in the preapply callback */
01465 
01466    return 0;
01467 }
01468 
01469 static int general_handler(const struct aco_option *opt,
01470       struct ast_variable *var, void *obj)
01471 {
01472    struct features_global_config *global = obj;
01473    struct ast_features_general_config *general = global->general;
01474 
01475    return general_set(general, var->name, var->value);
01476 }
01477 
01478 static int xfer_handler(const struct aco_option *opt,
01479       struct ast_variable *var, void *obj)
01480 {
01481    struct features_global_config *global = obj;
01482    struct ast_features_xfer_config *xfer = global->xfer;
01483 
01484    return xfer_set(xfer, var->name, var->value);
01485 }
01486 
01487 static int pickup_handler(const struct aco_option *opt,
01488       struct ast_variable *var, void *obj)
01489 {
01490    struct features_global_config *global = obj;
01491    struct ast_features_pickup_config *pickup = global->pickup;
01492 
01493    return pickup_set(pickup, var->name, var->value);
01494 }
01495 
01496 static int parking_warning = 0;
01497 static int unsupported_handler(const struct aco_option *opt,
01498       struct ast_variable *var, void *obj)
01499 {
01500    if (!parking_warning) {
01501       ast_log(LOG_WARNING, "Parkinglots are no longer configurable in features.conf; "
01502          "parking is now handled by res_parking.conf\n");
01503       parking_warning = 1;
01504    }
01505    ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
01506    return 0;
01507 }
01508 
01509 static int featuremap_handler(const struct aco_option *opt,
01510       struct ast_variable *var, void *obj)
01511 {
01512    struct ast_featuremap_config *featuremap = obj;
01513 
01514    return featuremap_set(featuremap, var->name, var->value);
01515 }
01516 
01517 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
01518 {
01519    struct ast_applicationmap_item *appmap_item;
01520    struct featuregroup_item *fg_item = obj;
01521    int *err = arg;
01522    struct ao2_container *applicationmap = data;
01523 
01524    appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
01525    if (!appmap_item) {
01526       *err = 1;
01527       return CMP_STOP;
01528    }
01529 
01530    fg_item->appmap_item = appmap_item;
01531 
01532    return 0;
01533 }
01534 
01535 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
01536 {
01537    struct featuregroup *group = obj;
01538    int *err = arg;
01539 
01540    ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
01541 
01542    if (*err) {
01543       ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
01544             group->name);
01545    }
01546 
01547    return *err ? CMP_STOP : 0;
01548 }
01549 
01550 static int features_pre_apply_config(void);
01551 
01552 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
01553    .files = ACO_FILES(&features_conf),
01554    .pre_apply_config = features_pre_apply_config,
01555 );
01556 
01557 static int features_pre_apply_config(void)
01558 {
01559    struct features_config *cfg = aco_pending_config(&cfg_info);
01560    int err = 0;
01561 
01562    /* Now that the entire config has been processed, we can check that the featuregroup
01563     * items refer to actual applicationmap items.
01564     */
01565 
01566    /* global config must be initialized */
01567    ast_assert(cfg->featuregroups != NULL);
01568    ast_assert(cfg->applicationmap != NULL);
01569    ao2_callback_data(cfg->featuregroups, 0, check_featuregroup, &err, cfg->applicationmap);
01570 
01571    return err;
01572 }
01573 
01574 static int internal_feature_read(struct ast_channel *chan, const char *cmd, char *data,
01575           char *buf, size_t len)
01576 {
01577    int res;
01578    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01579    SCOPED_CHANNELLOCK(lock, chan);
01580 
01581    if (!strcasecmp(data, "inherit")) {
01582       struct ast_datastore *ds = get_feature_chan_ds(chan);
01583       unsigned int inherit = ds ? ds->inheritance : 0;
01584 
01585       snprintf(buf, len, "%s", inherit ? "yes" : "no");
01586       return 0;
01587    }
01588 
01589    cfg = get_feature_ds(chan);
01590    if (!cfg) {
01591       return -1;
01592    }
01593 
01594    res = general_get(cfg->global->general, data, buf, len) &&
01595       xfer_get(cfg->global->xfer, data, buf, len) &&
01596       pickup_get(cfg->global->pickup, data, buf, len);
01597 
01598    if (res) {
01599       ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
01600    }
01601 
01602    return res;
01603 }
01604 
01605 static int internal_feature_write(struct ast_channel *chan, const char *cmd, char *data,
01606       const char *value)
01607 {
01608    int res;
01609    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01610    SCOPED_CHANNELLOCK(lock, chan);
01611 
01612    if (!strcasecmp(data, "inherit")) {
01613       struct ast_datastore *ds = get_feature_chan_ds(chan);
01614       if (ds) {
01615          ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
01616       }
01617       return 0;
01618    }
01619 
01620    if (!(cfg = get_feature_ds(chan))) {
01621       return -1;
01622    }
01623 
01624    res = general_set(cfg->global->general, data, value) &&
01625       xfer_set(cfg->global->xfer, data, value) &&
01626       pickup_set(cfg->global->pickup, data, value);
01627 
01628    if (res) {
01629       ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
01630    }
01631 
01632    return res;
01633 }
01634 
01635 static int internal_featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
01636           char *buf, size_t len)
01637 {
01638    int res;
01639    SCOPED_CHANNELLOCK(lock, chan);
01640 
01641    res = ast_get_builtin_feature(chan, data, buf, len);
01642 
01643    if (res) {
01644       ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
01645    }
01646 
01647    return res;
01648 }
01649 
01650 static int internal_featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
01651       const char *value)
01652 {
01653    int res;
01654    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01655    SCOPED_CHANNELLOCK(lock, chan);
01656 
01657    if (!(cfg = get_feature_ds(chan))) {
01658       return -1;
01659    }
01660 
01661    res = featuremap_set(cfg->featuremap, data, value);
01662    if (res) {
01663       ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
01664       return -1;
01665    }
01666 
01667    return 0;
01668 }
01669 
01670 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
01671       char *buf, size_t len)
01672 {
01673    if (!chan) {
01674       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
01675       return -1;
01676    }
01677 
01678    return internal_feature_read(chan, cmd, data, buf, len);
01679 }
01680 
01681 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
01682       const char *value)
01683 {
01684    if (!chan) {
01685       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
01686       return -1;
01687    }
01688 
01689    return internal_feature_write(chan, cmd, data, value);
01690 }
01691 
01692 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
01693       char *buf, size_t len)
01694 {
01695    if (!chan) {
01696       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
01697       return -1;
01698    }
01699 
01700    return internal_featuremap_read(chan, cmd, data, buf, len);
01701 }
01702 
01703 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
01704       const char *value)
01705 {
01706    if (!chan) {
01707       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
01708       return -1;
01709    }
01710 
01711    return internal_featuremap_write(chan, cmd, data, value);
01712 }
01713 
01714 static struct ast_custom_function feature_function = {
01715    .name = "FEATURE",
01716    .read = feature_read,
01717    .write = feature_write
01718 };
01719 
01720 static struct ast_custom_function featuremap_function = {
01721    .name = "FEATUREMAP",
01722    .read = featuremap_read,
01723    .write = featuremap_write
01724 };
01725 
01726 static int load_config(void)
01727 {
01728    if (aco_info_init(&cfg_info)) {
01729       ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
01730       return -1;
01731    }
01732 
01733    aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
01734          __stringify(DEFAULT_FEATURE_DIGIT_TIMEOUT), general_handler, 0);
01735    aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
01736          DEFAULT_RECORDING_FAIL_SOUND, general_handler, 0);
01737    aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
01738          DEFAULT_COURTESY_TONE, general_handler, 0);
01739 
01740    aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
01741          __stringify(DEFAULT_TRANSFER_DIGIT_TIMEOUT), xfer_handler, 0)
01742    aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
01743          __stringify(DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER), xfer_handler, 0);
01744    aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
01745          __stringify(DEFAULT_ATXFER_DROP_CALL), xfer_handler, 0);
01746    aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
01747          __stringify(DEFAULT_ATXFER_LOOP_DELAY), xfer_handler, 0);
01748    aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
01749          __stringify(DEFAULT_ATXFER_CALLBACK_RETRIES), xfer_handler, 0);
01750    aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
01751          DEFAULT_XFERSOUND, xfer_handler, 0);
01752    aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
01753          DEFAULT_XFERFAILSOUND, xfer_handler, 0);
01754    aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
01755          DEFAULT_ATXFER_ABORT, xfer_handler, 0);
01756    aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
01757          DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
01758    aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
01759          DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
01760    aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
01761          DEFAULT_ATXFER_SWAP, xfer_handler, 0);
01762    aco_option_register_custom(&cfg_info, "transferdialattempts", ACO_EXACT, global_options,
01763          __stringify(DEFAULT_TRANSFER_DIAL_ATTEMPTS), xfer_handler, 0);
01764    aco_option_register_custom(&cfg_info, "transferretrysound", ACO_EXACT, global_options,
01765          DEFAULT_TRANSFER_RETRY_SOUND, xfer_handler, 0);
01766    aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
01767          DEFAULT_TRANSFER_INVALID_SOUND, xfer_handler, 0);
01768 
01769    aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
01770          DEFAULT_PICKUPEXTEN, pickup_handler, 0);
01771    aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
01772          DEFAULT_PICKUPSOUND, pickup_handler, 0);
01773    aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
01774          DEFAULT_PICKUPFAILSOUND, pickup_handler, 0);
01775 
01776    aco_option_register_custom_nodoc(&cfg_info, "context", ACO_EXACT, global_options,
01777          "", unsupported_handler, 0);
01778    aco_option_register_custom_nodoc(&cfg_info, "parkext", ACO_EXACT, global_options,
01779          "", unsupported_handler, 0);
01780    aco_option_register_custom_nodoc(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
01781          "", unsupported_handler, 0);
01782    aco_option_register_custom_nodoc(&cfg_info, "parkinghints", ACO_EXACT, global_options,
01783          "", unsupported_handler, 0);
01784    aco_option_register_custom_nodoc(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
01785          "", unsupported_handler, 0);
01786    aco_option_register_custom_nodoc(&cfg_info, "parkingtime", ACO_EXACT, global_options,
01787          "", unsupported_handler, 0);
01788    aco_option_register_custom_nodoc(&cfg_info, "parkpos", ACO_EXACT, global_options,
01789          "", unsupported_handler, 0);
01790    aco_option_register_custom_nodoc(&cfg_info, "findslot", ACO_EXACT, global_options,
01791          "", unsupported_handler, 0);
01792    aco_option_register_custom_nodoc(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
01793          "", unsupported_handler, 0);
01794    aco_option_register_custom_nodoc(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
01795          "", unsupported_handler, 0);
01796    aco_option_register_custom_nodoc(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
01797          "", unsupported_handler, 0);
01798    aco_option_register_custom_nodoc(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
01799          "", unsupported_handler, 0);
01800    aco_option_register_custom_nodoc(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
01801          "", unsupported_handler, 0);
01802    aco_option_register_custom_nodoc(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
01803          "", unsupported_handler, 0);
01804    aco_option_register_custom_nodoc(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
01805          "", unsupported_handler, 0);
01806    aco_option_register_custom_nodoc(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
01807          "", unsupported_handler, 0);
01808    aco_option_register_custom_nodoc(&cfg_info, "adsipark", ACO_EXACT, global_options,
01809          "", unsupported_handler, 0);
01810 
01811    aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
01812          DEFAULT_FEATUREMAP_BLINDXFER, featuremap_handler, 0);
01813    aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
01814          DEFAULT_FEATUREMAP_DISCONNECT, featuremap_handler, 0);
01815    aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
01816          DEFAULT_FEATUREMAP_AUTOMON, featuremap_handler, 0);
01817    aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
01818          DEFAULT_FEATUREMAP_ATXFER, featuremap_handler, 0);
01819    aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
01820          DEFAULT_FEATUREMAP_PARKCALL, featuremap_handler, 0);
01821    aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
01822          DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
01823 
01824    aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
01825          "", applicationmap_handler, 0);
01826 
01827    aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
01828          "", featuregroup_handler, 0);
01829 
01830    aco_option_register_custom_nodoc(&cfg_info, "^.*$", ACO_REGEX, parkinglot_options,
01831          "", unsupported_handler, 0);
01832 
01833    if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
01834       RAII_VAR(struct features_config *, features_cfg, features_config_alloc(), ao2_cleanup);
01835 
01836       if (aco_set_defaults(&global_option, "general", features_cfg->global) ||
01837          aco_set_defaults(&featuremap_option, "featuremap", features_cfg->featuremap)) {
01838          ast_log(LOG_ERROR, "Failed to load features.conf and failed to initialize defaults.\n");
01839          return -1;
01840       }
01841 
01842       ast_log(LOG_NOTICE, "Could not load features config; using defaults\n");
01843       ao2_global_obj_replace_unref(globals, features_cfg);
01844    }
01845 
01846    return 0;
01847 }
01848 
01849 static int print_featuregroup(void *obj, void *arg, int flags)
01850 {
01851    struct featuregroup_item *item = obj;
01852    struct ast_cli_args *a = arg;
01853 
01854    ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
01855          S_OR(item->dtmf_override, item->appmap_item->dtmf));
01856 
01857    return 0;
01858 }
01859 
01860 static int print_featuregroups(void *obj, void *arg, int flags)
01861 {
01862    struct featuregroup *group = obj;
01863    struct ast_cli_args *a = arg;
01864 
01865    ast_cli(a->fd, "===> Group: %s\n", group->name);
01866 
01867    ao2_callback(group->items, 0, print_featuregroup, a);
01868    return 0;
01869 }
01870 
01871 #define HFS_FORMAT "%-25s %-7s %-7s\n"
01872 
01873 static int print_applicationmap(void *obj, void *arg, int flags)
01874 {
01875    struct ast_applicationmap_item *item = obj;
01876    struct ast_cli_args *a = arg;
01877 
01878    ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
01879    return 0;
01880 }
01881 
01882 /*!
01883  * \brief CLI command to list configured features
01884  * \param e
01885  * \param cmd
01886  * \param a
01887  *
01888  * \retval CLI_SUCCESS on success.
01889  * \retval NULL when tab completion is used.
01890  */
01891 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01892 {
01893    RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
01894 
01895    switch (cmd) {
01896 
01897    case CLI_INIT:
01898       e->command = "features show";
01899       e->usage =
01900          "Usage: features show\n"
01901          "       Lists configured features\n";
01902       return NULL;
01903    case CLI_GENERATE:
01904       return NULL;
01905    }
01906 
01907    cfg = ao2_global_obj_ref(globals);
01908 
01909    ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
01910    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
01911 
01912    ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
01913    ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
01914    ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
01915    ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
01916    ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
01917    ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
01918    ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
01919 
01920    ast_cli(a->fd, "\n");
01921    ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
01922    ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
01923    if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
01924       ast_cli(a->fd, "(none)\n");
01925    } else {
01926       ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
01927    }
01928 
01929    ast_cli(a->fd, "\nFeature Groups:\n");
01930    ast_cli(a->fd, "---------------\n");
01931    if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
01932       ast_cli(a->fd, "(none)\n");
01933    } else {
01934       ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
01935    }
01936 
01937    ast_cli(a->fd, "\n");
01938 
01939    return CLI_SUCCESS;
01940 }
01941 
01942 static struct ast_cli_entry cli_features_config[] = {
01943    AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
01944 };
01945 
01946 void ast_features_config_shutdown(void)
01947 {
01948    ast_custom_function_unregister(&featuremap_function);
01949    ast_custom_function_unregister(&feature_function);
01950    ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
01951    aco_info_destroy(&cfg_info);
01952    ao2_global_obj_release(globals);
01953 }
01954 
01955 int ast_features_config_reload(void)
01956 {
01957    /* Rearm the parking config options have moved warning. */
01958    parking_warning = 0;
01959 
01960    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
01961       return -1;
01962    }
01963    return 0;
01964 }
01965 
01966 int ast_features_config_init(void)
01967 {
01968    int res;
01969 
01970    res = load_config();
01971    res |= __ast_custom_function_register(&feature_function, NULL);
01972    res |= __ast_custom_function_register(&featuremap_function, NULL);
01973    res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
01974 
01975    if (res) {
01976       ast_features_config_shutdown();
01977    }
01978 
01979    return res;
01980 }

Generated on Thu Apr 16 06:27:34 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6