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