Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
features_config.c
Go to the documentation of this file.
1/*
2* Asterisk -- An open source telephony toolkit.
3*
4* Copyright (C) 2013, Digium, Inc.
5*
6* Mark Michelson <mmichelson@digium.com>
7*
8* See http://www.asterisk.org for more information about
9* the Asterisk project. Please do not directly contact
10* any of the maintainers of this project for assistance;
11* the project provides a web site, mailing lists and IRC
12* channels for your use.
13*
14* This program is free software, distributed under the terms of
15* the GNU General Public License Version 2. See the LICENSE file
16* at the top of the source tree.
17*/
18
19#include "asterisk.h"
20
23#include "asterisk/datastore.h"
24#include "asterisk/channel.h"
25#include "asterisk/pbx.h"
26#include "asterisk/app.h"
27#include "asterisk/cli.h"
28
29#include "features_config.h"
30
31/*** DOCUMENTATION
32 <configInfo name="features" language="en_US">
33 <synopsis>Features Configuration</synopsis>
34 <configFile name="features.conf">
35 <configObject name="globals">
36 <synopsis>
37 </synopsis>
38 <configOption name="featuredigittimeout" default="1000">
39 <synopsis>Milliseconds allowed between digit presses when entering a feature code.</synopsis>
40 </configOption>
41 <configOption name="courtesytone">
42 <synopsis>Sound to play when 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 */
401};
402
404{
405 struct ast_applicationmap_item *item = obj;
406
408}
409
410static 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
444static void featuregroup_item_destructor(void *obj)
445{
446 struct featuregroup_item *item = obj;
447
449 ao2_cleanup(item->appmap_item);
450}
451
452static 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 */
477 /*! The name of the featuregroup */
478 const char *name;
479 /*! A container of featuregroup_items */
481};
482
483static 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;
499 }
500}
501
502static 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
523static void *featuregroup_find(struct ao2_container *group_container, const char *category)
524{
525 return ao2_find(group_container, category, OBJ_KEY);
526}
527
528static 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
536static void *featuregroup_alloc(const char *cat)
537{
538 struct featuregroup *group;
539
541 if (!group) {
542 return NULL;
543 }
544
545 group->name = ast_strdup(cat);
546 if (!group->name) {
548 return NULL;
549 }
550
553 if (!group->items) {
555 return NULL;
556 }
557
558 return group;
559}
560
561/* Used for deprecated parking configuration */
563 char dummy;
564};
565
572};
573
574static 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
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
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
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
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
622
623static struct aco_file features_conf = {
624 .filename = "features.conf",
626};
627
629
630static void features_config_destructor(void *obj)
631{
632 struct features_config *cfg = obj;
633
634 ao2_cleanup(cfg->global);
639}
640
641static void featuremap_config_destructor(void *obj)
642{
643 struct ast_featuremap_config *cfg = obj;
644
646}
647
648static 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
657static void general_destructor(void *obj)
658{
659 struct ast_features_general_config *cfg = obj;
660
662}
663
664static void xfer_destructor(void *obj)
665{
666 struct ast_features_xfer_config *cfg = obj;
667
669}
670
671static void pickup_destructor(void *obj)
672{
673 struct ast_features_pickup_config *cfg = obj;
674
676}
677
679{
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
706static struct ao2_container *applicationmap_alloc(int replace_duplicates)
707{
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 */
726static 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,
758 if (!cfg->featuregroups) {
759 return NULL;
760 }
761 }
762
763 ao2_ref(cfg, +1);
764 return cfg;
765
766}
767
768static void *features_config_alloc(void)
769{
770 return __features_config_alloc(1);
771}
772
773static 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
779static 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
790static 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
795static 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
802static void featuremap_copy(struct ast_featuremap_config *dest, const struct ast_featuremap_config *src)
803{
804 ast_string_fields_copy(dest, src);
805}
806
807static void features_copy(struct features_config *dest, const struct features_config *src)
808{
809 global_copy(dest->global, src->global);
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
818static struct features_config *features_config_dup(const struct features_config *orig)
819{
820 struct features_config *dup;
821
823 if (!dup) {
824 return NULL;
825 }
826
827 features_copy(dup, orig);
828
829 return dup;
830}
831
832static 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")) {
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
851static 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")) {
860 } else if (!strcasecmp(field, "recordingfailsound")) {
862 } else {
863 /* Unrecognized option */
864 res = -1;
865 }
866
867 return res;
868}
869
870static 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")) {
877 } else if (!strcasecmp(name, "atxfernoanswertimeout")) {
879 } else if (!strcasecmp(name, "atxferloopdelay")) {
881 } else if (!strcasecmp(name, "atxfercallbackretries")) {
883 } else if (!strcasecmp(name, "atxferdropcall")) {
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")) {
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
913static 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")) {
930 } else if (!strcasecmp(field, "xferfailsound")) {
932 } else if (!strcasecmp(field, "atxferabort")) {
934 } else if (!strcasecmp(field, "atxfercomplete")) {
936 } else if (!strcasecmp(field, "atxferthreeway")) {
938 } else if (!strcasecmp(field, "atxferswap")) {
940 } else if (!strcasecmp(field, "transferdialattempts")) {
941 snprintf(buf, len, "%u", xfer->transferdialattempts);
942 } else if (!strcasecmp(field, "transferretrysound")) {
944 } else if (!strcasecmp(field, "transferinvalidsound")) {
946 } else {
947 /* Unrecognized option */
948 res = -1;
949 }
950
951 return res;
952}
953
954static 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
973static 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")) {
980 } else if (!strcasecmp(field, "pickupfailsound")) {
982 } else if (!strcasecmp(field, "pickupexten")) {
984 } else {
985 /* Unrecognized option */
986 res = -1;
987 }
988
989 return res;
990}
991
992static 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")) {
999 } else if (!strcasecmp(name, "disconnect")) {
1001 } else if (!strcasecmp(name, "atxfer")) {
1003 } else if (!strcasecmp(name, "automixmon")) {
1005 } else if (!strcasecmp(name, "parkcall")) {
1007 } else {
1008 /* Unrecognized option */
1009 res = -1;
1010 }
1011
1012 return res;
1013}
1014
1015static 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")) {
1022 } else if (!strcasecmp(field, "disconnect")) {
1024 } else if (!strcasecmp(field, "atxfer")) {
1026 } else if (!strcasecmp(field, "automixmon")) {
1028 } else if (!strcasecmp(field, "parkcall")) {
1030 } else {
1031 /* Unrecognized option */
1032 res = -1;
1033 }
1034
1035 return res;
1036}
1037
1038static void feature_ds_destroy(void *data)
1039{
1040 struct features_config *cfg = data;
1041 ao2_cleanup(cfg);
1042}
1043
1044static void *feature_ds_duplicate(void *data)
1045{
1046 struct features_config *old_cfg = data;
1047
1048 return features_config_dup(old_cfg);
1049}
1050
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 */
1065static 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
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
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. */
1108
1110 }
1111
1112 return ds;
1113}
1114
1116{
1117 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1118
1119 if (chan) {
1120 cfg = get_feature_ds(chan);
1121 } else {
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
1136{
1137 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1138
1139 if (chan) {
1140 cfg = get_feature_ds(chan);
1141 } else {
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
1156{
1157 char *res;
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
1171{
1172 char *res;
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
1186{
1187 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1188
1189 if (chan) {
1190 cfg = get_feature_ds(chan);
1191 } else {
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
1206{
1207 RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup);
1208
1209 if (chan) {
1210 cfg = get_feature_ds(chan);
1211 } else {
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
1225int 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 {
1233 }
1234
1235 if (!cfg) {
1236 return -1;
1237 }
1238
1239 return featuremap_get(cfg->featuremap, feature, buf, len);
1240}
1241
1242int 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);
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
1267 const char *app, const char *app_data, const char *moh_class, const char *dtmf,
1268 unsigned int activate_on_self)
1269{
1271
1273
1274 if (!item || ast_string_field_init(item, 64)) {
1275 return NULL;
1276 }
1277
1282 ast_copy_string(item->dtmf, dtmf, sizeof(item->dtmf));
1283 item->activate_on_self = activate_on_self;
1284
1285 return item;
1286}
1287
1288static 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
1318{
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
1374static int applicationmap_handler(const struct aco_option *opt,
1375 struct ast_variable *var, void *obj)
1376{
1378 struct ao2_container *applicationmap = obj;
1380 AST_APP_ARG(dtmf);
1381 AST_APP_ARG(activate_on);
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
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
1470static int featuregroup_handler(const struct aco_option *opt,
1471 struct ast_variable *var, void *obj)
1472{
1474 struct featuregroup *group = obj;
1475
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
1493static 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
1502static 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
1511static 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
1520static int parking_warning = 0;
1521static 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
1533static 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
1541static 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
1559static 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
1574static int features_pre_apply_config(void);
1575
1577 .files = ACO_FILES(&features_conf),
1578 .pre_apply_config = features_pre_apply_config,
1579);
1580
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);
1594
1595 return err;
1596}
1597
1598static 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
1629static 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) {
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
1659static 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
1674static 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
1694static 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
1705static 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
1716static 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
1727static 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
1739 .name = "FEATURE",
1740 .read = feature_read,
1741 .write = feature_write
1742};
1743
1745 .name = "FEATUREMAP",
1746 .read = featuremap_read,
1747 .write = featuremap_write
1748};
1749
1750static 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,
1759 aco_option_register_custom(&cfg_info, "recordingfailsound", ACO_EXACT, global_options,
1761 aco_option_register_custom(&cfg_info, "courtesytone", ACO_EXACT, global_options,
1763
1764 aco_option_register_custom(&cfg_info, "transferdigittimeout", ACO_EXACT, global_options,
1766 aco_option_register_custom(&cfg_info, "atxfernoanswertimeout", ACO_EXACT, global_options,
1768 aco_option_register_custom(&cfg_info, "atxferdropcall", ACO_EXACT, global_options,
1770 aco_option_register_custom(&cfg_info, "atxferloopdelay", ACO_EXACT, global_options,
1772 aco_option_register_custom(&cfg_info, "atxfercallbackretries", ACO_EXACT, global_options,
1774 aco_option_register_custom(&cfg_info, "xfersound", ACO_EXACT, global_options,
1776 aco_option_register_custom(&cfg_info, "xferfailsound", ACO_EXACT, global_options,
1778 aco_option_register_custom(&cfg_info, "atxferabort", ACO_EXACT, global_options,
1780 aco_option_register_custom(&cfg_info, "atxfercomplete", ACO_EXACT, global_options,
1782 aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
1784 aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
1786 aco_option_register_custom(&cfg_info, "transferdialattempts", ACO_EXACT, global_options,
1788 aco_option_register_custom(&cfg_info, "transferretrysound", ACO_EXACT, global_options,
1790 aco_option_register_custom(&cfg_info, "transferinvalidsound", ACO_EXACT, global_options,
1792 aco_option_register_custom(&cfg_info, "transferannouncesound", ACO_EXACT, global_options,
1794
1795 aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
1797 aco_option_register_custom(&cfg_info, "pickupsound", ACO_EXACT, global_options,
1799 aco_option_register_custom(&cfg_info, "pickupfailsound", ACO_EXACT, global_options,
1801
1803 "", unsupported_handler, 0);
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);
1813 "", unsupported_handler, 0);
1815 "", unsupported_handler, 0);
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);
1835 "", unsupported_handler, 0);
1836
1847
1849 "", applicationmap_handler, 0);
1850
1852 "", featuregroup_handler, 0);
1853
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");
1868 }
1869
1870 return 0;
1871}
1872
1873static 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
1884static 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
1892 return 0;
1893}
1894
1895#define HFS_FORMAT "%-25s %-7s %-7s\n"
1896
1897static 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 */
1915static 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
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
1964 AST_CLI_DEFINE(handle_feature_show, "Lists configured features"),
1965};
1966
1968{
1972 aco_info_destroy(&cfg_info);
1974}
1975
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
1988{
1989 int res;
1990
1991 res = load_config();
1995
1996 return res;
1997}
#define paren
Definition: ael_lex.c:962
static const char app[]
Definition: app_adsiprog.c:56
ast_mutex_t lock
Definition: app_sla.c:331
#define var
Definition: ast_expr2f.c:605
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:2404
#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:2418
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.
char * strsep(char **str, const char *delims)
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *p_result,...)
The argument parsing routine.
Definition: main/config.c:3834
#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:619
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