Asterisk - The Open Source Telephony Project  GIT-master-a1fa8df
features_config.c
Go to the documentation of this file.
1 /*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18 
19 #include "asterisk.h"
20 
23 #include "asterisk/datastore.h"
24 #include "asterisk/channel.h"
25 #include "asterisk/pbx.h"
26 #include "asterisk/app.h"
27 #include "asterisk/cli.h"
28 
29 #include "features_config.h"
30 
31 /*** DOCUMENTATION
32  <configInfo name="features" language="en_US">
33  <synopsis>Features Configuration</synopsis>
34  <configFile name="features.conf">
35  <configObject name="globals">
36  <synopsis>
37  </synopsis>
38  <configOption name="featuredigittimeout" default="1000">
39  <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
40  </configOption>
41  <configOption name="courtesytone">
42  <synopsis>Sound to play when automon or automixmon is activated</synopsis>
43  </configOption>
44  <configOption name="recordingfailsound">
45  <synopsis>Sound to play when automon or automixmon is attempted but fails to start</synopsis>
46  </configOption>
47  <configOption name="transferdigittimeout" default="3">
48  <synopsis>Seconds allowed between digit presses when dialing a transfer destination</synopsis>
49  </configOption>
50  <configOption name="atxfernoanswertimeout" default="15">
51  <synopsis>Seconds to wait for attended transfer destination to answer</synopsis>
52  </configOption>
53  <configOption name="atxferdropcall" default="no">
54  <synopsis>Hang up the call entirely if the attended transfer fails</synopsis>
55  <description>
56  <para>When this option is set to <literal>no</literal>, then Asterisk will attempt to
57  re-call the transferrer if the call to the transfer target fails. If the call to the
58  transferrer fails, then Asterisk will wait <replaceable>atxferloopdelay</replaceable>
59  milliseconds and then attempt to dial the transfer target again. This process will
60  repeat until <replaceable>atxfercallbackretries</replaceable> attempts to re-call
61  the transferrer have occurred.</para>
62  <para>When this option is set to <literal>yes</literal>, then Asterisk will not attempt
63  to re-call the transferrer if the call to the transfer target fails. Asterisk will instead
64  hang up all channels involved in the transfer.</para>
65  </description>
66  </configOption>
67  <configOption name="atxferloopdelay" default="10">
68  <synopsis>Seconds to wait between attempts to re-dial transfer destination</synopsis>
69  <see-also><ref type="configOption">atxferdropcall</ref></see-also>
70  </configOption>
71  <configOption name="atxfercallbackretries" default="2">
72  <synopsis>Number of times to re-attempt dialing a transfer destination</synopsis>
73  <see-also><ref type="configOption">atxferdropcall</ref></see-also>
74  </configOption>
75  <configOption name="xfersound" default="beep">
76  <synopsis>Sound to play to during transfer and transfer-like operations.</synopsis>
77  <description>
78  <para>This sound will play to the transferrer and transfer target channels when
79  an attended transfer completes. This sound is also played to channels when performing
80  an AMI <literal>Bridge</literal> action.</para>
81  </description>
82  </configOption>
83  <configOption name="xferfailsound" default="beeperr">
84  <synopsis>Sound to play to a transferee when a transfer fails</synopsis>
85  </configOption>
86  <configOption name="atxferabort" default="*1">
87  <synopsis>Digits to dial to abort an attended transfer attempt</synopsis>
88  <description>
89  <para>This option is only available to the transferrer during an attended
90  transfer operation. Aborting a transfer results in the transfer being cancelled and
91  the original parties in the call being re-bridged.</para>
92  </description>
93  </configOption>
94  <configOption name="atxfercomplete" default="*2">
95  <synopsis>Digits to dial to complete an attended transfer</synopsis>
96  <description>
97  <para>This option is only available to the transferrer during an attended
98  transfer operation. Completing the transfer with a DTMF sequence is functionally
99  equivalent to hanging up the transferrer channel during an attended transfer. The
100  result is that the transfer target and transferees are bridged.</para>
101  </description>
102  </configOption>
103  <configOption name="atxferthreeway" default="*3">
104  <synopsis>Digits to dial to change an attended transfer into a three-way call</synopsis>
105  <description>
106  <para>This option is only available to the transferrer during an attended
107  transfer operation. Pressing this DTMF sequence will result in the transferrer,
108  the transferees, and the transfer target all being in a single bridge together.</para>
109  </description>
110  </configOption>
111  <configOption name="atxferswap" default="*4">
112  <synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
113  <description>
114  <para>This option is only available to the transferrer during an attended
115  transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
116  which party he is bridged with. For instance, if the transferrer is currently bridged with
117  the transfer target, then pressing this DTMF sequence will cause the transferrer to be
118  bridged with the transferees.</para>
119  </description>
120  </configOption>
121  <configOption name="pickupexten" default="*8">
122  <synopsis>Digits used for picking up ringing calls</synopsis>
123  <description>
124  <para>In order for the pickup attempt to be successful, the party attempting to
125  pick up the call must either have a <replaceable>namedpickupgroup</replaceable> in
126  common with a ringing party's <replaceable>namedcallgroup</replaceable> or must
127  have a <replaceable>pickupgroup</replaceable> in common with a ringing party's
128  <replaceable>callgroup</replaceable>.</para>
129  </description>
130  </configOption>
131  <configOption name="pickupsound">
132  <synopsis>Sound to play to picker when a call is picked up</synopsis>
133  </configOption>
134  <configOption name="pickupfailsound">
135  <synopsis>Sound to play to picker when a call cannot be picked up</synopsis>
136  </configOption>
137  <configOption name="transferdialattempts" default="3">
138  <synopsis>Number of dial attempts allowed when attempting a transfer</synopsis>
139  </configOption>
140  <configOption name="transferretrysound" default="pbx-invalid">
141  <synopsis>Sound that is played when an incorrect extension is dialed and the transferer should try again.</synopsis>
142  </configOption>
143  <configOption name="transferinvalidsound" default="privacy-incorrect">
144  <synopsis>Sound that is played when an incorrect extension is dialed and the transferer has no attempts remaining.</synopsis>
145  </configOption>
146  </configObject>
147  <configObject name="featuremap">
148  <synopsis>DTMF options that can be triggered during bridged calls</synopsis>
149  <configOption name="atxfer">
150  <synopsis>DTMF sequence to initiate an attended transfer</synopsis>
151  <description>
152  <para>The transferee parties will be placed on hold and the
153  transferrer may dial an extension to reach a transfer target. During an
154  attended transfer, the transferrer may consult with the transfer target
155  before completing the transfer. Once the transferrer has hung up or pressed
156  the <replaceable>atxfercomplete</replaceable> DTMF sequence, then the transferees
157  and transfer target will be bridged.</para>
158  </description>
159  </configOption>
160  <configOption name="blindxfer" default="#">
161  <synopsis>DTMF sequence to initiate a blind transfer</synopsis>
162  <description>
163  <para>The transferee parties will be placed on hold and the
164  transferrer may dial an extension to reach a transfer target. During a
165  blind transfer, as soon as the transfer target is dialed, the transferrer
166  is hung up.</para>
167  </description>
168  </configOption>
169  <configOption name="disconnect" default="*">
170  <synopsis>DTMF sequence to disconnect the current call</synopsis>
171  <description>
172  <para>Entering this DTMF sequence will cause the bridge to end, no
173  matter the number of parties present</para>
174  </description>
175  </configOption>
176  <configOption name="parkcall">
177  <synopsis>DTMF sequence to park a call</synopsis>
178  <description>
179  <para>The parking lot used to park the call is determined by using either the
180  <replaceable>PARKINGLOT</replaceable> channel variable or a configured value on
181  the channel (provided by the channel driver) if the variable is not present. If
182  no configured value on the channel is present, then <literal>"default"</literal>
183  is used. The call is parked in the next available space in the parking lot.</para>
184  </description>
185  </configOption>
186  <configOption name="automon">
187  <synopsis>DTMF sequence to start or stop monitoring a call</synopsis>
188  <description>
189  <para>This will cause the channel that pressed the DTMF sequence
190  to be monitored by the <literal>Monitor</literal> application. The
191  format for the recording is determined by the <replaceable>TOUCH_MONITOR_FORMAT</replaceable>
192  channel variable. If this variable is not specified, then <literal>wav</literal> is the
193  default. The filename is constructed in the following manner:</para>
194 
195  <para> prefix-timestamp-filename</para>
196 
197  <para>where prefix is either the value of the <replaceable>TOUCH_MONITOR_PREFIX</replaceable>
198  channel variable or <literal>auto</literal> if the variable is not set. The timestamp
199  is a UNIX timestamp. The filename is either the value of the <replaceable>TOUCH_MONITOR</replaceable>
200  channel variable or the callerID of the channels if the variable is not set.</para>
201  </description>
202  </configOption>
203  <configOption name="automixmon">
204  <synopsis>DTMF sequence to start or stop mixmonitoring a call </synopsis>
205  <description>
206  <para>Operation of the automixmon is similar to the <literal> automon </literal>
207  feature, with the following exceptions:
208  <replaceable>TOUCH_MIXMONITOR</replaceable> is used in place of <replaceable>TOUCH_MONITOR</replaceable>
209  <replaceable>TOUCH_MIXMONITOR_FORMAT</replaceable> is used in place of <replaceable>TOUCH_MIXMONITOR</replaceable>
210  There is no equivalent for <replaceable>TOUCH_MONITOR_PREFIX</replaceable>. <literal>"auto"</literal> is always how the filename begins.</para>
211  </description>
212  <see-also><ref type="configOption">automon</ref></see-also>
213  </configOption>
214  </configObject>
215  <configObject name="applicationmap">
216  <synopsis>Section for defining custom feature invocations during a call</synopsis>
217  <description>
218  <para>The applicationmap is an area where new custom features can be created. Items
219  defined in the applicationmap are not automatically accessible to bridged parties. Access
220  to the individual items is controled using the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
221  The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
222  either applicationmap item names or featuregroup names.</para>
223  </description>
224  <configOption name="">
225  <synopsis>A custom feature to invoke during a bridged call</synopsis>
226  <description>
227  <para>Each item listed here is a comma-separated list of parameters that determine
228  how a feature may be invoked during a call</para>
229  <para> Example:</para>
230  <para> eggs = *5,self,Playback(hello-world),default</para>
231  <para>This would create a feature called <literal>eggs</literal> that could be invoked
232  during a call by pressing the <literal>*5</literal>. The party that presses the DTMF
233  sequence would then trigger the <literal>Playback</literal> application to play the
234  <literal>hello-world</literal> file. The application invocation would happen on the
235  party that pressed the DTMF sequence since <literal>self</literal> is specified. The
236  other parties in the bridge would hear the <literal>default</literal> music on hold
237  class during the playback.</para>
238  <para>In addition to the syntax outlined in this documentation, a backwards-compatible alternative
239  is also allowed. The following applicationmap lines are functionally identical:</para>
240  <para> eggs = *5,self,Playback(hello-world),default</para>
241  <para> eggs = *5,self,Playback,hello-world,default</para>
242  <para> eggs = *5,self,Playback,"hello-world",default</para>
243  </description>
244  <syntax argsep=",">
245  <parameter name="dtmf" required="true">
246  <para>The DTMF sequence used to trigger the option</para>
247  </parameter>
248  <parameter name="activate_on" required="true">
249  <para>The party that the feature will be invoked on</para>
250  <optionlist>
251  <option name="self"><para>Feature is invoked on party that presses the DTMF sequence</para></option>
252  <option name="peer"><para>Feature is invoked on other parties in the bridge</para></option>
253  </optionlist>
254  </parameter>
255  <parameter name="app" required="true">
256  <para>The dialplan application to run when the DTMF sequence is pressed</para>
257  <argument name="app_args" required="false">
258  <para>The arguments to the dialplan application to run</para>
259  </argument>
260  </parameter>
261  <parameter name="moh_class" required="false">
262  <para>Music on hold class to play to bridge participants that are not the target of the application invocation</para>
263  </parameter>
264  </syntax>
265  </configOption>
266  </configObject>
267  <configObject name="featuregroup">
268  <synopsis>Groupings of items from the applicationmap</synopsis>
269  <description>
270  <para>Feature groups allow for multiple applicationmap items to be
271  grouped together. Like with individual applicationmap items, feature groups
272  can be part of the <replaceable>DYNAMIC_FEATURES</replaceable> channel variable.
273  In addition to creating groupings, the feature group section allows for the
274  DTMF sequence used to invoke an applicationmap item to be overridden with
275  a different sequence.</para>
276  </description>
277  <configOption name="">
278  <synopsis>Applicationmap item to place in the feature group</synopsis>
279  <description>
280  <para>Each item here must be a name of an item in the applicationmap. The
281  argument may either be a new DTMF sequence to use for the item or it
282  may be left blank in order to use the DTMF sequence specified in the
283  applicationmap. For example:</para>
284  <para> eggs => *1</para>
285  <para> bacon =></para>
286  <para>would result in the applicationmap items <literal>eggs</literal> and
287  <literal>bacon</literal> being in the featuregroup. The former would have its
288  default DTMF trigger overridden with <literal>*1</literal> and the latter would
289  have the DTMF value specified in the applicationmap.</para>
290  </description>
291  </configOption>
292  </configObject>
293  </configFile>
294  </configInfo>
295  <function name="FEATURE" language="en_US">
296  <synopsis>
297  Get or set a feature option on a channel.
298  </synopsis>
299  <syntax>
300  <parameter name="option_name" required="true">
301  <para>The allowed values are:</para>
302  <enumlist>
303  <enum name="inherit"><para>Inherit feature settings made in FEATURE or FEATUREMAP to child channels.</para></enum>
304  <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>
305  <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>
306  <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>
307  <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>
308  <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>
309  <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>
310  <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>
311  <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>
312  <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>
313  <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>
314  <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>
315  <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>
316  <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>
317  <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>
318  <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>
319  <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>
320  <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>
321  <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>
322  <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>
323  </enumlist>
324  </parameter>
325  </syntax>
326  <description>
327  <para>When this function is used as a read, it will get the current
328  value of the specified feature option for this channel. It will be
329  the value of this option configured in features.conf if a channel specific
330  value has not been set. This function can also be used to set a channel
331  specific value for the supported feature options.</para>
332  </description>
333  <see-also>
334  <ref type="function">FEATUREMAP</ref>
335  </see-also>
336  </function>
337  <function name="FEATUREMAP" language="en_US">
338  <synopsis>
339  Get or set a feature map to a given value on a specific channel.
340  </synopsis>
341  <syntax>
342  <parameter name="feature_name" required="true">
343  <para>The allowed values are:</para>
344  <enumlist>
345  <enum name="atxfer"><para>Attended Transfer</para></enum>
346  <enum name="blindxfer"><para>Blind Transfer</para></enum>
347  <enum name="automon"><para>Auto Monitor</para></enum>
348  <enum name="disconnect"><para>Call Disconnect</para></enum>
349  <enum name="parkcall"><para>Park Call</para></enum>
350  <enum name="automixmon"><para>Auto MixMonitor</para></enum>
351  </enumlist>
352  </parameter>
353  </syntax>
354  <description>
355  <para>When this function is used as a read, it will get the current
356  digit sequence mapped to the specified feature for this channel. This
357  value will be the one configured in features.conf if a channel specific
358  value has not been set. This function can also be used to set a channel
359  specific value for a feature mapping.</para>
360  </description>
361  <see-also>
362  <ref type="function">FEATURE</ref>
363  </see-also>
364  </function>
365  ***/
366 /*! Default general options */
367 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 1000
368 #define DEFAULT_COURTESY_TONE ""
369 #define DEFAULT_RECORDING_FAIL_SOUND ""
370 
371 /*! Default xfer options */
372 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3
373 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15
374 #define DEFAULT_ATXFER_DROP_CALL 0
375 #define DEFAULT_ATXFER_LOOP_DELAY 10
376 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
377 #define DEFAULT_XFERSOUND "beep"
378 #define DEFAULT_XFERFAILSOUND "beeperr"
379 #define DEFAULT_ATXFER_ABORT "*1"
380 #define DEFAULT_ATXFER_COMPLETE "*2"
381 #define DEFAULT_ATXFER_THREEWAY "*3"
382 #define DEFAULT_ATXFER_SWAP "*4"
383 #define DEFAULT_TRANSFER_DIAL_ATTEMPTS 3
384 #define DEFAULT_TRANSFER_RETRY_SOUND "pbx-invalid"
385 #define DEFAULT_TRANSFER_INVALID_SOUND "privacy-incorrect"
386 
387 /*! Default pickup options */
388 #define DEFAULT_PICKUPEXTEN "*8"
389 #define DEFAULT_PICKUPSOUND ""
390 #define DEFAULT_PICKUPFAILSOUND ""
391 
392 /*! Default featuremap options */
393 #define DEFAULT_FEATUREMAP_BLINDXFER "#"
394 #define DEFAULT_FEATUREMAP_DISCONNECT "*"
395 #define DEFAULT_FEATUREMAP_AUTOMON ""
396 #define DEFAULT_FEATUREMAP_ATXFER ""
397 #define DEFAULT_FEATUREMAP_PARKCALL ""
398 #define DEFAULT_FEATUREMAP_AUTOMIXMON ""
399 
400 /*!
401  * \brief Configuration from the "general" section of features.conf
402  */
407 };
408 
410 {
411  struct ast_applicationmap_item *item = obj;
412 
414 }
415 
416 static int applicationmap_sort(const void *obj, const void *arg, int flags)
417 {
418  const struct ast_applicationmap_item *item1 = obj;
419  const struct ast_applicationmap_item *item2;
420  const char *key2;
421 
422  switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
423  case OBJ_KEY:
424  key2 = arg;
425  return strcasecmp(item1->name, key2);
426  case OBJ_PARTIAL_KEY:
427  key2 = arg;
428  return strncasecmp(item1->name, key2, strlen(key2));
429  default:
430  case OBJ_POINTER:
431  item2 = arg;
432  return strcasecmp(item1->name, item2->name);
433  }
434 }
435 
436 /*!
437  * \brief Entry in the container of featuregroups
438  */
441  /*! The name of the applicationmap item that we are referring to */
442  AST_STRING_FIELD(appmap_item_name);
443  /*! Custom DTMF override to use instead of the default for the applicationmap item */
444  AST_STRING_FIELD(dtmf_override);
445  );
446  /*! The applicationmap item that is being referred to */
448 };
449 
450 static void featuregroup_item_destructor(void *obj)
451 {
452  struct featuregroup_item *item = obj;
453 
455  ao2_cleanup(item->appmap_item);
456 }
457 
458 static int group_item_sort(const void *obj, const void *arg, int flags)
459 {
460  const struct featuregroup_item *item1 = obj;
461  const struct featuregroup_item *item2;
462  const char *key2;
463 
464  switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
465  case OBJ_KEY:
466  key2 = arg;
467  return strcasecmp(item1->appmap_item_name, key2);
468  case OBJ_PARTIAL_KEY:
469  key2 = arg;
470  return strncasecmp(item1->appmap_item_name, key2, strlen(key2));
471  case OBJ_POINTER:
472  item2 = arg;
473  return strcasecmp(item1->appmap_item_name, item2->appmap_item_name);
474  default:
475  return CMP_STOP;
476  }
477 }
478 
479 /*!
480  * \brief Featuregroup representation
481  */
482 struct featuregroup {
483  /*! The name of the featuregroup */
484  const char *name;
485  /*! A container of featuregroup_items */
487 };
488 
489 static int featuregroup_hash(const void *obj, int flags)
490 {
491  const struct featuregroup *group;
492  const char *key;
493 
494  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
495  case OBJ_KEY:
496  key = obj;
497  return ast_str_case_hash(key);
498  case OBJ_PARTIAL_KEY:
499  ast_assert(0);
500  return 0;
501  case OBJ_POINTER:
502  default:
503  group = obj;
504  return ast_str_case_hash(group->name);
505  }
506 }
507 
508 static int featuregroup_cmp(void *obj, void *arg, int flags)
509 {
510  struct featuregroup *group1 = obj;
511  struct featuregroup *group2;
512  const char *key2;
513 
514  switch(flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
515  case OBJ_KEY:
516  key2 = arg;
517  return strcasecmp(group1->name, key2) ? 0 : CMP_MATCH;
518  case OBJ_PARTIAL_KEY:
519  key2 = arg;
520  return strncasecmp(group1->name, key2, strlen(key2)) ? 0 : CMP_MATCH;
521  case OBJ_POINTER:
522  group2 = arg;
523  return strcasecmp(group1->name, group2->name) ? 0 : CMP_MATCH;
524  default:
525  return CMP_STOP;
526  }
527 }
528 
529 static void *featuregroup_find(struct ao2_container *group_container, const char *category)
530 {
531  return ao2_find(group_container, category, OBJ_KEY);
532 }
533 
534 static void featuregroup_destructor(void *obj)
535 {
536  struct featuregroup *group = obj;
537 
538  ast_free((char *) group->name);
539  ao2_cleanup(group->items);
540 }
541 
542 static void *featuregroup_alloc(const char *cat)
543 {
544  struct featuregroup *group;
545 
546  group = ao2_alloc(sizeof(*group), featuregroup_destructor);
547  if (!group) {
548  return NULL;
549  }
550 
551  group->name = ast_strdup(cat);
552  if (!group->name) {
553  ao2_cleanup(group);
554  return NULL;
555  }
556 
559  if (!group->items) {
560  ao2_cleanup(group);
561  return NULL;
562  }
563 
564  return group;
565 }
566 
567 /* Used for deprecated parking configuration */
568 struct dummy_config {
569  char dummy;
570 };
571 
578 };
579 
580 static struct aco_type global_option = {
581  .type = ACO_GLOBAL,
582  .name = "globals",
583  .category_match = ACO_WHITELIST_EXACT,
584  .category = "general",
585  .item_offset = offsetof(struct features_config, global),
586 };
587 
588 static struct aco_type featuremap_option = {
589  .type = ACO_GLOBAL,
590  .name = "featuremap",
591  .category_match = ACO_WHITELIST_EXACT,
592  .category = "featuremap",
593  .item_offset = offsetof(struct features_config, featuremap),
594 };
595 
597  .type = ACO_GLOBAL,
598  .name = "applicationmap",
599  .category_match = ACO_WHITELIST_EXACT,
600  .category = "applicationmap",
601  .item_offset = offsetof(struct features_config, applicationmap),
602 };
603 
604 static struct aco_type featuregroup_option = {
605  .type = ACO_ITEM,
606  .name = "featuregroup",
607  .category_match = ACO_BLACKLIST,
608  .category = "^(general|featuremap|applicationmap|parkinglot_.*)$",
609  .item_offset = offsetof(struct features_config, featuregroups),
610  .item_alloc = featuregroup_alloc,
611  .item_find = featuregroup_find,
612 };
613 
614 static struct aco_type parkinglot_option = {
615  .type = ACO_GLOBAL,
616  .name = "parkinglot",
617  .category_match = ACO_WHITELIST,
618  .category = "^parkinglot_.*$",
619  .item_offset = offsetof(struct features_config, parkinglots),
620  .hidden = 1,
621 };
622 
623 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
624 static struct aco_type *featuremap_options[] = ACO_TYPES(&featuremap_option);
625 static struct aco_type *applicationmap_options[] = ACO_TYPES(&applicationmap_option);
626 static struct aco_type *featuregroup_options[] = ACO_TYPES(&featuregroup_option);
627 static struct aco_type *parkinglot_options[] = ACO_TYPES(&parkinglot_option);
628 
629 static struct aco_file features_conf = {
630  .filename = "features.conf",
631  .types = ACO_TYPES(&global_option, &featuremap_option, &applicationmap_option, &featuregroup_option, &parkinglot_option),
632 };
633 
635 
636 static void features_config_destructor(void *obj)
637 {
638  struct features_config *cfg = obj;
639 
640  ao2_cleanup(cfg->global);
641  ao2_cleanup(cfg->featuremap);
642  ao2_cleanup(cfg->parkinglots);
645 }
646 
647 static void featuremap_config_destructor(void *obj)
648 {
649  struct ast_featuremap_config *cfg = obj;
650 
652 }
653 
654 static void global_config_destructor(void *obj)
655 {
656  struct features_global_config *cfg = obj;
657 
658  ao2_cleanup(cfg->general);
659  ao2_cleanup(cfg->xfer);
660  ao2_cleanup(cfg->pickup);
661 }
662 
663 static void general_destructor(void *obj)
664 {
665  struct ast_features_general_config *cfg = obj;
666 
668 }
669 
670 static void xfer_destructor(void *obj)
671 {
672  struct ast_features_xfer_config *cfg = obj;
673 
675 }
676 
677 static void pickup_destructor(void *obj)
678 {
679  struct ast_features_pickup_config *cfg = obj;
680 
682 }
683 
685 {
687 
688  cfg = ao2_alloc(sizeof(*cfg), global_config_destructor);
689  if (!cfg) {
690  return NULL;
691  }
692 
693  cfg->general = ao2_alloc(sizeof(*cfg->general), general_destructor);
694  if (!cfg->general || ast_string_field_init(cfg->general, 32)) {
695  return NULL;
696  }
697 
698  cfg->xfer = ao2_alloc(sizeof(*cfg->xfer), xfer_destructor);
699  if (!cfg->xfer || ast_string_field_init(cfg->xfer, 32)) {
700  return NULL;
701  }
702 
703  cfg->pickup = ao2_alloc(sizeof(*cfg->pickup), pickup_destructor);
704  if (!cfg->pickup || ast_string_field_init(cfg->pickup, 32)) {
705  return NULL;
706  }
707 
708  ao2_ref(cfg, +1);
709  return cfg;
710 }
711 
712 static struct ao2_container *applicationmap_alloc(int replace_duplicates)
713 {
717 }
718 
719 /*!
720  * \internal
721  * \brief Allocate the major configuration structure
722  *
723  * The parameter is used to determine if the applicationmap and featuregroup
724  * structures should be allocated. We only want to allocate these structures for
725  * the global features_config structure. For the datastores on channels, we don't
726  * need to allocate these structures because they are not used.
727  *
728  * \param allocate_applicationmap See previous explanation
729  * \retval NULL Failed to alloate configuration
730  * \retval non-NULL Allocated configuration
731  */
732 static struct features_config *__features_config_alloc(int allocate_applicationmap)
733 {
734  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
735 
736  cfg = ao2_alloc(sizeof(*cfg), features_config_destructor);
737  if (!cfg) {
738  return NULL;
739  }
740 
741  cfg->global = global_config_alloc();
742  if (!cfg->global) {
743  return NULL;
744  }
745 
746  cfg->featuremap = ao2_alloc(sizeof(*cfg->featuremap), featuremap_config_destructor);
747  if (!cfg->featuremap || ast_string_field_init(cfg->featuremap, 32)) {
748  return NULL;
749  }
750 
751  cfg->parkinglots = ao2_alloc(sizeof(*cfg->parkinglots), NULL);
752  if (!cfg->parkinglots) {
753  return NULL;
754  }
755 
756  if (allocate_applicationmap) {
757  cfg->applicationmap = applicationmap_alloc(1);
758  if (!cfg->applicationmap) {
759  return NULL;
760  }
761 
762  cfg->featuregroups = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 11,
764  if (!cfg->featuregroups) {
765  return NULL;
766  }
767  }
768 
769  ao2_ref(cfg, +1);
770  return cfg;
771 
772 }
773 
774 static void *features_config_alloc(void)
775 {
776  return __features_config_alloc(1);
777 }
778 
779 static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
780 {
781  ast_string_fields_copy(dest, src);
783 }
784 
785 static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
786 {
787  ast_string_fields_copy(dest, src);
790  dest->atxferloopdelay = src->atxferloopdelay;
792  dest->atxferdropcall = src->atxferdropcall;
794 }
795 
796 static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
797 {
798  ast_string_fields_copy(dest, src);
799 }
800 
801 static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
802 {
803  general_copy(dest->general, src->general);
804  xfer_copy(dest->xfer, src->xfer);
805  pickup_copy(dest->pickup, src->pickup);
806 }
807 
808 static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
809 {
810  ast_string_fields_copy(dest, src);
811 }
812 
813 static void features_copy(struct features_config *dest, const struct features_config *src)
814 {
815  global_copy(dest->global, src->global);
817 
818  /* applicationmap and featuregroups are purposely not copied. A channel's applicationmap
819  * is produced on the fly when ast_get_chan_applicationmap() is called
820  * NOTE: This does not apply to the global cfg->applicationmap and cfg->featuresgroups
821  */
822 }
823 
824 static struct features_config *features_config_dup(const struct features_config *orig)
825 {
826  struct features_config *dup;
827 
828  dup = __features_config_alloc(0);
829  if (!dup) {
830  return NULL;
831  }
832 
833  features_copy(dup, orig);
834 
835  return dup;
836 }
837 
838 static int general_set(struct ast_features_general_config *general, const char *name,
839  const char *value)
840 {
841  int res = 0;
842 
843  if (!strcasecmp(name, "featuredigittimeout")) {
844  res = ast_parse_arg(value, PARSE_INT32, &general->featuredigittimeout);
845  } else if (!strcasecmp(name, "courtesytone")) {
846  ast_string_field_set(general, courtesytone, value);
847  } else if (!strcasecmp(name, "recordingfailsound")) {
848  ast_string_field_set(general, recordingfailsound, value);
849  } else {
850  /* Unrecognized option */
851  res = -1;
852  }
853 
854  return res;
855 }
856 
857 static int general_get(struct ast_features_general_config *general, const char *field,
858  char *buf, size_t len)
859 {
860  int res = 0;
861 
862  if (!strcasecmp(field, "featuredigittimeout")) {
863  snprintf(buf, len, "%u", general->featuredigittimeout);
864  } else if (!strcasecmp(field, "courtesytone")) {
865  ast_copy_string(buf, general->courtesytone, len);
866  } else if (!strcasecmp(field, "recordingfailsound")) {
867  ast_copy_string(buf, general->recordingfailsound, len);
868  } else {
869  /* Unrecognized option */
870  res = -1;
871  }
872 
873  return res;
874 }
875 
876 static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
877  const char *value)
878 {
879  int res = 0;
880 
881  if (!strcasecmp(name, "transferdigittimeout")) {
882  res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdigittimeout);
883  } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
884  res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfernoanswertimeout);
885  } else if (!strcasecmp(name, "atxferloopdelay")) {
886  res = ast_parse_arg(value, PARSE_INT32, &xfer->atxferloopdelay);
887  } else if (!strcasecmp(name, "atxfercallbackretries")) {
888  res = ast_parse_arg(value, PARSE_INT32, &xfer->atxfercallbackretries);
889  } else if (!strcasecmp(name, "atxferdropcall")) {
890  xfer->atxferdropcall = ast_true(value);
891  } else if (!strcasecmp(name, "xfersound")) {
892  ast_string_field_set(xfer, xfersound, value);
893  } else if (!strcasecmp(name, "xferfailsound")) {
894  ast_string_field_set(xfer, xferfailsound, value);
895  } else if (!strcasecmp(name, "atxferabort")) {
896  ast_string_field_set(xfer, atxferabort, value);
897  } else if (!strcasecmp(name, "atxfercomplete")) {
898  ast_string_field_set(xfer, atxfercomplete, value);
899  } else if (!strcasecmp(name, "atxferthreeway")) {
900  ast_string_field_set(xfer, atxferthreeway, value);
901  } else if (!strcasecmp(name, "atxferswap")) {
902  ast_string_field_set(xfer, atxferswap, value);
903  } else if (!strcasecmp(name, "transferdialattempts")) {
904  res = ast_parse_arg(value, PARSE_INT32, &xfer->transferdialattempts);
905  } else if (!strcasecmp(name, "transferretrysound")) {
906  ast_string_field_set(xfer, transferretrysound, value);
907  } else if (!strcasecmp(name, "transferinvalidsound")) {
908  ast_string_field_set(xfer, transferinvalidsound, value);
909  } else {
910  /* Unrecognized option */
911  res = -1;
912  }
913 
914  return res;
915 }
916 
917 static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
918  char *buf, size_t len)
919 {
920  int res = 0;
921 
922  if (!strcasecmp(field, "transferdigittimeout")) {
923  snprintf(buf, len, "%u", xfer->transferdigittimeout);
924  } else if (!strcasecmp(field, "atxfernoanswertimeout")) {
925  snprintf(buf, len, "%u", xfer->atxfernoanswertimeout);
926  } else if (!strcasecmp(field, "atxferloopdelay")) {
927  snprintf(buf, len, "%u", xfer->atxferloopdelay);
928  } else if (!strcasecmp(field, "atxfercallbackretries")) {
929  snprintf(buf, len, "%u", xfer->atxfercallbackretries);
930  } else if (!strcasecmp(field, "atxferdropcall")) {
931  snprintf(buf, len, "%u", xfer->atxferdropcall);
932  } else if (!strcasecmp(field, "xfersound")) {
933  ast_copy_string(buf, xfer->xfersound, len);
934  } else if (!strcasecmp(field, "xferfailsound")) {
935  ast_copy_string(buf, xfer->xferfailsound, len);
936  } else if (!strcasecmp(field, "atxferabort")) {
937  ast_copy_string(buf, xfer->atxferabort, len);
938  } else if (!strcasecmp(field, "atxfercomplete")) {
939  ast_copy_string(buf, xfer->atxfercomplete, len);
940  } else if (!strcasecmp(field, "atxferthreeway")) {
941  ast_copy_string(buf, xfer->atxferthreeway, len);
942  } else if (!strcasecmp(field, "atxferswap")) {
943  ast_copy_string(buf, xfer->atxferswap, len);
944  } else if (!strcasecmp(field, "transferdialattempts")) {
945  snprintf(buf, len, "%u", xfer->transferdialattempts);
946  } else if (!strcasecmp(field, "transferretrysound")) {
947  ast_copy_string(buf, xfer->transferretrysound, len);
948  } else if (!strcasecmp(field, "transferinvalidsound")) {
949  ast_copy_string(buf, xfer->transferinvalidsound, len);
950  } else {
951  /* Unrecognized option */
952  res = -1;
953  }
954 
955  return res;
956 }
957 
958 static int pickup_set(struct ast_features_pickup_config *pickup, const char *name,
959  const char *value)
960 {
961  int res = 0;
962 
963  if (!strcasecmp(name, "pickupsound")) {
964  ast_string_field_set(pickup, pickupsound, value);
965  } else if (!strcasecmp(name, "pickupfailsound")) {
966  ast_string_field_set(pickup, pickupfailsound, value);
967  } else if (!strcasecmp(name, "pickupexten")) {
968  ast_string_field_set(pickup, pickupexten, value);
969  } else {
970  /* Unrecognized option */
971  res = -1;
972  }
973 
974  return res;
975 }
976 
977 static int pickup_get(struct ast_features_pickup_config *pickup, const char *field,
978  char *buf, size_t len)
979 {
980  int res = 0;
981 
982  if (!strcasecmp(field, "pickupsound")) {
983  ast_copy_string(buf, pickup->pickupsound, len);
984  } else if (!strcasecmp(field, "pickupfailsound")) {
985  ast_copy_string(buf, pickup->pickupfailsound, len);
986  } else if (!strcasecmp(field, "pickupexten")) {
987  ast_copy_string(buf, pickup->pickupexten, len);
988  } else {
989  /* Unrecognized option */
990  res = -1;
991  }
992 
993  return res;
994 }
995 
996 static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name,
997  const char *value)
998 {
999  int res = 0;
1000 
1001  if (!strcasecmp(name, "blindxfer")) {
1002  ast_string_field_set(featuremap, blindxfer, value);
1003  } else if (!strcasecmp(name, "disconnect")) {
1004  ast_string_field_set(featuremap, disconnect, value);
1005  } else if (!strcasecmp(name, "automon")) {
1006  ast_string_field_set(featuremap, automon, value);
1007  } else if (!strcasecmp(name, "atxfer")) {
1008  ast_string_field_set(featuremap, atxfer, value);
1009  } else if (!strcasecmp(name, "automixmon")) {
1010  ast_string_field_set(featuremap, automixmon, value);
1011  } else if (!strcasecmp(name, "parkcall")) {
1012  ast_string_field_set(featuremap, parkcall, value);
1013  } else {
1014  /* Unrecognized option */
1015  res = -1;
1016  }
1017 
1018  return res;
1019 }
1020 
1021 static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field,
1022  char *buf, size_t len)
1023 {
1024  int res = 0;
1025 
1026  if (!strcasecmp(field, "blindxfer")) {
1027  ast_copy_string(buf, featuremap->blindxfer, len);
1028  } else if (!strcasecmp(field, "disconnect")) {
1029  ast_copy_string(buf, featuremap->disconnect, len);
1030  } else if (!strcasecmp(field, "automon")) {
1031  ast_copy_string(buf, featuremap->automon, len);
1032  } else if (!strcasecmp(field, "atxfer")) {
1033  ast_copy_string(buf, featuremap->atxfer, len);
1034  } else if (!strcasecmp(field, "automixmon")) {
1035  ast_copy_string(buf, featuremap->automixmon, len);
1036  } else if (!strcasecmp(field, "parkcall")) {
1037  ast_copy_string(buf, featuremap->parkcall, len);
1038  } else {
1039  /* Unrecognized option */
1040  res = -1;
1041  }
1042 
1043  return res;
1044 }
1045 
1046 static void feature_ds_destroy(void *data)
1047 {
1048  struct features_config *cfg = data;
1049  ao2_cleanup(cfg);
1050 }
1051 
1052 static void *feature_ds_duplicate(void *data)
1053 {
1054  struct features_config *old_cfg = data;
1055 
1056  return features_config_dup(old_cfg);
1057 }
1058 
1059 static const struct ast_datastore_info feature_ds_info = {
1060  .type = "FEATURE",
1061  .destroy = feature_ds_destroy,
1062  .duplicate = feature_ds_duplicate,
1063 };
1064 
1065 /*!
1066  * \internal
1067  * \brief Find or create feature datastore on a channel
1068  *
1069  * \pre chan is locked
1070  *
1071  * \return the data on the FEATURE datastore, or NULL on error
1072  */
1073 static struct features_config *get_feature_ds(struct ast_channel *chan)
1074 {
1075  RAII_VAR(struct features_config *, orig, NULL, ao2_cleanup);
1076  struct features_config *cfg;
1077  struct ast_datastore *ds;
1078 
1079  if ((ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1080  cfg = ds->data;
1081  ao2_ref(cfg, +1);
1082  return cfg;
1083  }
1084 
1085  orig = ao2_global_obj_ref(globals);
1086  if (!orig) {
1087  return NULL;
1088  }
1089 
1090  cfg = features_config_dup(orig);
1091  if (!cfg) {
1092  return NULL;
1093  }
1094 
1095  if (!(ds = ast_datastore_alloc(&feature_ds_info, NULL))) {
1096  ao2_cleanup(cfg);
1097  return NULL;
1098  }
1099 
1100  /* Give the datastore a reference to the config */
1101  ao2_ref(cfg, +1);
1102  ds->data = cfg;
1103 
1104  ast_channel_datastore_add(chan, ds);
1105 
1106  return cfg;
1107 }
1108 
1109 static struct ast_datastore *get_feature_chan_ds(struct ast_channel *chan)
1110 {
1111  struct ast_datastore *ds;
1112 
1113  if (!(ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL))) {
1114  /* Hasn't been created yet. Trigger creation. */
1115  ao2_cleanup(get_feature_ds(chan));
1116 
1117  ds = ast_channel_datastore_find(chan, &feature_ds_info, NULL);
1118  }
1119 
1120  return ds;
1121 }
1122 
1124 {
1125  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1126 
1127  if (chan) {
1128  cfg = get_feature_ds(chan);
1129  } else {
1130  cfg = ao2_global_obj_ref(globals);
1131  }
1132 
1133  if (!cfg) {
1134  return NULL;
1135  }
1136 
1137  ast_assert(cfg->global && cfg->global->general);
1138 
1139  ao2_ref(cfg->global->general, +1);
1140  return cfg->global->general;
1141 }
1142 
1144 {
1145  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1146 
1147  if (chan) {
1148  cfg = get_feature_ds(chan);
1149  } else {
1150  cfg = ao2_global_obj_ref(globals);
1151  }
1152 
1153  if (!cfg) {
1154  return NULL;
1155  }
1156 
1157  ast_assert(cfg->global && cfg->global->xfer);
1158 
1159  ao2_ref(cfg->global->xfer, +1);
1160  return cfg->global->xfer;
1161 }
1162 
1164 {
1165  char *res;
1167 
1168  if (!cfg) {
1169  return NULL;
1170  }
1171 
1172  res = ast_strdup(cfg->xferfailsound);
1173  ao2_ref(cfg, -1);
1174 
1175  return res;
1176 }
1177 
1179 {
1180  char *res;
1182 
1183  if (!cfg) {
1184  return NULL;
1185  }
1186 
1187  res = ast_strdup(cfg->atxferabort);
1188  ao2_ref(cfg, -1);
1189 
1190  return res;
1191 }
1192 
1194 {
1195  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1196 
1197  if (chan) {
1198  cfg = get_feature_ds(chan);
1199  } else {
1200  cfg = ao2_global_obj_ref(globals);
1201  }
1202 
1203  if (!cfg) {
1204  return NULL;
1205  }
1206 
1207  ast_assert(cfg->global && cfg->global->pickup);
1208 
1209  ao2_ref(cfg->global->pickup, +1);
1210  return cfg->global->pickup;
1211 }
1212 
1214 {
1215  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1216 
1217  if (chan) {
1218  cfg = get_feature_ds(chan);
1219  } else {
1220  cfg = ao2_global_obj_ref(globals);
1221  }
1222 
1223  if (!cfg) {
1224  return NULL;
1225  }
1226 
1227  ast_assert(cfg->featuremap != NULL);
1228 
1229  ao2_ref(cfg->featuremap, +1);
1230  return cfg->featuremap;
1231 }
1232 
1233 int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1234 {
1235  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1236 
1237  if (chan) {
1238  cfg = get_feature_ds(chan);
1239  } else {
1240  cfg = ao2_global_obj_ref(globals);
1241  }
1242 
1243  if (!cfg) {
1244  return -1;
1245  }
1246 
1247  return featuremap_get(cfg->featuremap, feature, buf, len);
1248 }
1249 
1250 int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
1251 {
1252  RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
1254 
1255  if (!ast_get_builtin_feature(chan, feature, buf, len)) {
1256  return 0;
1257  }
1258 
1259  /* Dang, must be in the application map */
1260  applicationmap = ast_get_chan_applicationmap(chan);
1261  if (!applicationmap) {
1262  return -1;
1263  }
1264 
1265  item = ao2_find(applicationmap, feature, OBJ_KEY);
1266  if (!item) {
1267  return -1;
1268  }
1269 
1270  ast_copy_string(buf, item->dtmf, len);
1271  return 0;
1272 }
1273 
1275  const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1276  unsigned int activate_on_self)
1277 {
1278  struct ast_applicationmap_item *item;
1279 
1280  item = ao2_alloc(sizeof(*item), ast_applicationmap_item_destructor);
1281 
1282  if (!item || ast_string_field_init(item, 64)) {
1283  return NULL;
1284  }
1285 
1286  ast_string_field_set(item, name, name);
1287  ast_string_field_set(item, app, app);
1288  ast_string_field_set(item, app_data, app_data);
1289  ast_string_field_set(item, moh_class, moh_class);
1290  ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1292 
1293  return item;
1294 }
1295 
1296 static int add_item(void *obj, void *arg, int flags)
1297 {
1298  struct featuregroup_item *fg_item = obj;
1299  struct ao2_container *applicationmap = arg;
1300  RAII_VAR(struct ast_applicationmap_item *, appmap_item, NULL, ao2_cleanup);
1301 
1302  /* If there's no DTMF override, then we can just link
1303  * the applicationmap item directly. Otherwise, we need
1304  * to create a copy with the DTMF override in place and
1305  * link that instead
1306  */
1307  if (ast_strlen_zero(fg_item->dtmf_override)) {
1308  ao2_ref(fg_item->appmap_item, +1);
1309  appmap_item = fg_item->appmap_item;
1310  } else {
1311  appmap_item = applicationmap_item_alloc(fg_item->appmap_item_name,
1312  fg_item->appmap_item->app, fg_item->appmap_item->app_data,
1313  fg_item->appmap_item->moh_class, fg_item->dtmf_override,
1314  fg_item->appmap_item->activate_on_self);
1315  }
1316 
1317  if (!appmap_item) {
1318  return 0;
1319  }
1320 
1321  ao2_link(applicationmap, appmap_item);
1322  return 0;
1323 }
1324 
1326 {
1328  struct ao2_container *applicationmap;
1329  char *group_names;
1330  char *name;
1331 
1332  if (!cfg) {
1333  return NULL;
1334  }
1335 
1336  if (!chan) {
1337  if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1338  return NULL;
1339  }
1340  ao2_ref(cfg->applicationmap, +1);
1341  return cfg->applicationmap;
1342  }
1343 
1344  group_names = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES"), ""));
1345  if (ast_strlen_zero(group_names)) {
1346  return NULL;
1347  }
1348 
1349  applicationmap = applicationmap_alloc(0);
1350  if (!applicationmap) {
1351  return NULL;
1352  }
1353 
1354  /* global config must be initialized */
1355  ast_assert(cfg->featuregroups != NULL);
1356  ast_assert(cfg->applicationmap != NULL);
1357  while ((name = strsep(&group_names, "#"))) {
1358  RAII_VAR(struct featuregroup *, group, ao2_find(cfg->featuregroups, name, OBJ_KEY), ao2_cleanup);
1359 
1360  if (!group) {
1361  RAII_VAR(struct ast_applicationmap_item *, item, ao2_find(cfg->applicationmap, name, OBJ_KEY), ao2_cleanup);
1362 
1363  if (item) {
1364  ao2_link(applicationmap, item);
1365  } else {
1366  ast_log(LOG_WARNING, "Unknown DYNAMIC_FEATURES item '%s' on channel %s.\n",
1367  name, ast_channel_name(chan));
1368  }
1369  } else {
1370  ao2_callback(group->items, 0, add_item, applicationmap);
1371  }
1372  }
1373 
1374  if (ao2_container_count(applicationmap) == 0) {
1375  ao2_cleanup(applicationmap);
1376  return NULL;
1377  }
1378 
1379  return applicationmap;
1380 }
1381 
1382 static int applicationmap_handler(const struct aco_option *opt,
1383  struct ast_variable *var, void *obj)
1384 {
1386  struct ao2_container *applicationmap = obj;
1388  AST_APP_ARG(dtmf);
1389  AST_APP_ARG(activate_on);
1390  AST_APP_ARG(app);
1392  AST_APP_ARG(moh_class);
1393  );
1394  char *parse = ast_strdupa(var->value);
1395  char *slash;
1396  char *paren;
1397  unsigned int activate_on_self;
1398 
1399  AST_STANDARD_APP_ARGS(args, parse);
1400 
1401  if (ast_strlen_zero(args.dtmf) ||
1402  ast_strlen_zero(args.activate_on) ||
1403  ast_strlen_zero(args.app)) {
1404  ast_log(LOG_WARNING, "Invalid applicationmap syntax for '%s'. Missing required argument\n", var->name);
1405  return -1;
1406  }
1407 
1408  /* features.conf used to have an "activated_by" portion
1409  * in addition to activate_on. Get rid of whatever may be
1410  * there
1411  */
1412  slash = strchr(args.activate_on, '/');
1413  if (slash) {
1414  *slash = '\0';
1415  }
1416 
1417  /* Some applications do not require arguments. */
1418  if (!args.app_data) {
1419  args.app_data = "";
1420  }
1421 
1422  /* Two syntaxes allowed for applicationmap:
1423  * Old: foo = *1,self,NoOp,Boo!,default
1424  * New: foo = *1,self,NoOp(Boo!),default
1425  *
1426  * We need to handle both
1427  */
1428  paren = strchr(args.app, '(');
1429  if (paren) {
1430  /* New syntax */
1431  char *close_paren;
1432 
1433  args.moh_class = args.app_data;
1434  *paren++ = '\0';
1435  close_paren = strrchr(paren, ')');
1436  if (close_paren) {
1437  *close_paren = '\0';
1438  }
1439  args.app_data = paren;
1440 
1441  /* Re-check that the application is not empty */
1442  if (ast_strlen_zero(args.app)) {
1443  ast_log(LOG_WARNING, "Applicationmap item '%s' does not contain an application name.\n", var->name);
1444  return -1;
1445  }
1446  } else if (strchr(args.app_data, '"')) {
1447  args.app_data = ast_strip_quoted(args.app_data, "\"", "\"");
1448  }
1449 
1450  /* Allow caller and callee to be specified for backwards compatibility */
1451  if (!strcasecmp(args.activate_on, "self") || !strcasecmp(args.activate_on, "caller")) {
1452  activate_on_self = 1;
1453  } else if (!strcasecmp(args.activate_on, "peer") || !strcasecmp(args.activate_on, "callee")) {
1454  activate_on_self = 0;
1455  } else {
1456  ast_log(LOG_WARNING, "Invalid 'activate_on' value %s for applicationmap item %s\n",
1457  args.activate_on, var->name);
1458  return -1;
1459  }
1460 
1461  ast_debug(1, "Allocating applicationmap item: dtmf = %s, app = %s, app_data = %s, moh_class = %s\n",
1462  args.dtmf, args.app, args.app_data, args.moh_class);
1463 
1464  item = applicationmap_item_alloc(var->name, args.app, args.app_data,
1465  args.moh_class, args.dtmf, activate_on_self);
1466 
1467  if (!item) {
1468  return -1;
1469  }
1470 
1471  if (!ao2_link(applicationmap, item)) {
1472  return -1;
1473  }
1474 
1475  return 0;
1476 }
1477 
1478 static int featuregroup_handler(const struct aco_option *opt,
1479  struct ast_variable *var, void *obj)
1480 {
1482  struct featuregroup *group = obj;
1483 
1485  if (!item || ast_string_field_init(item, 32)) {
1486  return -1;
1487  }
1488 
1489  ast_string_field_set(item, appmap_item_name, var->name);
1490  ast_string_field_set(item, dtmf_override, var->value);
1491 
1492  if (!ao2_link(group->items, item)) {
1493  return -1;
1494  }
1495 
1496  /* We wait to look up the application map item in the preapply callback */
1497 
1498  return 0;
1499 }
1500 
1501 static int general_handler(const struct aco_option *opt,
1502  struct ast_variable *var, void *obj)
1503 {
1504  struct features_global_config *global = obj;
1505  struct ast_features_general_config *general = global->general;
1506 
1507  return general_set(general, var->name, var->value);
1508 }
1509 
1510 static int xfer_handler(const struct aco_option *opt,
1511  struct ast_variable *var, void *obj)
1512 {
1513  struct features_global_config *global = obj;
1514  struct ast_features_xfer_config *xfer = global->xfer;
1515 
1516  return xfer_set(xfer, var->name, var->value);
1517 }
1518 
1519 static int pickup_handler(const struct aco_option *opt,
1520  struct ast_variable *var, void *obj)
1521 {
1522  struct features_global_config *global = obj;
1523  struct ast_features_pickup_config *pickup = global->pickup;
1524 
1525  return pickup_set(pickup, var->name, var->value);
1526 }
1527 
1528 static int parking_warning = 0;
1529 static int unsupported_handler(const struct aco_option *opt,
1530  struct ast_variable *var, void *obj)
1531 {
1532  if (!parking_warning) {
1533  ast_log(LOG_WARNING, "Parkinglots are no longer configurable in features.conf; "
1534  "parking is now handled by res_parking.conf\n");
1535  parking_warning = 1;
1536  }
1537  ast_log(LOG_WARNING, "The option '%s' is no longer configurable in features.conf.\n", var->name);
1538  return 0;
1539 }
1540 
1541 static int featuremap_handler(const struct aco_option *opt,
1542  struct ast_variable *var, void *obj)
1543 {
1544  struct ast_featuremap_config *featuremap = obj;
1545 
1546  return featuremap_set(featuremap, var->name, var->value);
1547 }
1548 
1549 static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
1550 {
1551  struct ast_applicationmap_item *appmap_item;
1552  struct featuregroup_item *fg_item = obj;
1553  int *err = arg;
1554  struct ao2_container *applicationmap = data;
1555 
1556  appmap_item = ao2_find(applicationmap, fg_item->appmap_item_name, OBJ_KEY);
1557  if (!appmap_item) {
1558  *err = 1;
1559  return CMP_STOP;
1560  }
1561 
1562  fg_item->appmap_item = appmap_item;
1563 
1564  return 0;
1565 }
1566 
1567 static int check_featuregroup(void *obj, void *arg, void *data, int flags)
1568 {
1569  struct featuregroup *group = obj;
1570  int *err = arg;
1571 
1572  ao2_callback_data(group->items, 0, check_featuregroup_item, arg, data);
1573 
1574  if (*err) {
1575  ast_log(LOG_WARNING, "Featuregroup %s refers to non-existent applicationmap item\n",
1576  group->name);
1577  }
1578 
1579  return *err ? CMP_STOP : 0;
1580 }
1581 
1582 static int features_pre_apply_config(void);
1583 
1584 CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,
1585  .files = ACO_FILES(&features_conf),
1586  .pre_apply_config = features_pre_apply_config,
1587 );
1588 
1590 {
1591  struct features_config *cfg = aco_pending_config(&cfg_info);
1592  int err = 0;
1593 
1594  /* Now that the entire config has been processed, we can check that the featuregroup
1595  * items refer to actual applicationmap items.
1596  */
1597 
1598  /* global config must be initialized */
1599  ast_assert(cfg->featuregroups != NULL);
1600  ast_assert(cfg->applicationmap != NULL);
1602 
1603  return err;
1604 }
1605 
1606 static int internal_feature_read(struct ast_channel *chan, const char *cmd, char *data,
1607  char *buf, size_t len)
1608 {
1609  int res;
1610  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1611  SCOPED_CHANNELLOCK(lock, chan);
1612 
1613  if (!strcasecmp(data, "inherit")) {
1614  struct ast_datastore *ds = get_feature_chan_ds(chan);
1615  unsigned int inherit = ds ? ds->inheritance : 0;
1616 
1617  snprintf(buf, len, "%s", inherit ? "yes" : "no");
1618  return 0;
1619  }
1620 
1621  cfg = get_feature_ds(chan);
1622  if (!cfg) {
1623  return -1;
1624  }
1625 
1626  res = general_get(cfg->global->general, data, buf, len) &&
1627  xfer_get(cfg->global->xfer, data, buf, len) &&
1628  pickup_get(cfg->global->pickup, data, buf, len);
1629 
1630  if (res) {
1631  ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1632  }
1633 
1634  return res;
1635 }
1636 
1637 static int internal_feature_write(struct ast_channel *chan, const char *cmd, char *data,
1638  const char *value)
1639 {
1640  int res;
1641  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1642  SCOPED_CHANNELLOCK(lock, chan);
1643 
1644  if (!strcasecmp(data, "inherit")) {
1645  struct ast_datastore *ds = get_feature_chan_ds(chan);
1646  if (ds) {
1647  ds->inheritance = ast_true(value) ? DATASTORE_INHERIT_FOREVER : 0;
1648  }
1649  return 0;
1650  }
1651 
1652  if (!(cfg = get_feature_ds(chan))) {
1653  return -1;
1654  }
1655 
1656  res = general_set(cfg->global->general, data, value) &&
1657  xfer_set(cfg->global->xfer, data, value) &&
1658  pickup_set(cfg->global->pickup, data, value);
1659 
1660  if (res) {
1661  ast_log(LOG_WARNING, "Invalid argument '%s' to FEATURE()\n", data);
1662  }
1663 
1664  return res;
1665 }
1666 
1667 static int internal_featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1668  char *buf, size_t len)
1669 {
1670  int res;
1671  SCOPED_CHANNELLOCK(lock, chan);
1672 
1673  res = ast_get_builtin_feature(chan, data, buf, len);
1674 
1675  if (res) {
1676  ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1677  }
1678 
1679  return res;
1680 }
1681 
1682 static int internal_featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1683  const char *value)
1684 {
1685  int res;
1686  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1687  SCOPED_CHANNELLOCK(lock, chan);
1688 
1689  if (!(cfg = get_feature_ds(chan))) {
1690  return -1;
1691  }
1692 
1693  res = featuremap_set(cfg->featuremap, data, value);
1694  if (res) {
1695  ast_log(LOG_WARNING, "Invalid argument '%s' to FEATUREMAP()\n", data);
1696  return -1;
1697  }
1698 
1699  return 0;
1700 }
1701 
1702 static int feature_read(struct ast_channel *chan, const char *cmd, char *data,
1703  char *buf, size_t len)
1704 {
1705  if (!chan) {
1706  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1707  return -1;
1708  }
1709 
1710  return internal_feature_read(chan, cmd, data, buf, len);
1711 }
1712 
1713 static int feature_write(struct ast_channel *chan, const char *cmd, char *data,
1714  const char *value)
1715 {
1716  if (!chan) {
1717  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1718  return -1;
1719  }
1720 
1721  return internal_feature_write(chan, cmd, data, value);
1722 }
1723 
1724 static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data,
1725  char *buf, size_t len)
1726 {
1727  if (!chan) {
1728  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1729  return -1;
1730  }
1731 
1732  return internal_featuremap_read(chan, cmd, data, buf, len);
1733 }
1734 
1735 static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data,
1736  const char *value)
1737 {
1738  if (!chan) {
1739  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1740  return -1;
1741  }
1742 
1743  return internal_featuremap_write(chan, cmd, data, value);
1744 }
1745 
1747  .name = "FEATURE",
1748  .read = feature_read,
1749  .write = feature_write
1750 };
1751 
1753  .name = "FEATUREMAP",
1754  .read = featuremap_read,
1755  .write = featuremap_write
1756 };
1757 
1758 static int load_config(void)
1759 {
1760  if (aco_info_init(&cfg_info)) {
1761  ast_log(LOG_ERROR, "Unable to initialize configuration info for features\n");
1762  return -1;
1763  }
1764 
1765  aco_option_register_custom(&cfg_info, "featuredigittimeout", ACO_EXACT, global_options,
1767  aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1769  aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1771 
1772  aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1774  aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1776  aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1778  aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1780  aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1782  aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1784  aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1786  aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1788  aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1790  aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1792  aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
1794  aco_option_register_custom(&cfg_info, "transferdialattempts", ACO_EXACT, global_options,
1796  aco_option_register_custom(&cfg_info, "transferretrysound", ACO_EXACT, global_options,
1798  aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
1800 
1801  aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1803  aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1805  aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1807 
1808  aco_option_register_custom_nodoc(&cfg_info, "context", ACO_EXACT, global_options,
1809  "", unsupported_handler, 0);
1810  aco_option_register_custom_nodoc(&cfg_info, "parkext", ACO_EXACT, global_options,
1811  "", unsupported_handler, 0);
1812  aco_option_register_custom_nodoc(&cfg_info, "parkext_exclusive", ACO_EXACT, global_options,
1813  "", unsupported_handler, 0);
1814  aco_option_register_custom_nodoc(&cfg_info, "parkinghints", ACO_EXACT, global_options,
1815  "", unsupported_handler, 0);
1816  aco_option_register_custom_nodoc(&cfg_info, "parkedmusicclass", ACO_EXACT, global_options,
1817  "", unsupported_handler, 0);
1818  aco_option_register_custom_nodoc(&cfg_info, "parkingtime", ACO_EXACT, global_options,
1819  "", unsupported_handler, 0);
1820  aco_option_register_custom_nodoc(&cfg_info, "parkpos", ACO_EXACT, global_options,
1821  "", unsupported_handler, 0);
1822  aco_option_register_custom_nodoc(&cfg_info, "findslot", ACO_EXACT, global_options,
1823  "", unsupported_handler, 0);
1824  aco_option_register_custom_nodoc(&cfg_info, "parkedcalltransfers", ACO_EXACT, global_options,
1825  "", unsupported_handler, 0);
1826  aco_option_register_custom_nodoc(&cfg_info, "parkedcallreparking", ACO_EXACT, global_options,
1827  "", unsupported_handler, 0);
1828  aco_option_register_custom_nodoc(&cfg_info, "parkedcallhangup", ACO_EXACT, global_options,
1829  "", unsupported_handler, 0);
1830  aco_option_register_custom_nodoc(&cfg_info, "parkedcallrecording", ACO_EXACT, global_options,
1831  "", unsupported_handler, 0);
1832  aco_option_register_custom_nodoc(&cfg_info, "comebackcontext", ACO_EXACT, global_options,
1833  "", unsupported_handler, 0);
1834  aco_option_register_custom_nodoc(&cfg_info, "comebacktoorigin", ACO_EXACT, global_options,
1835  "", unsupported_handler, 0);
1836  aco_option_register_custom_nodoc(&cfg_info, "comebackdialtime", ACO_EXACT, global_options,
1837  "", unsupported_handler, 0);
1838  aco_option_register_custom_nodoc(&cfg_info, "parkeddynamic", ACO_EXACT, global_options,
1839  "", unsupported_handler, 0);
1840  aco_option_register_custom_nodoc(&cfg_info, "adsipark", ACO_EXACT, global_options,
1841  "", unsupported_handler, 0);
1842 
1843  aco_option_register_custom(&cfg_info, "blindxfer", ACO_EXACT, featuremap_options,
1845  aco_option_register_custom(&cfg_info, "disconnect", ACO_EXACT, featuremap_options,
1847  aco_option_register_custom(&cfg_info, "automon", ACO_EXACT, featuremap_options,
1849  aco_option_register_custom(&cfg_info, "atxfer", ACO_EXACT, featuremap_options,
1851  aco_option_register_custom(&cfg_info, "parkcall", ACO_EXACT, featuremap_options,
1853  aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
1855 
1856  aco_option_register_custom(&cfg_info, "", ACO_PREFIX, applicationmap_options,
1857  "", applicationmap_handler, 0);
1858 
1859  aco_option_register_custom(&cfg_info, "", ACO_PREFIX, featuregroup_options,
1860  "", featuregroup_handler, 0);
1861 
1862  aco_option_register_custom_nodoc(&cfg_info, "", ACO_PREFIX, parkinglot_options,
1863  "", unsupported_handler, 0);
1864 
1865  if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1866  RAII_VAR(struct features_config *, features_cfg, features_config_alloc(), ao2_cleanup);
1867 
1868  if (aco_set_defaults(&global_option, "general", features_cfg->global) ||
1869  aco_set_defaults(&featuremap_option, "featuremap", features_cfg->featuremap)) {
1870  ast_log(LOG_ERROR, "Failed to load features.conf and failed to initialize defaults.\n");
1871  return -1;
1872  }
1873 
1874  ast_log(LOG_NOTICE, "Could not load features config; using defaults\n");
1875  ao2_global_obj_replace_unref(globals, features_cfg);
1876  }
1877 
1878  return 0;
1879 }
1880 
1881 static int print_featuregroup(void *obj, void *arg, int flags)
1882 {
1883  struct featuregroup_item *item = obj;
1884  struct ast_cli_args *a = arg;
1885 
1886  ast_cli(a->fd, "===> --> %s (%s)\n", item->appmap_item_name,
1887  S_OR(item->dtmf_override, item->appmap_item->dtmf));
1888 
1889  return 0;
1890 }
1891 
1892 static int print_featuregroups(void *obj, void *arg, int flags)
1893 {
1894  struct featuregroup *group = obj;
1895  struct ast_cli_args *a = arg;
1896 
1897  ast_cli(a->fd, "===> Group: %s\n", group->name);
1898 
1899  ao2_callback(group->items, 0, print_featuregroup, a);
1900  return 0;
1901 }
1902 
1903 #define HFS_FORMAT "%-25s %-7s %-7s\n"
1904 
1905 static int print_applicationmap(void *obj, void *arg, int flags)
1906 {
1907  struct ast_applicationmap_item *item = obj;
1908  struct ast_cli_args *a = arg;
1909 
1910  ast_cli(a->fd, HFS_FORMAT, item->name, "no def", item->dtmf);
1911  return 0;
1912 }
1913 
1914 /*!
1915  * \brief CLI command to list configured features
1916  * \param e
1917  * \param cmd
1918  * \param a
1919  *
1920  * \retval CLI_SUCCESS on success.
1921  * \retval NULL when tab completion is used.
1922  */
1923 static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1924 {
1925  RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1926 
1927  switch (cmd) {
1928 
1929  case CLI_INIT:
1930  e->command = "features show";
1931  e->usage =
1932  "Usage: features show\n"
1933  " Lists configured features\n";
1934  return NULL;
1935  case CLI_GENERATE:
1936  return NULL;
1937  }
1938 
1939  cfg = ao2_global_obj_ref(globals);
1940 
1941  ast_cli(a->fd, HFS_FORMAT, "Builtin Feature", "Default", "Current");
1942  ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1943 
1944  ast_cli(a->fd, HFS_FORMAT, "Pickup", DEFAULT_PICKUPEXTEN, cfg->global->pickup->pickupexten);
1945  ast_cli(a->fd, HFS_FORMAT, "Blind Transfer", DEFAULT_FEATUREMAP_BLINDXFER, cfg->featuremap->blindxfer);
1946  ast_cli(a->fd, HFS_FORMAT, "Attended Transfer", DEFAULT_FEATUREMAP_ATXFER, cfg->featuremap->atxfer);
1947  ast_cli(a->fd, HFS_FORMAT, "One Touch Monitor", DEFAULT_FEATUREMAP_AUTOMON, cfg->featuremap->automon);
1948  ast_cli(a->fd, HFS_FORMAT, "Disconnect Call", DEFAULT_FEATUREMAP_DISCONNECT, cfg->featuremap->disconnect);
1949  ast_cli(a->fd, HFS_FORMAT, "Park Call", DEFAULT_FEATUREMAP_PARKCALL, cfg->featuremap->parkcall);
1950  ast_cli(a->fd, HFS_FORMAT, "One Touch MixMonitor", DEFAULT_FEATUREMAP_AUTOMIXMON, cfg->featuremap->automixmon);
1951 
1952  ast_cli(a->fd, "\n");
1953  ast_cli(a->fd, HFS_FORMAT, "Dynamic Feature", "Default", "Current");
1954  ast_cli(a->fd, HFS_FORMAT, "---------------", "-------", "-------");
1955  if (!cfg->applicationmap || ao2_container_count(cfg->applicationmap) == 0) {
1956  ast_cli(a->fd, "(none)\n");
1957  } else {
1958  ao2_callback(cfg->applicationmap, 0, print_applicationmap, a);
1959  }
1960 
1961  ast_cli(a->fd, "\nFeature Groups:\n");
1962  ast_cli(a->fd, "---------------\n");
1963  if (!cfg->featuregroups || ao2_container_count(cfg->featuregroups) == 0) {
1964  ast_cli(a->fd, "(none)\n");
1965  } else {
1966  ao2_callback(cfg->featuregroups, 0, print_featuregroups, a);
1967  }
1968 
1969  return CLI_SUCCESS;
1970 }
1971 
1973  AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1974 };
1975 
1977 {
1978  ast_custom_function_unregister(&featuremap_function);
1979  ast_custom_function_unregister(&feature_function);
1980  ast_cli_unregister_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
1981  aco_info_destroy(&cfg_info);
1983 }
1984 
1986 {
1987  /* Rearm the parking config options have moved warning. */
1988  parking_warning = 0;
1989 
1990  if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1991  return -1;
1992  }
1993  return 0;
1994 }
1995 
1997 {
1998  int res;
1999 
2000  res = load_config();
2001  res |= __ast_custom_function_register(&feature_function, NULL);
2002  res |= __ast_custom_function_register(&featuremap_function, NULL);
2003  res |= ast_cli_register_multiple(cli_features_config, ARRAY_LEN(cli_features_config));
2004 
2005  return res;
2006 }
struct features_global_config * global
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
static int internal_featuremap_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
static struct aco_type featuregroup_option
static struct features_config * __features_config_alloc(int allocate_applicationmap)
Main Channel structure associated with a channel.
#define DEFAULT_XFERFAILSOUND
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define DEFAULT_FEATUREMAP_BLINDXFER
static void general_copy(struct ast_features_general_config *dest, const struct ast_features_general_config *src)
#define DEFAULT_ATXFER_CALLBACK_RETRIES
static void * feature_ds_duplicate(void *data)
static struct aco_type global_option
Feature configuration relating to transfers.
static int pickup_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static const char name[]
Definition: format_mp3.c:68
Asterisk main include file. File version handling, generic pbx functions.
static struct aco_type * applicationmap_options[]
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int load_features_config(void)
static struct aco_type featuremap_option
static struct aco_type global
Definition: test_config.c:1445
#define DEFAULT_ATXFER_LOOP_DELAY
static void features_config_destructor(void *obj)
Featuregroup representation.
static struct aco_type applicationmap_option
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
struct ao2_container * featuregroups
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static int internal_feature_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int print_applicationmap(void *obj, void *arg, int flags)
#define OBJ_KEY
Definition: astobj2.h:1155
static const struct ast_datastore_info feature_ds_info
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int featuregroup_hash(const void *obj, int flags)
#define DEFAULT_FEATUREMAP_ATXFER
#define OBJ_POINTER
Definition: astobj2.h:1154
Configuration for the builtin features.
static struct ast_datastore * get_feature_chan_ds(struct ast_channel *chan)
An applicationmap configuration item.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
#define LOG_WARNING
Definition: logger.h:274
static void featuregroup_item_destructor(void *obj)
static struct aco_file features_conf
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
Allow objects with duplicate keys in container.
Definition: astobj2.h:1185
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Definition: astobj2.h:1335
struct ao2_container * ast_get_chan_applicationmap(struct ast_channel *chan)
Get the applicationmap for a given channel.
Structure for variables, used for configurations and for channel variables.
static struct ao2_container * applicationmap_alloc(int replace_duplicates)
#define var
Definition: ast_expr2f.c:614
#define DEFAULT_ATXFER_COMPLETE
struct ast_features_pickup_config * ast_get_chan_features_pickup_config(struct ast_channel *chan)
Get the pickup configuration options for a channel.
static int featuregroup_cmp(void *obj, void *arg, int flags)
const ast_string_field appmap_item_name
Definition: cli.h:152
Structure for a data store type.
Definition: datastore.h:31
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
static struct aco_type item
Definition: test_config.c:1463
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
General features configuration items.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
static void global_config_destructor(void *obj)
static int group_item_sort(const void *obj, const void *arg, int flags)
#define ast_assert(a)
Definition: utils.h:710
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2400
static int feature_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
const char * args
#define NULL
Definition: resample.c:96
static void ast_applicationmap_item_destructor(void *obj)
The representation of a single configuration file to be processed.
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define DEFAULT_FEATUREMAP_DISCONNECT
enum aco_type_t type
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
struct ast_applicationmap_item * appmap_item
struct ao2_container * items
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
static void * featuregroup_find(struct ao2_container *group_container, const char *category)
struct ast_featuremap_config * featuremap
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1156
static int check_featuregroup_item(void *obj, void *arg, void *data, int flags)
#define DEFAULT_RECORDING_FAIL_SOUND
#define DEFAULT_PICKUPEXTEN
static struct ao2_container * group_container
struct ast_features_xfer_config * xfer
static int general_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
struct ast_features_general_config * general
Asterisk datastore objects.
struct ast_features_general_config * ast_get_chan_features_general_config(struct ast_channel *chan)
Get the general configuration options for a channel.
static int unsupported_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
Configuration from the "general" section of features.conf.
int reload_features_config(void)
static void * featuregroup_alloc(const char *cat)
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition: main/utils.c:1639
struct ast_features_pickup_config * pickup
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
static int general_get(struct ast_features_general_config *general, const char *field, char *buf, size_t len)
#define __stringify(x)
Definition: asterisk.h:214
#define DEFAULT_ATXFER_DROP_CALL
const char * name
static int xfer_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static struct features_global_config * global_config_alloc(void)
#define DEFAULT_TRANSFER_INVALID_SOUND
static const char app[]
Definition: app_adsiprog.c:56
AO2_GLOBAL_OBJ_STATIC(globals)
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
static struct aco_type parkinglot_option
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
General Asterisk PBX channel definitions.
static struct aco_type * featuremap_options[]
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static struct ast_applicationmap_item * applicationmap_item_alloc(const char *name, const char *app, const char *app_data, const char *moh_class, const char *dtmf, unsigned int activate_on_self)
const int fd
Definition: cli.h:159
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static int applicationmap_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
ast_mutex_t lock
Definition: app_meetme.c:1093
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define DEFAULT_FEATUREMAP_AUTOMIXMON
static int add_item(void *obj, void *arg, int flags)
static struct console_pvt globals
static void feature_ds_destroy(void *data)
static int featuremap_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
struct ast_featuremap_config * ast_get_chan_featuremap_config(struct ast_channel *chan)
Get the featuremap configuration options for a channel.
int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
Get the DTMF code for a call feature.
static void general_destructor(void *obj)
static struct ast_cli_entry cli_features_config[]
struct dummy_config * parkinglots
#define ARRAY_LEN(a)
Definition: utils.h:639
Core PBX routines and definitions.
static void xfer_copy(struct ast_features_xfer_config *dest, const struct ast_features_xfer_config *src)
Their was an error and no changes were applied.
struct ao2_container * applicationmap
static int pickup_set(struct ast_features_pickup_config *pickup, const char *name, const char *value)
static int load_config(void)
static void xfer_destructor(void *obj)
static struct features_config * features_config_dup(const struct features_config *orig)
static int xfer_get(struct ast_features_xfer_config *xfer, const char *field, char *buf, size_t len)
#define DEFAULT_FEATUREMAP_PARKCALL
Configuration option-handling.
static int feature_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#define ast_string_fields_copy(copy, orig)
Copy all string fields from one instance to another of the same structure.
Definition: stringfields.h:627
#define LOG_ERROR
Definition: logger.h:285
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
static struct ast_custom_function feature_function
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1743
static void pickup_copy(struct ast_features_pickup_config *dest, const struct ast_features_pickup_config *src)
static int pickup_get(struct ast_features_pickup_config *pickup, const char *field, char *buf, size_t len)
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
static struct aco_type * featuregroup_options[]
#define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER
static int featuremap_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int applicationmap_sort(const void *obj, const void *arg, int flags)
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1844
#define HFS_FORMAT
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static struct ast_custom_function featuremap_function
static int featuregroup_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
static int parking_warning
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *result,...)
The argument parsing routine.
Definition: main/config.c:3657
static int print_featuregroup(void *obj, void *arg, int flags)
const ast_string_field dtmf_override
#define DEFAULT_TRANSFER_DIAL_ATTEMPTS
static struct aco_type * parkinglot_options[]
static int general_set(struct ast_features_general_config *general, const char *name, const char *value)
void unload_features_config(void)
#define DATASTORE_INHERIT_FOREVER
Definition: channel.h:193
#define DEFAULT_ATXFER_ABORT
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
unsigned int inheritance
Definition: datastore.h:73
static int check_featuregroup(void *obj, void *arg, void *data, int flags)
static char * handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to list configured features.
#define DEFAULT_XFERSOUND
char * ast_get_chan_features_xferfailsound(struct ast_channel *chan)
Get the transfer configuration option xferfailsound.
Entry in the container of featuregroups.
const char * usage
Definition: cli.h:177
CONFIG_INFO_CORE("features", cfg_info, globals, features_config_alloc,.files=ACO_FILES(&features_conf),.pre_apply_config=features_pre_apply_config,)
static int print_featuregroups(void *obj, void *arg, int flags)
static int featuremap_get(struct ast_featuremap_config *featuremap, const char *field, char *buf, size_t len)
#define CLI_SUCCESS
Definition: cli.h:44
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
#define ACO_FILES(...)
void * data
Definition: datastore.h:70
#define DEFAULT_FEATURE_DIGIT_TIMEOUT
Replace objects with duplicate keys in container.
Definition: astobj2.h:1215
char * strsep(char **str, const char *delims)
static int internal_featuremap_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#define DEFAULT_COURTESY_TONE
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
Type information about a category-level configurable object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define DEFAULT_TRANSFER_DIGIT_TIMEOUT
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
#define DEFAULT_PICKUPFAILSOUND
const char * ast_channel_name(const struct ast_channel *chan)
const char * filename
static void featuregroup_destructor(void *obj)
struct ast_features_xfer_config * ast_get_chan_features_xfer_config(struct ast_channel *chan)
Get the transfer configuration options for a channel.
static void pickup_destructor(void *obj)
int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
Register a custom function.
static int internal_feature_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static void featuremap_config_destructor(void *obj)
static struct aco_type * global_options[]
char * ast_get_chan_features_atxferabort(struct ast_channel *chan)
Get the transfer configuration option atxferabort.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
static void * features_config_alloc(void)
Generic container type.
int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
Get the DTMF code for a builtin feature.
static int features_pre_apply_config(void)
#define DEFAULT_FEATUREMAP_AUTOMON
#define paren
Definition: ael_lex.c:973
static struct features_config * get_feature_ds(struct ast_channel *chan)
static int xfer_set(struct ast_features_xfer_config *xfer, const char *name, const char *value)
static int featuremap_set(struct ast_featuremap_config *featuremap, const char *name, const char *value)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2386
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static void features_copy(struct features_config *dest, const struct features_config *src)
#define DEFAULT_PICKUPSOUND
#define DEFAULT_ATXFER_THREEWAY
static void global_copy(struct features_global_config *dest, const struct features_global_config *src)
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1250
#define DEFAULT_ATXFER_SWAP
Configuration relating to call pickup.
#define AST_APP_ARG(name)
Define an application argument.
#define DEFAULT_TRANSFER_RETRY_SOUND
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
#define aco_option_register_custom_nodoc(info, name, matchtype, types, default_val, handler, flags)
Register a config option with no expected documentation.
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549
static int featuremap_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)