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