summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--apps/app_skel.c48
-rw-r--r--apps/confbridge/conf_config_parser.c466
-rw-r--r--channels/chan_motif.c140
-rw-r--r--configs/motif.conf.sample27
-rw-r--r--configs/xmpp.conf.sample3
-rw-r--r--doc/appdocsxml.dtd34
-rw-r--r--include/asterisk/_private.h1
-rw-r--r--include/asterisk/config_options.h9
-rw-r--r--include/asterisk/xml.h39
-rw-r--r--include/asterisk/xmldoc.h28
-rw-r--r--main/asterisk.c2
-rw-r--r--main/config_options.c534
-rw-r--r--main/named_acl.c51
-rw-r--r--main/udptl.c37
-rw-r--r--main/xml.c39
-rw-r--r--main/xmldoc.c254
-rw-r--r--res/res_xmpp.c122
18 files changed, 1762 insertions, 74 deletions
diff --git a/Makefile b/Makefile
index 76e68c416..02d6fa55d 100644
--- a/Makefile
+++ b/Makefile
@@ -456,7 +456,7 @@ doc/core-en_US.xml: makeopts $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "la
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
@for x in $(MOD_SUBDIRS); do \
printf "$$x " ; \
- for i in $$x/*.c; do \
+ for i in `find $$x -name *.c`; do \
$(AWK) -f build_tools/get_documentation $$i >> $@ ; \
done ; \
done
diff --git a/apps/app_skel.c b/apps/app_skel.c
index 6fdbebbf2..8a5b5bc75 100644
--- a/apps/app_skel.c
+++ b/apps/app_skel.c
@@ -86,6 +86,51 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
from. It shows you the basic structure to create your own Asterisk applications.</para>
</description>
</application>
+
+ <configInfo name="app_skel" language="en_US">
+ <configFile name="app_skel.conf">
+ <configObject name="globals">
+ <synopsis>Options that apply globally to app_skel</synopsis>
+ <configOption name="games">
+ <synopsis>The number of games a single execution of SkelGuessNumber will play</synopsis>
+ </configOption>
+ <configOption name="cheat">
+ <synopsis>Should the computer cheat?</synopsis>
+ <description><para>If enabled, the computer will ignore winning guesses.</para></description>
+ </configOption>
+ </configObject>
+ <configObject name="sounds">
+ <synopsis>Prompts for SkelGuessNumber to play</synopsis>
+ <configOption name="prompt" default="please-enter-your&amp;number&amp;queue-less-than">
+ <synopsis>A prompt directing the user to enter a number less than the max number</synopsis>
+ </configOption>
+ <configOption name="wrong_guess" default="vm-pls-try-again">
+ <synopsis>The sound file to play when a wrong guess is made</synopsis>
+ </configOption>
+ <configOption name="right_guess" default="auth-thankyou">
+ <synopsis>The sound file to play when a correct guess is made</synopsis>
+ </configOption>
+ <configOption name="too_low">
+ <synopsis>The sound file to play when a guess is too low</synopsis>
+ </configOption>
+ <configOption name="too_high">
+ <synopsis>The sound file to play when a guess is too high</synopsis>
+ </configOption>
+ <configOption name="lose" default="vm-goodbye">
+ <synopsis>The sound file to play when a player loses</synopsis>
+ </configOption>
+ </configObject>
+ <configObject name="level">
+ <synopsis>Defined levels for the SkelGuessNumber game</synopsis>
+ <configOption name="max_number">
+ <synopsis>The maximum in the range of numbers to guess (1 is the implied minimum)</synopsis>
+ </configOption>
+ <configOption name="max_guesses">
+ <synopsis>The maximum number of guesses before a game is considered lost</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
***/
static char *app = "SkelGuessNumber";
@@ -197,6 +242,7 @@ static void *skel_level_find(struct ao2_container *tmp_container, const char *ca
/*! \brief An aco_type structure to link the "general" category to the skel_global_config type */
static struct aco_type global_option = {
.type = ACO_GLOBAL,
+ .name = "globals",
.item_offset = offsetof(struct skel_config, global),
.category_match = ACO_WHITELIST,
.category = "^general$",
@@ -207,6 +253,7 @@ struct aco_type *global_options[] = ACO_TYPES(&global_option);
/*! \brief An aco_type structure to link the "sounds" category to the skel_global_config type */
static struct aco_type sound_option = {
.type = ACO_GLOBAL,
+ .name = "sounds",
.item_offset = offsetof(struct skel_config, global),
.category_match = ACO_WHITELIST,
.category = "^sounds$",
@@ -217,6 +264,7 @@ struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
/*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
static struct aco_type level_option = {
.type = ACO_ITEM,
+ .name = "level",
.category_match = ACO_BLACKLIST,
.category = "^(general|sounds)$",
.item_alloc = skel_level_alloc,
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index c915b184c..6c5ea0bb8 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -40,6 +40,464 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stringfields.h"
#include "asterisk/pbx.h"
+
+/*** DOCUMENTATION
+ <configInfo name="app_confbridge" language="en_US">
+ <synopsis>Conference Bridge Application</synopsis>
+ <configFile name="confbridge.conf">
+ <configObject name="global">
+ <synopsis>Unused, but reserved.</synopsis>
+ </configObject>
+ <configObject name="user_profile">
+ <synopsis>A named profile to apply to specific callers.</synopsis>
+ <description><para>Callers in a ConfBridge have a profile associated with them
+ that determine their options. A configuration section is determined to be a
+ user_profile when the <literal>type</literal> parameter has a value
+ of <literal>user</literal>.
+ </para></description>
+ <configOption name="type">
+ <synopsis>Define this configuration category as a user profile.</synopsis>
+ <description><para>The type parameter determines how a context in the
+ configuration file is interpreted.</para>
+ <enumlist>
+ <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
+ <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
+ <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="admin">
+ <synopsis>Sets if the user is an admin or not</synopsis>
+ </configOption>
+ <configOption name="marked">
+ <synopsis>Sets if this is a marked user or not</synopsis>
+ </configOption>
+ <configOption name="startmuted">
+ <synopsis>Sets if all users should start out muted</synopsis>
+ </configOption>
+ <configOption name="music_on_hold_when_empty">
+ <synopsis>Play MOH when user is alone or waiting on a marked user</synopsis>
+ </configOption>
+ <configOption name="quiet">
+ <synopsis>Silence enter/leave prompts and user intros for this user</synopsis>
+ </configOption>
+ <configOption name="announce_user_count">
+ <synopsis>Sets if the number of users should be announced to the user</synopsis>
+ </configOption>
+ <configOption name="announce_user_count_all">
+ <synopsis>Announce user count to all the other users when this user joins</synopsis>
+ <description><para>Sets if the number of users should be announced to all the other users
+ in the conference when this user joins. This option can be either set to 'yes' or
+ a number. When set to a number, the announcement will only occur once the user
+ count is above the specified number.
+ </para></description>
+ </configOption>
+ <configOption name="announce_only_user">
+ <synopsis>Announce to a user when they join an empty conference</synopsis>
+ </configOption>
+ <configOption name="wait_marked">
+ <synopsis>Sets if the user must wait for a marked user to enter before joining a conference</synopsis>
+ </configOption>
+ <configOption name="end_marked">
+ <synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
+ </configOption>
+ <configOption name="talk_detection_events">
+ <synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
+ </configOption>
+ <configOption name="dtmf_passthrough">
+ <synopsis>Sets whether or not DTMF should pass through the conference</synopsis>
+ </configOption>
+ <configOption name="announce_join_leave">
+ <synopsis>Prompt user for their name when joining a conference and play it to the conference when they enter</synopsis>
+ </configOption>
+ <configOption name="pin">
+ <synopsis>Sets a PIN the user must enter before joining the conference</synopsis>
+ </configOption>
+ <configOption name="music_on_hold_class">
+ <synopsis>The MOH class to use for this user</synopsis>
+ </configOption>
+ <configOption name="announcement">
+ <synopsis>Sound file to play to the user when they join a conference</synopsis>
+ </configOption>
+ <configOption name="denoise">
+ <synopsis>Apply a denoise filter to the audio before mixing</synopsis>
+ <description><para>Sets whether or not a denoise filter should be applied
+ to the audio before mixing or not. Off by default. Requires
+ codec_speex to be built and installed. Do not confuse this option
+ with drop_silence. Denoise is useful if there is a lot of background
+ noise for a user as it attempts to remove the noise while preserving
+ the speech. This option does NOT remove silence from being mixed into
+ the conference and does come at the cost of a slight performance hit.
+ </para></description>
+ </configOption>
+ <configOption name="dsp_drop_silence">
+ <synopsis>Drop what Asterisk detects as silence from audio sent to the bridge</synopsis>
+ <description><para>
+ This option drops what Asterisk detects as silence from
+ entering into the bridge. Enabling this option will drastically
+ improve performance and help remove the buildup of background
+ noise from the conference. Highly recommended for large conferences
+ due to its performance enhancements.
+ </para></description>
+ </configOption>
+ <configOption name="dsp_silence_threshold">
+ <synopsis>The number of milliseconds of detected silence necessary to trigger silence detection</synopsis>
+ <description><para>
+ The time in milliseconds of sound falling within the what
+ the dsp has established as baseline silence before a user
+ is considered be silent. This value affects several
+ operations and should not be changed unless the impact
+ on call quality is fully understood.</para>
+ <para>What this value affects internally:</para>
+ <para>
+ 1. When talk detection AMI events are enabled, this value
+ determines when the user has stopped talking after a
+ period of talking. If this value is set too low
+ AMI events indicating the user has stopped talking
+ may get falsely sent out when the user briefly pauses
+ during mid sentence.
+ </para>
+ <para>
+ 2. The drop_silence option depends on this value to
+ determine when the user's audio should begin to be
+ dropped from the conference bridge after the user
+ stops talking. If this value is set too low the user's
+ audio stream may sound choppy to the other participants.
+ This is caused by the user transitioning constantly from
+ silence to talking during mid sentence.
+ </para>
+ <para>
+ The best way to approach this option is to set it slightly above
+ the maximum amount of ms of silence a user may generate during
+ natural speech.
+ </para>
+ <para>By default this value is 2500ms. Valid values are 1 through 2^31.</para>
+ </description>
+ </configOption>
+ <configOption name="dsp_talking_threshold">
+ <synopsis>The number of milliseconds of detected non-silence necessary to triger talk detection</synopsis>
+ <description><para>
+ The time in milliseconds of sound above what the dsp has
+ established as base line silence for a user before a user
+ is considered to be talking. This value affects several
+ operations and should not be changed unless the impact on
+ call quality is fully understood.</para>
+ <para>
+ What this value affects internally:
+ </para>
+ <para>
+ 1. Audio is only mixed out of a user's incoming audio stream
+ if talking is detected. If this value is set too
+ loose the user will hear themselves briefly each
+ time they begin talking until the dsp has time to
+ establish that they are in fact talking.
+ </para>
+ <para>
+ 2. When talk detection AMI events are enabled, this value
+ determines when talking has begun which results in
+ an AMI event to fire. If this value is set too tight
+ AMI events may be falsely triggered by variants in
+ room noise.
+ </para>
+ <para>
+ 3. The drop_silence option depends on this value to determine
+ when the user's audio should be mixed into the bridge
+ after periods of silence. If this value is too loose
+ the beginning of a user's speech will get cut off as they
+ transition from silence to talking.
+ </para>
+ <para>By default this value is 160 ms. Valid values are 1 through 2^31</para>
+ </description>
+ </configOption>
+ <configOption name="jitterbuffer">
+ <synopsis>Place a jitter buffer on the user's audio stream before audio mixing is performed</synopsis>
+ <description><para>
+ Enabling this option places a jitterbuffer on the user's audio stream
+ before audio mixing is performed. This is highly recommended but will
+ add a slight delay to the audio. This option is using the <literal>JITTERBUFFER</literal>
+ dialplan function's default adaptive jitterbuffer. For a more fine tuned
+ jitterbuffer, disable this option and use the <literal>JITTERBUFFER</literal> dialplan function
+ on the user before entering the ConfBridge application.
+ </para></description>
+ </configOption>
+ <configOption name="template">
+ <synopsis>When using the CONFBRIDGE dialplan function, use a user profile as a template for creating a new temporary profile</synopsis>
+ </configOption>
+ </configObject>
+ <configObject name="bridge_profile">
+ <synopsis>A named profile to apply to specific bridges.</synopsis>
+ <description><para>ConfBridge bridges have a profile associated with them
+ that determine their options. A configuration section is determined to be a
+ <literal>bridge_profile</literal> when the <literal>type</literal> parameter has a value
+ of <literal>bridge</literal>.
+ </para></description>
+ <configOption name="type">
+ <synopsis>Define this configuration category as a bridge profile</synopsis>
+ <description><para>The type parameter determines how a context in the
+ configuration file is interpreted.</para>
+ <enumlist>
+ <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
+ <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
+ <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="jitterbuffer">
+ <synopsis>Place a jitter buffer on the conference's audio stream</synopsis>
+ </configOption>
+ <configOption name="internal_sample_rate">
+ <synopsis>Set the internal native sample rate for mixing the conference</synopsis>
+ <description><para>
+ Sets the internal native sample rate the
+ conference is mixed at. This is set to automatically
+ adjust the sample rate to the best quality by default.
+ Other values can be anything from 8000-192000. If a
+ sample rate is set that Asterisk does not support, the
+ closest sample rate Asterisk does support to the one requested
+ will be used.
+ </para></description>
+ </configOption>
+ <configOption name="mixing_interval">
+ <synopsis>Sets the internal mixing interval in milliseconds for the bridge</synopsis>
+ <description><para>
+ Sets the internal mixing interval in milliseconds for the bridge. This
+ number reflects how tight or loose the mixing will be for the conference.
+ In order to improve performance a larger mixing interval such as 40ms may
+ be chosen. Using a larger mixing interval comes at the cost of introducing
+ larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
+ or 80.
+ </para></description>
+ </configOption>
+ <configOption name="record_conference">
+ <synopsis>Record the conference starting with the first active user's entrance and ending with the last active user's exit</synopsis>
+ <description><para>
+ Records the conference call starting when the first user
+ enters the room, and ending when the last user exits the room.
+ The default recorded filename is
+ <filename>'confbridge-${name of conference bridge}-${start time}.wav</filename>
+ and the default format is 8khz slinear. This file will be
+ located in the configured monitoring directory in asterisk.conf.
+ </para></description>
+ </configOption>
+ <configOption name="record_file" default="confbridge-${name of conference bridge}-${start time}.wav">
+ <synopsis>The filename of the conference recording</synopsis>
+ <description><para>
+ When record_conference is set to yes, the specific name of the
+ record file can be set using this option. Note that since multiple
+ conferences may use the same bridge profile, this may cause issues
+ depending on the configuration. It is recommended to only use this
+ option dynamically with the <literal>CONFBRIDGE()</literal> dialplan function. This
+ allows the record name to be specified and a unique name to be chosen.
+ By default, the record_file is stored in Asterisk's spool/monitor directory
+ with a unique filename starting with the 'confbridge' prefix.
+ </para></description>
+ </configOption>
+ <configOption name="video_mode">
+ <synopsis>Sets how confbridge handles video distribution to the conference participants</synopsis>
+ <description><para>
+ Sets how confbridge handles video distribution to the conference participants.
+ Note that participants wanting to view and be the source of a video feed
+ _MUST_ be sharing the same video codec. Also, using video in conjunction with
+ with the jitterbuffer currently results in the audio being slightly out of sync
+ with the video. This is a result of the jitterbuffer only working on the audio
+ stream. It is recommended to disable the jitterbuffer when video is used.</para>
+ <enumlist>
+ <enum name="none">
+ <para>No video sources are set by default in the conference. It is still
+ possible for a user to be set as a video source via AMI or DTMF action
+ at any time.</para>
+ </enum>
+ <enum name="follow_talker">
+ <para>The video feed will follow whoever is talking and providing video.</para>
+ </enum>
+ <enum name="last_marked">
+ <para>The last marked user to join the conference with video capabilities
+ will be the single source of video distributed to all participants.
+ If multiple marked users are capable of video, the last one to join
+ is always the source, when that user leaves it goes to the one who
+ joined before them.</para>
+ </enum>
+ <enum name="first_marked">
+ <para>The first marked user to join the conference with video capabilities
+ is the single source of video distribution among all participants. If
+ that user leaves, the marked user to join after them becomes the source.</para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="max_members">
+ <synopsis>Limit the maximum number of participants for a single conference</synopsis>
+ <description><para>
+ This option limits the number of participants for a single
+ conference to a specific number. By default conferences
+ have no participant limit. After the limit is reached, the
+ conference will be locked until someone leaves. Note however
+ that an Admin user will always be alowed to join the conference
+ regardless if this limit is reached or not.
+ </para></description>
+ </configOption>
+ <configOption name="^sound_">
+ <synopsis>Override the various conference bridge sound files</synopsis>
+ <description><para>
+ All sounds in the conference are customizable using the bridge profile options below.
+ Simply state the option followed by the filename or full path of the filename after
+ the option. Example: <literal>sound_had_joined=conf-hasjoin</literal> This will play the <literal>conf-hasjoin</literal>
+ sound file found in the sounds directory when announcing someone's name is joining the
+ conference.</para>
+ <enumlist>
+ <enum name="sound_join"><para>The sound played to everyone when someone enters the conference.</para></enum>
+ <enum name="sound_leave"><para>The sound played to everyone when someone leaves the conference.</para></enum>
+ <enum name="sound_has_joined"><para>The sound played before announcing someone's name has
+ joined the conference. This is used for user intros.
+ Example <literal>"_____ has joined the conference"</literal></para></enum>
+ <enum name="sound_has_left"><para>The sound played when announcing someone's name has
+ left the conference. This is used for user intros.
+ Example <literal>"_____ has left the conference"</literal></para></enum>
+ <enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
+ <enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
+ <enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
+ <enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
+ <enum name="sound_only_one"><para>The sound played to a user when there is only one other
+ person is in the conference.</para></enum>
+ <enum name="sound_there_are"><para>The sound played when announcing how many users there
+ are in a conference.</para></enum>
+ <enum name="sound_other_in_party"><para>This file is used in conjunction with <literal>sound_there_are</literal>
+ when announcing how many users there are in the conference.
+ The sounds are stringed together like this.
+ <literal>"sound_there_are" ${number of participants} "sound_other_in_party"</literal></para></enum>
+ <enum name="sound_place_into_conference"><para>The sound played when someone is placed into the conference
+ after waiting for a marked user.</para></enum>
+ <enum name="sound_wait_for_leader"><para>The sound played when a user is placed into a conference that
+ can not start until a marked user enters.</para></enum>
+ <enum name="sound_leader_has_left"><para>The sound played when the last marked user leaves the conference.</para></enum>
+ <enum name="sound_get_pin"><para>The sound played when prompting for a conference pin number.</para></enum>
+ <enum name="sound_invalid_pin"><para>The sound played when an invalid pin is entered too many times.</para></enum>
+ <enum name="sound_locked"><para>The sound played to a user trying to join a locked conference.</para></enum>
+ <enum name="sound_locked_now"><para>The sound played to an admin after toggling the conference to locked mode.</para></enum>
+ <enum name="sound_unlocked_now"><para>The sound played to an admin after toggling the conference to unlocked mode.</para></enum>
+ <enum name="sound_error_menu"><para>The sound played when an invalid menu option is entered.</para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="template">
+ <synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
+ </configOption>
+ </configObject>
+ <configObject name="menu">
+ <synopsis>A conference user menu</synopsis>
+ <description>
+ <para>Conference users, as defined by a <literal>conf_user</literal>,
+ can have a DTMF menu assigned to their profile when they enter the
+ <literal>ConfBridge</literal> application.</para>
+ </description>
+ <configOption name="type">
+ <synopsis>Define this configuration category as a menu</synopsis>
+ <description><para>The type parameter determines how a context in the
+ configuration file is interpreted.</para>
+ <enumlist>
+ <enum name="user"><para>Configure the context as a <replaceable>user_profile</replaceable></para></enum>
+ <enum name="bridge"><para>Configure the context as a <replaceable>bridge_profile</replaceable></para></enum>
+ <enum name="menu"><para>Configure the context as a <replaceable>menu</replaceable></para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="^[0-9A-D*#]+$">
+ <synopsis>DTMF sequences to assign various confbridge actions to</synopsis>
+ <description><para>--- ConfBridge Menu Options ---</para>
+ <para>The ConfBridge application also has the ability to apply custom DTMF menus to
+ each channel using the application. Like the User and Bridge profiles a menu
+ is passed in to ConfBridge as an argument in the dialplan.</para>
+ <para>Below is a list of menu actions that can be assigned to a DTMF sequence.</para>
+ <note><para>
+ A single DTMF sequence can have multiple actions associated with it. This is
+ accomplished by stringing the actions together and using a <literal>,</literal> as the
+ delimiter. Example: Both listening and talking volume is reset when <literal>5</literal> is
+ pressed. <literal>5=reset_talking_volume, reset_listening_volume</literal></para></note>
+ <enumlist>
+ <enum name="playback(filename&amp;filename2&amp;...)"><para>
+ <literal>playback</literal> will play back an audio file to a channel
+ and then immediately return to the conference.
+ This file can not be interupted by DTMF.
+ Multiple files can be chained together using the
+ <literal>&amp;</literal> character.</para></enum>
+ <enum name="playback_and_continue(filename&amp;filename2&amp;...)"><para>
+ <literal>playback_and_continue</literal> will
+ play back a prompt while continuing to
+ collect the dtmf sequence. This is useful
+ when using a menu prompt that describes all
+ the menu options. Note however that any DTMF
+ during this action will terminate the prompts
+ playback. Prompt files can be chained together
+ using the <literal>&amp;</literal> character as a delimiter.</para></enum>
+ <enum name="toggle_mute"><para>
+ Toggle turning on and off mute. Mute will make the user silent
+ to everyone else, but the user will still be able to listen in.
+ continue to collect the dtmf sequence.</para></enum>
+ <enum name="no_op"><para>
+ This action does nothing (No Operation). Its only real purpose exists for
+ being able to reserve a sequence in the config as a menu exit sequence.</para></enum>
+ <enum name="decrease_listening_volume"><para>
+ Decreases the channel's listening volume.</para></enum>
+ <enum name="increase_listening_volume"><para>
+ Increases the channel's listening volume.</para></enum>
+ <enum name="reset_listening_volume"><para>
+ Reset channel's listening volume to default level.</para></enum>
+ <enum name="decrease_talking_volume"><para>
+ Decreases the channel's talking volume.</para></enum>
+ <enum name="increase_talking_volume"><para>
+ Increases the channel's talking volume.</para></enum>
+ <enum name="reset_talking_volume"><para>
+ Reset channel's talking volume to default level.</para></enum>
+ <enum name="dialplan_exec(context,exten,priority)"><para>
+ The <literal>dialplan_exec</literal> action allows a user
+ to escape from the conference and execute
+ commands in the dialplan. Once the dialplan
+ exits the user will be put back into the
+ conference. The possibilities are endless!</para></enum>
+ <enum name="leave_conference"><para>
+ This action allows a user to exit the conference and continue
+ execution in the dialplan.</para></enum>
+ <enum name="admin_kick_last"><para>
+ This action allows an Admin to kick the last participant from the
+ conference. This action will only work for admins which allows
+ a single menu to be used for both users and admins.</para></enum>
+ <enum name="admin_toggle_conference_lock"><para>
+ This action allows an Admin to toggle locking and
+ unlocking the conference. Non admins can not use
+ this action even if it is in their menu.</para></enum>
+ <enum name="set_as_single_video_src"><para>
+ This action allows any user to set themselves as the
+ single video source distributed to all participants.
+ This will make the video feed stick to them regardless
+ of what the <literal>video_mode</literal> is set to.</para></enum>
+ <enum name="release_as_single_video_src"><para>
+ This action allows a user to release themselves as
+ the video source. If <literal>video_mode</literal> is not set to <literal>none</literal>
+ this action will result in the conference returning to
+ whatever video mode the bridge profile is using.</para>
+ <para>Note that this action will have no effect if the user
+ is not currently the video source. Also, the user is
+ not guaranteed by using this action that they will not
+ become the video source again. The bridge will return
+ to whatever operation the <literal>video_mode</literal> option is set to
+ upon release of the video src.</para></enum>
+ <enum name="admin_toggle_mute_participants"><para>
+ This action allows an administrator to toggle the mute
+ state for all non-admins within a conference. All
+ admin users are unaffected by this option. Note that all
+ users, regardless of their admin status, are notified
+ that the conference is muted.</para></enum>
+ <enum name="participant_count"><para>
+ This action plays back the number of participants currently
+ in a conference</para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+***/
+
struct confbridge_cfg {
struct ao2_container *bridge_profiles;
struct ao2_container *user_profiles;
@@ -81,6 +539,7 @@ static void *bridge_profile_find(struct ao2_container *container, const char *ca
static struct aco_type bridge_type = {
.type = ACO_ITEM,
+ .name = "bridge_profile",
.category_match = ACO_BLACKLIST,
.category = "^general$",
.matchfield = "type",
@@ -117,6 +576,7 @@ static void *user_profile_find(struct ao2_container *container, const char *cate
static struct aco_type user_type = {
.type = ACO_ITEM,
+ .name = "user_profile",
.category_match = ACO_BLACKLIST,
.category = "^general$",
.matchfield = "type",
@@ -147,6 +607,7 @@ static void *menu_find(struct ao2_container *container, const char *category)
static struct aco_type menu_type = {
.type = ACO_ITEM,
+ .name = "menu",
.category_match = ACO_BLACKLIST,
.category = "^general$",
.matchfield = "type",
@@ -164,6 +625,7 @@ static struct aco_type *user_types[] = ACO_TYPES(&user_type);
/* The general category is reserved, but unused */
static struct aco_type general_type = {
.type = ACO_GLOBAL,
+ .name = "global",
.category_match = ACO_WHITELIST,
.category = "^general$",
};
@@ -1293,8 +1755,6 @@ int conf_load_config(int reload)
/* User options */
aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
- aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
- aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
@@ -1321,6 +1781,7 @@ int conf_load_config(int reload)
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
/* Bridge options */
+ aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
/* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
@@ -1334,6 +1795,7 @@ int conf_load_config(int reload)
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
/* Menu options */
+ aco_option_register(&cfg_info, "type", ACO_EXACT, menu_types, NULL, OPT_NOOP_T, 0, 0);
aco_option_register_custom(&cfg_info, "^[0-9A-D*#]+$", ACO_REGEX, menu_types, NULL, menu_option_handler, 0);
if (aco_process_config(&cfg_info, reload) == ACO_PROCESS_ERROR) {
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index cc1e370fd..4dbfb8e3a 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -77,6 +77,145 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/abstract_jb.h"
#include "asterisk/xmpp.h"
+/*** DOCUMENTATION
+ <configInfo name="chan_motif" language="en_US">
+ <synopsis>Jingle Channel Driver</synopsis>
+ <description>
+ <para><emphasis>Transports</emphasis></para>
+ <para>There are three different transports and protocol derivatives
+ supported by <literal>chan_motif</literal>. They are in order of
+ preference: Jingle using ICE-UDP, Google Jingle, and Google-V1.</para>
+ <para>Jingle as defined in XEP-0166 supports the widest range of
+ features. It is referred to as <literal>ice-udp</literal>. This is
+ the specification that Jingle clients implement.</para>
+ <para>Google Jingle follows the Jingle specification for signaling
+ but uses a custom transport for media. It is supported by the
+ Google Talk Plug-in in Gmail and by some other Jingle clients. It
+ is referred to as <literal>google</literal> in this file.</para>
+ <para>Google-V1 is the original Google Talk signaling protocol
+ which uses an initial preliminary version of Jingle. It also uses
+ the same custom transport as Google Jingle for media. It is
+ supported by Google Voice, some other Jingle clients, and the
+ Windows Google Talk client. It is referred to as <literal>google-v1</literal>
+ in this file.</para>
+ <para>Incoming sessions will automatically switch to the correct
+ transport once it has been determined.</para>
+ <para>Outgoing sessions are capable of determining if the target
+ is capable of Jingle or a Google transport if the target is in the
+ roster. Unfortunately it is not possible to differentiate between
+ a Google Jingle or Google-V1 capable resource until a session
+ initiate attempt occurs. If a resource is determined to use a
+ Google transport it will initially use Google Jingle but will fall
+ back to Google-V1 if required.</para>
+ <para>If an outgoing session attempt fails due to failure to
+ support the given transport <literal>chan_motif</literal> will
+ fall back in preference order listed previously until all
+ transports have been exhausted.</para>
+ <para><emphasis>Dialing and Resource Selection Strategy</emphasis></para>
+ <para>Placing a call through an endpoint can be accomplished using the
+ following dial string:</para>
+ <para><literal>Motif/[endpoint name]/[target]</literal></para>
+ <para>When placing an outgoing call through an endpoint the requested
+ target is searched for in the roster list. If present the first Jingle
+ or Google Jingle capable resource is specifically targeted. Since the
+ capabilities of the resource are known the outgoing session initiation
+ will disregard the configured transport and use the determined one.</para>
+ <para>If the target is not found in the roster the target will be used
+ as-is and a session will be initiated using the transport specified
+ in this configuration file. If no transport has been specified the
+ endpoint defaults to <literal>ice-udp</literal>.</para>
+ <para><emphasis>Video Support</emphasis></para>
+ <para>Support for video does not need to be explicitly enabled.
+ Configuring any video codec on your endpoint will automatically enable
+ it.</para>
+ <para><emphasis>DTMF</emphasis></para>
+ <para>The only supported method for DTMF is RFC2833. This is always
+ enabled on audio streams and negotiated if possible.</para>
+ <para><emphasis>Incoming Calls</emphasis></para>
+ <para>Incoming calls will first look for the extension matching the
+ name of the endpoint in the configured context. If no such extension
+ exists the call will automatically fall back to the <literal>s</literal> extension.</para>
+ <para><emphasis>CallerID</emphasis></para>
+ <para>The incoming caller id number is populated with the username of
+ the caller and the name is populated with the full identity of the
+ caller. If you would like to perform authentication or filtering
+ of incoming calls it is recommended that you use these fields to do so.</para>
+ <para>Outgoing caller id can <emphasis>not</emphasis> be set.</para>
+ <warning>
+ <para>Multiple endpoints using the
+ same connection is <emphasis>NOT</emphasis> supported. Doing so
+ may result in broken calls.</para>
+ </warning>
+ </description>
+ <configFile name="motif.conf">
+ <configObject name="endpoint">
+ <synopsis>The configuration for an endpoint.</synopsis>
+ <configOption name="context">
+ <synopsis>Default dialplan context that incoming sessions will be routed to</synopsis>
+ </configOption>
+ <configOption name="callgroup">
+ <synopsis>A callgroup to assign to this endpoint.</synopsis>
+ </configOption>
+ <configOption name="pickupgroup">
+ <synopsis>A pickup group to assign to this endpoint.</synopsis>
+ </configOption>
+ <configOption name="language">
+ <synopsis>The default language for this endpoint.</synopsis>
+ </configOption>
+ <configOption name="musicclass">
+ <synopsis>Default music on hold class for this endpoint.</synopsis>
+ </configOption>
+ <configOption name="parkinglot">
+ <synopsis>Default parking lot for this endpoint.</synopsis>
+ </configOption>
+ <configOption name="accountcode">
+ <synopsis>Accout code for CDR purposes</synopsis>
+ </configOption>
+ <configOption name="allow">
+ <synopsis>Codecs to allow</synopsis>
+ </configOption>
+ <configOption name="disallow">
+ <synopsis>Codecs to disallow</synopsis>
+ </configOption>
+ <configOption name="connection">
+ <synopsis>Connection to accept traffic on and on which to send traffic out</synopsis>
+ </configOption>
+ <configOption name="transport">
+ <synopsis>The transport to use for the endpoint.</synopsis>
+ <description>
+ <para>The default outbound transport for this endpoint. Inbound
+ messages are inferred. Allowed transports are <literal>ice-udp</literal>,
+ <literal>google</literal>, or <literal>google-v1</literal>. Note
+ that <literal>chan_motif</literal> will fall back to transport
+ preference order if the transport value chosen here fails.</para>
+ <enumlist>
+ <enum name="ice-udp">
+ <para>The Jingle protocol, as defined in XEP 0166.</para>
+ </enum>
+ <enum name="google">
+ <para>The Google Jingle protocol, which follows the Jingle
+ specification for signaling but uses a custom transport for
+ media.</para>
+ </enum>
+ <enum name="google-v1">
+ <para>Google-V1 is the original Google Talk signaling
+ protocol which uses an initial preliminary version of Jingle.
+ It also uses the same custom transport as <literal>google</literal> for media.</para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="maxicecandidates">
+ <synopsis>Maximum number of ICE candidates to offer</synopsis>
+ </configOption>
+ <configOption name="maxpayloads">
+ <synopsis>Maximum number of pyaloads to offer</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+***/
+
/*! \brief Default maximum number of ICE candidates we will offer */
#define DEFAULT_MAX_ICE_CANDIDATES "10"
@@ -406,6 +545,7 @@ static int jingle_endpoint_cmp(void *obj, void *arg, int flags)
static struct aco_type endpoint_option = {
.type = ACO_ITEM,
+ .name = "endpoint",
.category_match = ACO_BLACKLIST,
.category = "^general$",
.item_alloc = jingle_endpoint_alloc,
diff --git a/configs/motif.conf.sample b/configs/motif.conf.sample
index ae3ab30d7..9d5bc808c 100644
--- a/configs/motif.conf.sample
+++ b/configs/motif.conf.sample
@@ -75,18 +75,25 @@ context=incoming-motif ; Default context that incoming sessions will land in
;maxpayloads = 30 ; Maximum number of payloads we will offer
; Sample configuration entry for Jingle
-[jingle-endpoint](default)
-transport=ice-udp ; Change the default protocol of outgoing sessions to Jingle ICE-UDP
-allow=g722 ; Add G.722 as an allowed format since the other side may support it
-connection=local-jabber-account ; Connection to accept traffic on and send traffic out
-accountcode=jingle ; Account code for CDR purposes
+;[jingle-endpoint](default)
+;transport=ice-udp ; Change the default protocol of outgoing sessions to Jingle ICE-UDP
+;allow=g722 ; Add G.722 as an allowed format since the other side may support it
+;connection=local-jabber-account ; Connection to accept traffic on and send traffic out
+;accountcode=jingle ; Account code for CDR purposes
; Sample configuration entry for Google Talk
[gtalk-endpoint](default)
-transport=google ; Since this is a Google Talk endpoint we want to offer Google Jingle for outgoing sessions
-connection=gtalk-account
+;transport=google ; Since this is a Google Talk endpoint we want to offer Google Jingle for outgoing sessions
+;connection=gtalk-account
; Sample configuration entry for Google Voice
-[gvoice](default)
-transport=google-v1 ; Google Voice uses the original Google Talk protocol
-connection=gvoice-account
+;[gvoice](default)
+;transport=google-v1 ; Google Voice uses the original Google Talk protocol
+;connection=gvoice-account
+
+; Additional options
+; callgroup
+; pickupgroup
+; language
+; musicclass
+; parkinglot
diff --git a/configs/xmpp.conf.sample b/configs/xmpp.conf.sample
index a83856867..dad0f79ef 100644
--- a/configs/xmpp.conf.sample
+++ b/configs/xmpp.conf.sample
@@ -37,3 +37,6 @@
;sendtodialplan=yes ; Send incoming messages into the dialplan. Off by default.
;context=messages ; Dialplan context to send incoming messages to. If not set,
; "default" will be used.
+;forceoldssl=no ; Force the use of old-style SSL.
+;keepalive=
+
diff --git a/doc/appdocsxml.dtd b/doc/appdocsxml.dtd
index 561e3d38c..a475cd32f 100644
--- a/doc/appdocsxml.dtd
+++ b/doc/appdocsxml.dtd
@@ -1,13 +1,13 @@
- <!ELEMENT docs (application|function|agi|manager|managerEvent|info)*>
+ <!ELEMENT docs (application|function|agi|manager|managerEvent|info|configInfo)*>
<!ATTLIST docs xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">
<!ELEMENT xi:include (xi:fallback?) >
- <!ATTLIST xi:include
+ <!ATTLIST xi:include
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude"
href CDATA #IMPLIED
parse (xml|text) "xml"
xpointer CDATA #IMPLIED
- encoding CDATA #IMPLIED
+ encoding CDATA #IMPLIED
accept CDATA #IMPLIED
accept-language CDATA #IMPLIED >
@@ -39,6 +39,30 @@
<!ELEMENT managerEventInstance (synopsis?,syntax?,description?,see-also?)*>
<!ATTLIST managerEventInstance class CDATA #REQUIRED>
+ <!ELEMENT configInfo (synopsis?,description?,configFile+)>
+ <!ATTLIST configInfo name CDATA #REQUIRED>
+ <!ATTLIST configInfo language CDATA #REQUIRED>
+
+ <!ELEMENT configFile (configObject+)>
+ <!ATTLIST configFile name CDATA #REQUIRED>
+
+ <!ELEMENT configObject (synopsis?|description?|syntax?|see-also?|configOption)*>
+ <!ATTLIST configObject name CDATA #REQUIRED>
+
+ <!ELEMENT configOption (synopsis,description?,syntax?,see-also?)*>
+ <!ATTLIST configOption name CDATA #REQUIRED>
+ <!ATTLIST configOption regex (yes|no|true|false) "false">
+ <!ATTLIST configOption default CDATA #IMPLIED>
+ <!ATTLIST configOption type CDATA #IMPLIED>
+
+ <!ELEMENT matchInfo (category|field?)>
+
+ <!ELEMENT category (#PCDATA)>
+ <!ATTLIST category match (yes|no|true|false) #REQUIRED>
+
+ <!ELEMENT field (#PCDATA)>
+ <!ATTLIST field name CDATA #REQUIRED>
+
<!ELEMENT info (para|note|warning|variablelist|enumlist|info|xi:include)*>
<!ATTLIST info name CDATA #REQUIRED>
<!ATTLIST info language CDATA #REQUIRED>
@@ -52,7 +76,7 @@
<!ELEMENT synopsis (#PCDATA)>
- <!ELEMENT syntax (parameter|xi:include)*>
+ <!ELEMENT syntax (parameter|dataType|category|matchInfo|xi:include)*>
<!ATTLIST syntax argsep CDATA ",">
<!ELEMENT description (para|note|warning|variablelist|enumlist|info|xi:include)*>
@@ -91,7 +115,7 @@
<!ELEMENT replaceable (#PCDATA)>
<!ELEMENT directory (#PCDATA)>
<!ELEMENT astcli (#PCDATA)>
-
+
<!ELEMENT note (para+|xi:include*)>
<!ELEMENT warning (para+|xi:include*)>
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index cded90ef7..7e1ef13ec 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -49,6 +49,7 @@ int ast_ssl_init(void); /*!< Provided by ssl.c */
int ast_test_init(void); /*!< Provided by test.c */
int ast_msg_init(void); /*!< Provided by message.c */
void ast_msg_shutdown(void); /*!< Provided by message.c */
+int aco_init(void); /*!< Provided by config_options.c */
/*!
* \brief Reload asterisk modules.
diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h
index 1b0beb206..64d8d5089 100644
--- a/include/asterisk/config_options.h
+++ b/include/asterisk/config_options.h
@@ -109,6 +109,7 @@ typedef int (*aco_matchvalue_func)(const char *text);
struct aco_type {
/* common stuff */
enum aco_type_t type; /*!< Whether this is a global or item type */
+ const char *name; /*!< The name of this type (must match XML documentation) */
const char *category; /*!< A regular expression for matching categories to be allowed or denied */
const char *matchfield; /*!< An option name to match for this type (i.e. a 'type'-like column) */
const char *matchvalue; /*!< The value of the option to require for matching (i.e. 'peer' for type= in sip.conf) */
@@ -202,6 +203,14 @@ static struct aco_info name = { \
__VA_ARGS__ \
};
+#define CONFIG_INFO_CORE(mod, name, arr, alloc, ...) \
+static struct aco_info name = { \
+ .module = mod, \
+ .global_obj = &arr, \
+ .snapshot_alloc = alloc, \
+ __VA_ARGS__ \
+};
+
/*! \brief Initialize an aco_info structure
* \note aco_info_destroy must be called if this succeeds
* \param info The address of an aco_info struct to initialize
diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h
index ddfcc25d9..063e8c0b3 100644
--- a/include/asterisk/xml.h
+++ b/include/asterisk/xml.h
@@ -23,6 +23,7 @@
struct ast_xml_node;
struct ast_xml_doc;
+struct ast_xml_xpath_results;
/*!
* \brief Initialize the XML library implementation.
@@ -207,6 +208,44 @@ struct ast_xml_node *ast_xml_node_get_parent(struct ast_xml_node *node);
* \brief Dump the specified document to a file. */
int ast_xml_doc_dump_file(FILE *output, struct ast_xml_doc *doc);
+/*!
+ * \brief Free the XPath results
+ * \param results The XPath results object to dispose of
+ *
+ * \since 12
+ */
+void ast_xml_xpath_results_free(struct ast_xml_xpath_results *results);
+
+/*!
+ * \brief Return the number of results from an XPath query
+ * \param results The XPath results object to count
+ * \retval The number of results in the XPath object
+ *
+ * \since 12
+ */
+int ast_xml_xpath_num_results(struct ast_xml_xpath_results *results);
+
+/*!
+ * \brief Return the first result node of an XPath query
+ * \param results The XPath results object to get the first result from
+ * \retval The first result in the XPath object on success
+ * \retval NULL on error
+ *
+ * \since 12
+ */
+struct ast_xml_node *ast_xml_xpath_get_first_result(struct ast_xml_xpath_results *results);
+
+/*!
+ * \brief Execute an XPath query on an XML document
+ * \param doc The XML document to query
+ * \param xpath_str The XPath query string to execute on the document
+ * \retval An object containing the results of the XPath query on success
+ * \retval NULL on failure
+ *
+ * \since 12
+ */
+struct ast_xml_xpath_results *ast_xml_query(struct ast_xml_doc *doc, const char *xpath_str);
+
/* Features using ast_xml_ */
#ifdef HAVE_LIBXML2
#define AST_XML_DOCS
diff --git a/include/asterisk/xmldoc.h b/include/asterisk/xmldoc.h
index 9bf647612..c09f693c8 100644
--- a/include/asterisk/xmldoc.h
+++ b/include/asterisk/xmldoc.h
@@ -35,6 +35,7 @@ enum ast_doc_src {
#ifdef AST_XML_DOCS
struct ao2_container;
+struct ast_xml_node;
/*! \brief Struct that contains the XML documentation for a particular item. Note
* that this is an ao2 ref counted object.
@@ -61,11 +62,27 @@ struct ast_xml_doc_item {
AST_STRING_FIELD(name);
/*! The type of the item */
AST_STRING_FIELD(type);
+ /*! Reference to another field */
+ AST_STRING_FIELD(ref);
);
+ /*! The node that this item was created from. Note that the life time of
+ * the node is not tied to the lifetime of this object.
+ */
+ struct ast_xml_node *node;
/*! The next XML documentation item that matches the same name/item type */
struct ast_xml_doc_item *next;
};
+/*! \brief Execute an XPath query on the loaded XML documentation
+ * \param query The XPath query string to execute
+ * \param ... Variable printf style format arguments
+ * \retval An XPath results object on success
+ * \retval NULL if no match found
+ *
+ * \since 12
+ */
+struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_query(const char *fmt, ...);
+
/*!
* \brief Get the syntax for a specified application or function.
* \param type Application, Function or AGI ?
@@ -138,6 +155,17 @@ char *ast_xmldoc_build_description(const char *type, const char *name, const cha
*/
struct ao2_container *ast_xmldoc_build_documentation(const char *type);
+/*!
+ * \brief Regenerate the documentation for a particular item
+ * \param item The documentation item to regenerate
+ *
+ * \retval -1 on error
+ * \retval 0 on success
+ *
+ * \since 12
+ */
+int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item);
+
#endif /* AST_XML_DOCS */
#endif /* _ASTERISK_XMLDOC_H */
diff --git a/main/asterisk.c b/main/asterisk.c
index 243321dda..1d5371938 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -4170,6 +4170,8 @@ int main(int argc, char *argv[])
ast_xmldoc_load_documentation();
#endif
+ aco_init();
+
if (astdb_init()) {
printf("%s", term_quit());
exit(1);
diff --git a/main/config_options.c b/main/config_options.c
index 5e76a7a7b..7a65cc555 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -31,11 +31,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <regex.h>
+#include "asterisk/_private.h"
#include "asterisk/config.h"
#include "asterisk/config_options.h"
#include "asterisk/stringfields.h"
#include "asterisk/acl.h"
#include "asterisk/frame.h"
+#include "asterisk/xmldoc.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
#ifdef LOW_MEMORY
#define CONFIG_OPT_BUCKETS 5
@@ -70,6 +74,26 @@ struct aco_option {
intptr_t args[0];
};
+#ifdef AST_XML_DOCS
+static struct ao2_container *xmldocs;
+#endif /* AST_XML_DOCS */
+
+/*! \brief Value of the aco_option_type enum as strings */
+static char *aco_option_type_string[] = {
+ "ACL", /* OPT_ACL_T, */
+ "Boolean", /* OPT_BOOL_T, */
+ "Boolean", /* OPT_BOOLFLAG_T, */
+ "String", /* OPT_CHAR_ARRAY_T, */
+ "Codec", /* OPT_CODEC_T, */
+ "Custom", /* OPT_CUSTOM_T, */
+ "Double", /* OPT_DOUBLE_T, */
+ "Integer", /* OPT_INT_T, */
+ "None", /* OPT_NOOP_T, */
+ "IP Address", /* OPT_SOCKADDR_T, */
+ "String", /* OPT_STRINGFIELD_T, */
+ "Unsigned Integer", /* OPT_UINT_T, */
+};
+
void *aco_pending_config(struct aco_info *info)
{
if (!(info && info->internal)) {
@@ -100,6 +124,11 @@ static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *v
static int noop_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
static int chararray_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
+#ifdef AST_XML_DOCS
+static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, unsigned int matches);
+static int xmldoc_update_config_option(struct aco_type **types, const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex, enum aco_option_type type);
+#endif
+
static aco_option_handler ast_config_option_default_handler(enum aco_option_type type)
{
switch(type) {
@@ -142,7 +171,7 @@ static regex_t *build_regex(const char *text)
return regex;
}
-static int link_option_to_types(struct aco_type **types, struct aco_option *opt)
+static int link_option_to_types(struct aco_info *info, struct aco_type **types, struct aco_option *opt)
{
size_t idx = 0;
struct aco_type *type;
@@ -152,7 +181,11 @@ static int link_option_to_types(struct aco_type **types, struct aco_option *opt)
ast_log(LOG_ERROR, "Attempting to register option using uninitialized type\n");
return -1;
}
- if (!ao2_link(type->internal->opts, opt)) {
+ if (!ao2_link(type->internal->opts, opt)
+#ifdef AST_XML_DOCS
+ || xmldoc_update_config_option(types, info->module, opt->name, type->name, opt->default_val, opt->match_type == ACO_REGEX, opt->type)
+#endif /* AST_XML_DOCS */
+ ) {
while (--idx) {
ao2_unlink(types[idx]->internal->opts, opt);
}
@@ -181,7 +214,7 @@ int aco_option_register_deprecated(struct aco_info *info, const char *name, stru
opt->deprecated = 1;
opt->match_type = ACO_EXACT;
- if (link_option_to_types(types, opt)) {
+ if (link_option_to_types(info, types, opt)) {
ao2_ref(opt, -1);
return -1;
}
@@ -189,6 +222,53 @@ int aco_option_register_deprecated(struct aco_info *info, const char *name, stru
return 0;
}
+#ifdef AST_XML_DOCS
+/*! \internal
+ * \brief Find a particular ast_xml_doc_item from it's parent config_info, types, and name
+ */
+static struct ast_xml_doc_item *find_xmldoc_option(struct ast_xml_doc_item *config_info, struct aco_type **types, const char *name)
+{
+ struct ast_xml_doc_item *iter = config_info;
+
+ if (!iter) {
+ return NULL;
+ }
+ /* First is just the configInfo, we can skip it */
+ while ((iter = iter->next)) {
+ size_t x;
+ if (strcasecmp(iter->name, name)) {
+ continue;
+ }
+ for (x = 0; types[x]; x++) {
+ /* All we care about is that at least one type has the option */
+ if (!strcasecmp(types[x]->name, iter->ref)) {
+ return iter;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*! \internal
+ * \brief Find a particular ast_xml_doc_item from it's parent config_info and name
+ */
+static struct ast_xml_doc_item *find_xmldoc_type(struct ast_xml_doc_item *config_info, const char *name)
+{
+ struct ast_xml_doc_item *iter = config_info;
+ if (!iter) {
+ return NULL;
+ }
+ /* First is just the config Info, skip it */
+ while ((iter = iter->next)) {
+ if (!strcasecmp(iter->type, "configObject") && !strcasecmp(iter->name, name)) {
+ break;
+ }
+ }
+ return iter;
+}
+
+#endif /* AST_XML_DOCS */
+
int __aco_option_register(struct aco_info *info, const char *name, enum aco_matchtype matchtype, struct aco_type **types,
const char *default_val, enum aco_option_type kind, aco_option_handler handler, unsigned int flags, size_t argc, ...)
{
@@ -235,7 +315,7 @@ int __aco_option_register(struct aco_info *info, const char *name, enum aco_matc
return -1;
};
- if (link_option_to_types(types, opt)) {
+ if (link_option_to_types(info, types, opt)) {
ao2_ref(opt, -1);
return -1;
}
@@ -535,10 +615,10 @@ try_alias:
}
if (res != ACO_PROCESS_OK) {
- goto end;
+ goto end;
}
- if (info->pre_apply_config && (info->pre_apply_config())) {
+ if (info->pre_apply_config && (info->pre_apply_config())) {
res = ACO_PROCESS_ERROR;
goto end;
}
@@ -648,18 +728,26 @@ static int internal_type_init(struct aco_type *type)
int aco_info_init(struct aco_info *info)
{
- size_t x, y;
+ size_t x = 0, y = 0;
+ struct aco_file *file;
+ struct aco_type *type;
if (!(info->internal = ast_calloc(1, sizeof(*info->internal)))) {
return -1;
}
- for (x = 0; info->files[x]; x++) {
- for (y = 0; info->files[x]->types[y]; y++) {
- if (internal_type_init(info->files[x]->types[y])) {
+ while ((file = info->files[x++])) {
+ while ((type = file->types[y++])) {
+ if (internal_type_init(type)) {
+ goto error;
+ }
+#ifdef AST_XML_DOCS
+ if (xmldoc_update_config_type(info->module, type->name, type->category, type->matchfield, type->matchvalue, type->category_match == ACO_WHITELIST)) {
goto error;
}
+#endif /* AST_XML_DOCS */
}
+ y = 0;
}
return 0;
@@ -714,6 +802,428 @@ int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
return 0;
}
+#ifdef AST_XML_DOCS
+
+/*! \internal
+ * \brief Complete the name of the module the user is looking for
+ */
+static char *complete_config_module(const char *word, int pos, int state)
+{
+ char *c = NULL;
+ size_t wordlen = strlen(word);
+ int which = 0;
+ struct ao2_iterator i;
+ struct ast_xml_doc_item *cur;
+
+ if (pos != 3) {
+ return NULL;
+ }
+
+ i = ao2_iterator_init(xmldocs, 0);
+ while ((cur = ao2_iterator_next(&i))) {
+ if (!strncasecmp(word, cur->name, wordlen) && ++which > state) {
+ c = ast_strdup(cur->name);
+ ao2_ref(cur, -1);
+ break;
+ }
+ ao2_ref(cur, -1);
+ }
+ ao2_iterator_destroy(&i);
+
+ return c;
+}
+
+/*! \internal
+ * \brief Complete the name of the configuration type the user is looking for
+ */
+static char *complete_config_type(const char *module, const char *word, int pos, int state)
+{
+ char *c = NULL;
+ size_t wordlen = strlen(word);
+ int which = 0;
+ RAII_VAR(struct ast_xml_doc_item *, info, NULL, ao2_cleanup);
+ struct ast_xml_doc_item *cur;
+
+ if (pos != 4) {
+ return NULL;
+ }
+
+ if (!(info = ao2_find(xmldocs, module, OBJ_KEY))) {
+ return NULL;
+ }
+
+ cur = info;
+ while ((cur = cur->next)) {
+ if (!strcasecmp(cur->type, "configObject") && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
+ c = ast_strdup(cur->name);
+ break;
+ }
+ }
+ return c;
+}
+
+/*! \internal
+ * \brief Complete the name of the configuration option the user is looking for
+ */
+static char *complete_config_option(const char *module, const char *option, const char *word, int pos, int state)
+{
+ char *c = NULL;
+ size_t wordlen = strlen(word);
+ int which = 0;
+ RAII_VAR(struct ast_xml_doc_item *, info, NULL, ao2_cleanup);
+ struct ast_xml_doc_item *cur;
+
+ if (pos != 5) {
+ return NULL;
+ }
+
+ if (!(info = ao2_find(xmldocs, module, OBJ_KEY))) {
+ return NULL;
+ }
+
+ cur = info;
+ while ((cur = cur->next)) {
+ if (!strcasecmp(cur->type, "configOption") && !strcasecmp(cur->ref, option) && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
+ c = ast_strdup(cur->name);
+ break;
+ }
+ }
+ return c;
+}
+
+/* Define as 0 if we want to allow configurations to be registered without
+ * documentation
+ */
+#define XMLDOC_STRICT 1
+
+/*! \internal
+ * \brief Update the XML documentation for a config type based on its registration
+ */
+static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, unsigned int matches)
+{
+ RAII_VAR(struct ast_xml_xpath_results *, results, NULL, ast_xml_xpath_results_free);
+ RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
+ struct ast_xml_doc_item *config_type;
+ struct ast_xml_node *type, *syntax, *matchinfo, *tmp;
+
+ /* If we already have a syntax element, bail. This isn't an error, since we may unload a module which
+ * has updated the docs and then load it again. */
+ if ((results = ast_xmldoc_query("//configInfo[@name='%s']/*/configObject[@name='%s']/syntax", module, name))) {
+ return 0;
+ }
+
+ if (!(results = ast_xmldoc_query("//configInfo[@name='%s']/*/configObject[@name='%s']", module, name))) {
+ ast_log(LOG_WARNING, "Cannot update type '%s' in module '%s' because it has no existing documentation!\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(type = ast_xml_xpath_get_first_result(results))) {
+ ast_log(LOG_WARNING, "Could not retrieve documentation for type '%s' in module '%s'\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(syntax = ast_xml_new_child(type, "syntax"))) {
+ ast_log(LOG_WARNING, "Could not create syntax node for type '%s' in module '%s'\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(matchinfo = ast_xml_new_child(syntax, "matchInfo"))) {
+ ast_log(LOG_WARNING, "Could not create matchInfo node for type '%s' in module '%s'\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(tmp = ast_xml_new_child(matchinfo, "category"))) {
+ ast_log(LOG_WARNING, "Could not create category node for type '%s' in module '%s'\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ ast_xml_set_text(tmp, category);
+ ast_xml_set_attribute(tmp, "match", matches ? "true" : "false");
+
+ if (!ast_strlen_zero(matchfield) && !(tmp = ast_xml_new_child(matchinfo, "field"))) {
+ ast_log(LOG_WARNING, "Could not add %s attribute for type '%s' in module '%s'\n", matchfield, name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ ast_xml_set_attribute(tmp, "name", matchfield);
+ ast_xml_set_text(tmp, matchvalue);
+
+ if (!config_info || !(config_type = find_xmldoc_type(config_info, name))) {
+ ast_log(LOG_WARNING, "Could not obtain XML documentation item for config type %s\n", name);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (ast_xmldoc_regenerate_doc_item(config_type)) {
+ ast_log(LOG_WARNING, "Could not update type '%s' with values from config type registration\n", name);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ return 0;
+}
+
+/*! \internal
+ * \brief Update the XML documentation for a config option based on its registration
+ */
+static int xmldoc_update_config_option(struct aco_type **types, const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex, enum aco_option_type type)
+{
+ RAII_VAR(struct ast_xml_xpath_results *, results, NULL, ast_xml_xpath_results_free);
+ RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
+ struct ast_xml_doc_item * config_option;
+ struct ast_xml_node *option;
+
+ ast_assert(ARRAY_LEN(aco_option_type_string) > type);
+
+ if (!config_info || !(config_option = find_xmldoc_option(config_info, types, name))) {
+ ast_log(LOG_ERROR, "XML Documentation for option '%s' in modules '%s' not found!\n", name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(results = ast_xmldoc_query("//configInfo[@name='%s']/*/configObject[@name='%s']/configOption[@name='%s']", module, object_name, name))) {
+ ast_log(LOG_WARNING, "Could not find option '%s' with type '%s' in module '%s'\n", name, object_name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ if (!(option = ast_xml_xpath_get_first_result(results))) {
+ ast_log(LOG_WARNING, "Could obtain results for option '%s' with type '%s' in module '%s'\n", name, object_name, module);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+ ast_xml_set_attribute(option, "regex", regex ? "true" : "false");
+ ast_xml_set_attribute(option, "default", default_value);
+ ast_xml_set_attribute(option, "type", aco_option_type_string[type]);
+
+ if (ast_xmldoc_regenerate_doc_item(config_option)) {
+ ast_log(LOG_WARNING, "Could not update option '%s' with values from config option registration\n", name);
+ return XMLDOC_STRICT ? -1 : 0;
+ }
+
+ return 0;
+}
+
+/*! \internal
+ * \brief Show the modules with configuration information
+ */
+static void cli_show_modules(struct ast_cli_args *a)
+{
+ struct ast_xml_doc_item *item;
+ struct ao2_iterator it_items;
+
+ ast_assert(a->argc == 3);
+
+ if (ao2_container_count(xmldocs) == 0) {
+ ast_cli(a->fd, "No modules found.\n");
+ return;
+ }
+
+ it_items = ao2_iterator_init(xmldocs, 0);
+ ast_cli(a->fd, "The following modules have configuration information:\n");
+ while ((item = ao2_iterator_next(&it_items))) {
+ ast_cli(a->fd, "\t%s\n", item->name);
+ ao2_ref(item, -1);
+ }
+ ao2_iterator_destroy(&it_items);
+}
+
+/*! \internal
+ * \brief Show the configuration types for a module
+ */
+static void cli_show_module_types(struct ast_cli_args *a)
+{
+ RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
+ struct ast_xml_doc_item *tmp;
+
+ ast_assert(a->argc == 4);
+
+ if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
+ ast_cli(a->fd, "Module %s not found.\n", a->argv[3]);
+ return;
+ }
+
+ if (ast_str_strlen(item->synopsis)) {
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->synopsis), 1));
+ }
+ if (ast_str_strlen(item->description)) {
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->description), 1));
+ }
+
+ tmp = item;
+ ast_cli(a->fd, "Configuration option types for %s:\n", tmp->name);
+ while ((tmp = tmp->next)) {
+ if (!strcasecmp(tmp->type, "configObject")) {
+ ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name,
+ ast_str_buffer(tmp->synopsis));
+ }
+ }
+}
+
+/*! \internal
+ * \brief Show the information for a configuration type
+ */
+static void cli_show_module_type(struct ast_cli_args *a)
+{
+ RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
+ struct ast_xml_doc_item *tmp;
+ char option_type[64];
+ int match = 0;
+
+ ast_assert(a->argc == 5);
+
+ if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
+ ast_cli(a->fd, "Unknown module %s\n", a->argv[3]);
+ return;
+ }
+
+ tmp = item;
+ while ((tmp = tmp->next)) {
+ if (!strcasecmp(tmp->type, "configObject") && !strcasecmp(tmp->name, a->argv[4])) {
+ match = 1;
+ term_color(option_type, tmp->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_type));
+ ast_cli(a->fd, "%s", option_type);
+ if (ast_str_strlen(tmp->syntax)) {
+ ast_cli(a->fd, ": [%s]\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
+ } else {
+ ast_cli(a->fd, "\n\n");
+ }
+ if (ast_str_strlen(tmp->synopsis)) {
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->synopsis), 1));
+ }
+ if (ast_str_strlen(tmp->description)) {
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
+ }
+ }
+ }
+
+ if (!match) {
+ ast_cli(a->fd, "Unknown configuration type %s\n", a->argv[4]);
+ return;
+ }
+
+ /* Now iterate over the options for the type */
+ tmp = item;
+ while ((tmp = tmp->next)) {
+ if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4])) {
+ ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name,
+ ast_str_buffer(tmp->synopsis));
+ }
+ }
+}
+
+/*! \internal
+ * \brief Show detailed information for an option
+ */
+static void cli_show_module_options(struct ast_cli_args *a)
+{
+ RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
+ struct ast_xml_doc_item *tmp;
+ char option_name[64];
+ int match = 0;
+
+ ast_assert(a->argc == 6);
+
+ if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
+ ast_cli(a->fd, "Unknown module %s\n", a->argv[3]);
+ return;
+ }
+ tmp = item;
+ while ((tmp = tmp->next)) {
+ if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4]) && !strcasecmp(tmp->name, a->argv[5])) {
+ if (match) {
+ ast_cli(a->fd, "\n");
+ }
+ term_color(option_name, tmp->ref, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_name));
+ ast_cli(a->fd, "[%s%s]\n", option_name, term_end());
+ if (ast_str_strlen(tmp->syntax)) {
+ ast_cli(a->fd, "%s\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
+ }
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(AS_OR(tmp->synopsis, "No information available"), 1));
+ if (ast_str_strlen(tmp->description)) {
+ ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
+ }
+
+ match = 1;
+ }
+ }
+
+ if (!match) {
+ ast_cli(a->fd, "No option %s found for %s:%s\n", a->argv[5], a->argv[3], a->argv[4]);
+ }
+}
+
+static char *cli_show_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "config show help";
+ e->usage =
+ "Usage: config show help [<module> [<type> [<option>]]]\n"
+ " Display detailed information about module configuration.\n"
+ " * If nothing is specified, the modules that have\n"
+ " configuration information are listed.\n"
+ " * If <module> is specified, the configuration types\n"
+ " for that module will be listed, along with brief\n"
+ " information about that type.\n"
+ " * If <module> and <type> are specified, detailed\n"
+ " information about the type is displayed, as well\n"
+ " as the available options.\n"
+ " * If <module>, <type>, and <option> are specified,\n"
+ " detailed information will be displayed about that\n"
+ " option.\n"
+ " NOTE: the help documentation is partially generated at run\n"
+ " time when a module is loaded. If a module is not loaded,\n"
+ " configuration help for that module may be incomplete.\n";
+ return NULL;
+ case CLI_GENERATE:
+ switch(a->pos) {
+ case 3: return complete_config_module(a->word, a->pos, a->n);
+ case 4: return complete_config_type(a->argv[3], a->word, a->pos, a->n);
+ case 5: return complete_config_option(a->argv[3], a->argv[4], a->word, a->pos, a->n);
+ default: return NULL;
+ }
+ }
+
+ switch (a->argc) {
+ case 3:
+ cli_show_modules(a);
+ break;
+ case 4:
+ cli_show_module_types(a);
+ break;
+ case 5:
+ cli_show_module_type(a);
+ break;
+ case 6:
+ cli_show_module_options(a);
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_aco[] = {
+ AST_CLI_DEFINE(cli_show_help, "Show configuration help for a module"),
+};
+
+static void aco_deinit(void)
+{
+ ast_cli_unregister(cli_aco);
+ ao2_cleanup(xmldocs);
+}
+#endif /* AST_XML_DOCS */
+
+int aco_init(void)
+{
+#ifdef AST_XML_DOCS
+ ast_register_atexit(aco_deinit);
+ if (!(xmldocs = ast_xmldoc_build_documentation("configInfo"))) {
+ ast_log(LOG_ERROR, "Couldn't build config documentation\n");
+ return -1;
+ }
+ ast_cli_register_multiple(cli_aco, ARRAY_LEN(cli_aco));
+#endif /* AST_XML_DOCS */
+ return 0;
+}
+
/* Default config option handlers */
/*! \brief Default option handler for signed integers
@@ -730,7 +1240,7 @@ static int int_handler_fn(const struct aco_option *opt, struct ast_variable *var
ast_parse_arg(var->value, flags, field, (int) opt->args[1], (int) opt->args[2]);
if (res) {
if (opt->flags & PARSE_RANGE_DEFAULTS) {
- ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
+ ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
res = 0;
} else if (opt->flags & PARSE_DEFAULT) {
ast_log(LOG_WARNING, "Failed to set %s=%s, Set to default value %d instead.\n", var->name, var->value, *field);
@@ -760,7 +1270,7 @@ static int uint_handler_fn(const struct aco_option *opt, struct ast_variable *va
ast_parse_arg(var->value, flags, field, (unsigned int) opt->args[1], (unsigned int) opt->args[2]);
if (res) {
if (opt->flags & PARSE_RANGE_DEFAULTS) {
- ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
+ ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
res = 0;
} else if (opt->flags & PARSE_DEFAULT) {
ast_log(LOG_WARNING, "Failed to set %s=%s, Set to default value %d instead.\n", var->name, var->value, *field);
diff --git a/main/named_acl.c b/main/named_acl.c
index 074f4c57f..142693194 100644
--- a/main/named_acl.c
+++ b/main/named_acl.c
@@ -44,11 +44,21 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define NACL_CONFIG "acl.conf"
#define ACL_FAMILY "acls"
-struct named_acl_global_config {
- AST_DECLARE_STRING_FIELDS(
- /* Nothing here yet. */
- );
-};
+/*** DOCUMENTATION
+ <configInfo name="named_acl" language="en_US">
+ <configFile name="named_acl.conf">
+ <configObject name="named_acl">
+ <synopsis>Options for configuring a named ACL</synopsis>
+ <configOption name="permit">
+ <synopsis>An address/subnet from which to allow access</synopsis>
+ </configOption>
+ <configOption name="deny">
+ <synopsis>An address/subnet from which to disallow access</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+***/
/*
* Configuration structure - holds pointers to ao2 containers used for configuration
@@ -56,7 +66,6 @@ struct named_acl_global_config {
* time, it's really a config options friendly wrapper for the named ACL container
*/
struct named_acl_config {
- struct named_acl_global_config *global;
struct ao2_container *named_acl_list;
};
@@ -70,6 +79,7 @@ static void *named_acl_find(struct ao2_container *container, const char *cat);
/* Config type for named ACL profiles (must not be named general) */
static struct aco_type named_acl_type = {
.type = ACO_ITEM, /*!< named_acls are items stored in containers, not individual global objects */
+ .name = "named_acl",
.category_match = ACO_BLACKLIST,
.category = "^general$", /*!< Match everything but "general" */
.item_alloc = named_acl_alloc, /*!< A callback to allocate a new named_acl based on category */
@@ -77,26 +87,16 @@ static struct aco_type named_acl_type = {
.item_offset = offsetof(struct named_acl_config, named_acl_list), /*!< Could leave this out since 0 */
};
-/* Config type for the general part of the ACL profile (must be named general) */
-static struct aco_type global_option = {
- .type = ACO_GLOBAL,
- .item_offset = offsetof(struct named_acl_config, global),
- .category_match = ACO_WHITELIST,
- .category = "^general$",
-};
-
/* This array of aco_type structs is necessary to use aco_option_register */
struct aco_type *named_acl_types[] = ACO_TYPES(&named_acl_type);
-struct aco_type *global_options[] = ACO_TYPES(&global_option);
-
struct aco_file named_acl_conf = {
.filename = "acl.conf",
- .types = ACO_TYPES(&named_acl_type, &global_option),
+ .types = ACO_TYPES(&named_acl_type),
};
/* Create a config info struct that describes the config processing for named ACLs. */
-CONFIG_INFO_STANDARD(cfg_info, globals, named_acl_config_alloc,
+CONFIG_INFO_CORE("named_acl", cfg_info, globals, named_acl_config_alloc,
.files = ACO_FILES(&named_acl_conf),
);
@@ -124,13 +124,6 @@ static void named_acl_config_destructor(void *obj)
{
struct named_acl_config *cfg = obj;
ao2_cleanup(cfg->named_acl_list);
- ao2_cleanup(cfg->global);
-}
-
-static void named_acl_global_config_destructor(void *obj)
-{
- struct named_acl_global_config *global = obj;
- ast_string_field_free_memory(global);
}
/*! \brief allocator callback for named_acl_config. Notice it returns void * since it is used by
@@ -144,14 +137,6 @@ static void *named_acl_config_alloc(void)
return NULL;
}
- if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), named_acl_global_config_destructor))) {
- goto error;
- }
-
- if (ast_string_field_init(cfg->global, 128)) {
- goto error;
- }
-
if (!(cfg->named_acl_list = ao2_container_alloc(37, named_acl_hash_fn, named_acl_cmp_fn))) {
goto error;
}
diff --git a/main/udptl.c b/main/udptl.c
index 37f04d530..8ae8334a9 100644
--- a/main/udptl.c
+++ b/main/udptl.c
@@ -80,6 +80,40 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/unaligned.h"
+/*** DOCUMENTATION
+ <configInfo name="udptl" language="en_US">
+ <configFile name="udptl.conf">
+ <configObject name="global">
+ <synopsis>Global options for configuring UDPTL</synopsis>
+ <configOption name="udptlstart">
+ <synopsis>The start of the UDPTL port range</synopsis>
+ </configOption>
+ <configOption name="udptlend">
+ <synopsis>The end of the UDPTL port range</synopsis>
+ </configOption>
+ <configOption name="udptlchecksums">
+ <synopsis>Whether to enable or disable UDP checksums on UDPTL traffic</synopsis>
+ </configOption>
+ <configOption name="udptlfecentries">
+ <synopsis>The number of error correction entries in a UDPTL packet</synopsis>
+ </configOption>
+ <configOption name="udptlfecspan">
+ <synopsis>The span over which parity is calculated for FEC in a UDPTL packet</synopsis>
+ </configOption>
+ <configOption name="use_even_ports">
+ <synopsis>Whether to only use even-numbered UDPTL ports</synopsis>
+ </configOption>
+ <configOption name="t38faxudpec">
+ <synopsis>Removed</synopsis>
+ </configOption>
+ <configOption name="t38faxmaxdatagram">
+ <synopsis>Removed</synopsis>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+***/
+
#define UDPTL_MTU 1200
#if !defined(FALSE)
@@ -206,6 +240,7 @@ static int udptl_pre_apply_config(void);
static struct aco_type general_option = {
.type = ACO_GLOBAL,
+ .name = "global",
.category_match = ACO_WHITELIST,
.item_offset = offsetof(struct udptl_config, general),
.category = "^general$",
@@ -218,7 +253,7 @@ static struct aco_file udptl_conf = {
.types = ACO_TYPES(&general_option),
};
-CONFIG_INFO_STANDARD(cfg_info, globals, udptl_snapshot_alloc,
+CONFIG_INFO_CORE("udptl", cfg_info, globals, udptl_snapshot_alloc,
.files = ACO_FILES(&udptl_conf),
.pre_apply_config = udptl_pre_apply_config,
);
diff --git a/main/xml.c b/main/xml.c
index bdf983e51..5ca4d4ff1 100644
--- a/main/xml.c
+++ b/main/xml.c
@@ -28,6 +28,7 @@
#include "asterisk.h"
#include "asterisk/xml.h"
#include "asterisk/logger.h"
+#include "asterisk/utils.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -35,6 +36,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xinclude.h>
+#include <libxml/xpath.h>
/* libxml2 ast_xml implementation. */
@@ -316,5 +318,42 @@ struct ast_xml_node *ast_xml_node_get_parent(struct ast_xml_node *node)
return (struct ast_xml_node *) ((xmlNode *) node)->parent;
}
+struct ast_xml_node *ast_xml_xpath_get_first_result(struct ast_xml_xpath_results *results)
+{
+ return (struct ast_xml_node *) ((xmlXPathObjectPtr) results)->nodesetval->nodeTab[0];
+}
+
+void ast_xml_xpath_results_free(struct ast_xml_xpath_results *results)
+{
+ xmlXPathFreeObject((xmlXPathObjectPtr) results);
+}
+
+int ast_xml_xpath_num_results(struct ast_xml_xpath_results *results)
+{
+ return ((xmlXPathObjectPtr) results)->nodesetval->nodeNr;
+}
+
+struct ast_xml_xpath_results *ast_xml_query(struct ast_xml_doc *doc, const char *xpath_str)
+{
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr result;
+ if (!(context = xmlXPathNewContext((xmlDoc *) doc))) {
+ ast_log(LOG_ERROR, "Could not create XPath context!\n");
+ return NULL;
+ }
+ result = xmlXPathEvalExpression((xmlChar *) xpath_str, context);
+ xmlXPathFreeContext(context);
+ if (!result) {
+ ast_log(LOG_WARNING, "Error for query: %s\n", xpath_str);
+ return NULL;
+ }
+ if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
+ xmlXPathFreeObject(result);
+ ast_debug(5, "No results for query: %s\n", xpath_str);
+ return NULL;
+ }
+ return (struct ast_xml_xpath_results *) result;
+}
+
#endif /* defined(HAVE_LIBXML2) */
diff --git a/main/xmldoc.c b/main/xmldoc.c
index 225ed6611..a38b59852 100644
--- a/main/xmldoc.c
+++ b/main/xmldoc.c
@@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/term.h"
#include "asterisk/astobj2.h"
#include "asterisk/xmldoc.h"
+#include "asterisk/cli.h"
#ifdef AST_XML_DOCS
@@ -979,6 +980,10 @@ static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *nam
const char *paramtype, *attrname, *literal;
int required, isenum, first = 1, isliteral;
+ if (!fixnode) {
+ return NULL;
+ }
+
syntax = ast_str_create(128);
if (!syntax) {
/* at least try to return something... */
@@ -1078,6 +1083,10 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
int required;
char *ret;
+ if (!fixnode) {
+ return NULL;
+ }
+
syntax = ast_str_create(128);
if (!syntax) {
return ast_strdup(name);
@@ -1118,11 +1127,76 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
return ret;
}
+static char *xmldoc_get_syntax_config_object(struct ast_xml_node *fixnode, const char *name)
+{
+ struct ast_xml_node *matchinfo, *tmp;
+ int match;
+ const char *attr_value;
+ const char *text;
+ RAII_VAR(struct ast_str *, syntax, ast_str_create(128), ast_free);
+
+ if (!syntax || !fixnode) {
+ return NULL;
+ }
+ if (!(matchinfo = ast_xml_find_element(ast_xml_node_get_children(fixnode), "matchInfo", NULL, NULL))) {
+ return NULL;
+ }
+ if (!(tmp = ast_xml_find_element(ast_xml_node_get_children(matchinfo), "category", NULL, NULL))) {
+ return NULL;
+ }
+ attr_value = ast_xml_get_attribute(tmp, "match");
+ if (attr_value) {
+ match = ast_true(attr_value);
+ text = ast_xml_get_text(tmp);
+ ast_str_set(&syntax, 0, "category %s /%s/", match ? "=~" : "!~", text);
+ ast_xml_free_attr(attr_value);
+ }
+
+ if ((tmp = ast_xml_find_element(ast_xml_node_get_children(matchinfo), "field", NULL, NULL))) {
+ text = ast_xml_get_text(tmp);
+ attr_value = ast_xml_get_attribute(tmp, "name");
+ ast_str_append(&syntax, 0, " matchfield: %s = %s", S_OR(attr_value, "Unknown"), text);
+ ast_xml_free_attr(attr_value);
+ }
+ return ast_strdup(ast_str_buffer(syntax));
+}
+
+static char *xmldoc_get_syntax_config_option(struct ast_xml_node *fixnode, const char *name)
+{
+ const char *type;
+ const char *default_value;
+ const char *regex;
+ RAII_VAR(struct ast_str *, syntax, ast_str_create(128), ast_free);
+
+ if (!syntax || !fixnode) {
+ return NULL;
+ }
+ type = ast_xml_get_attribute(fixnode, "type");
+ default_value = ast_xml_get_attribute(fixnode, "default");
+
+ regex = ast_xml_get_attribute(fixnode, "regex");
+ ast_str_set(&syntax, 0, "%s = [%s] (Default: %s) (Regex: %s)\n",
+ name,
+ type,
+ default_value,
+ regex ? regex : "False");
+
+ ast_xml_free_attr(type);
+ ast_xml_free_attr(default_value);
+ ast_xml_free_attr(regex);
+
+ return ast_strdup(ast_str_buffer(syntax));
+}
+
/*! \brief Types of syntax that we are able to generate. */
enum syntaxtype {
FUNCTION_SYNTAX,
MANAGER_SYNTAX,
MANAGER_EVENT_SYNTAX,
+ CONFIG_INFO_SYNTAX,
+ CONFIG_FILE_SYNTAX,
+ CONFIG_OPTION_SYNTAX,
+ CONFIG_OBJECT_SYNTAX,
COMMAND_SYNTAX
};
@@ -1131,11 +1205,15 @@ static struct strsyntaxtype {
const char *type;
enum syntaxtype stxtype;
} stxtype[] = {
- { "function", FUNCTION_SYNTAX },
- { "application", FUNCTION_SYNTAX },
- { "manager", MANAGER_SYNTAX },
- { "managerEvent", MANAGER_EVENT_SYNTAX },
- { "agi", COMMAND_SYNTAX }
+ { "function", FUNCTION_SYNTAX },
+ { "application", FUNCTION_SYNTAX },
+ { "manager", MANAGER_SYNTAX },
+ { "managerEvent", MANAGER_EVENT_SYNTAX },
+ { "configInfo", CONFIG_INFO_SYNTAX },
+ { "configFile", CONFIG_FILE_SYNTAX },
+ { "configOption", CONFIG_OPTION_SYNTAX },
+ { "configObject", CONFIG_OBJECT_SYNTAX },
+ { "agi", COMMAND_SYNTAX },
};
/*! \internal
@@ -1170,9 +1248,10 @@ static enum syntaxtype xmldoc_get_syntax_type(const char *type)
*
* \since 11
*/
-static char *_ast_xmldoc_build_syntax(struct ast_xml_node *node, const char *type, const char *name)
+static char *_ast_xmldoc_build_syntax(struct ast_xml_node *root_node, const char *type, const char *name)
{
char *syntax = NULL;
+ struct ast_xml_node *node = root_node;
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
@@ -1180,10 +1259,6 @@ static char *_ast_xmldoc_build_syntax(struct ast_xml_node *node, const char *typ
}
}
- if (!node) {
- return syntax;
- }
-
switch (xmldoc_get_syntax_type(type)) {
case FUNCTION_SYNTAX:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
@@ -1197,6 +1272,12 @@ static char *_ast_xmldoc_build_syntax(struct ast_xml_node *node, const char *typ
case MANAGER_EVENT_SYNTAX:
syntax = xmldoc_get_syntax_manager(node, name, "Event");
break;
+ case CONFIG_OPTION_SYNTAX:
+ syntax = xmldoc_get_syntax_config_option(root_node, name);
+ break;
+ case CONFIG_OBJECT_SYNTAX:
+ syntax = xmldoc_get_syntax_config_object(node, name);
+ break;
default:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
}
@@ -2198,6 +2279,7 @@ static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_n
if (!(item = ast_xml_doc_item_alloc(name, type))) {
return NULL;
}
+ item->node = node;
syntax = _ast_xmldoc_build_syntax(node, type, name);
seealso = _ast_xmldoc_build_seealso(node);
@@ -2230,6 +2312,105 @@ static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_n
return item;
}
+struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_query(const char *fmt, ...)
+{
+ struct ast_xml_xpath_results *results = NULL;
+ struct documentation_tree *doctree;
+ RAII_VAR(struct ast_str *, xpath_str, ast_str_create(128), ast_free);
+ va_list ap;
+
+ if (!xpath_str) {
+ return NULL;
+ }
+
+ va_start(ap, fmt);
+ ast_str_set_va(&xpath_str, 0, fmt, ap);
+ va_end(ap);
+
+ AST_RWLIST_RDLOCK(&xmldoc_tree);
+ AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
+ if (!(results = ast_xml_query(doctree->doc, ast_str_buffer(xpath_str)))) {
+ continue;
+ }
+ break;
+ }
+ AST_RWLIST_UNLOCK(&xmldoc_tree);
+
+ return results;
+}
+
+static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item **tail)
+{
+ struct ast_xml_node *iter;
+ struct ast_xml_doc_item *item;
+
+ for (iter = ast_xml_node_get_children(cur); iter; iter = ast_xml_node_get_next(iter)) {
+ if (strncasecmp(ast_xml_node_get_name(iter), "config", 6)) {
+ continue;
+ }
+ /* Now add all of the child config-related items to the list */
+ if (!(item = xmldoc_build_documentation_item(iter, ast_xml_get_attribute(iter, "name"), ast_xml_node_get_name(iter)))) {
+ ast_log(LOG_ERROR, "Could not build documentation for '%s:%s'\n", ast_xml_node_get_name(iter), ast_xml_get_attribute(iter, "name"));
+ break;
+ }
+ if (!strcasecmp(ast_xml_node_get_name(iter), "configOption")) {
+ ast_string_field_set(item, ref, ast_xml_get_attribute(cur, "name"));
+ }
+ (*tail)->next = item;
+ *tail = (*tail)->next;
+ build_config_docs(iter, tail);
+ }
+}
+
+int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item)
+{
+ const char *name;
+ char *syntax;
+ char *seealso;
+ char *arguments;
+ char *synopsis;
+ char *description;
+
+ if (!item || !item->node) {
+ return -1;
+ }
+
+ name = ast_xml_get_attribute(item->node, "name");
+ if (!name) {
+ return -1;
+ }
+
+ syntax = _ast_xmldoc_build_syntax(item->node, item->type, name);
+ seealso = _ast_xmldoc_build_seealso(item->node);
+ arguments = _ast_xmldoc_build_arguments(item->node);
+ synopsis = _ast_xmldoc_build_synopsis(item->node);
+ description = _ast_xmldoc_build_description(item->node);
+
+ if (syntax) {
+ ast_str_set(&item->syntax, 0, "%s", syntax);
+ }
+ if (seealso) {
+ ast_str_set(&item->seealso, 0, "%s", seealso);
+ }
+ if (arguments) {
+ ast_str_set(&item->arguments, 0, "%s", arguments);
+ }
+ if (synopsis) {
+ ast_str_set(&item->synopsis, 0, "%s", synopsis);
+ }
+ if (description) {
+ ast_str_set(&item->description, 0, "%s", description);
+ }
+
+ ast_free(syntax);
+ ast_free(seealso);
+ ast_free(arguments);
+ ast_free(synopsis);
+ ast_free(description);
+ ast_xml_free_attr(name);
+ return 0;
+}
+
struct ao2_container *ast_xmldoc_build_documentation(const char *type)
{
struct ao2_container *docs;
@@ -2282,6 +2463,19 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type)
}
item = root;
break;
+ case CONFIG_INFO_SYNTAX:
+ {
+ struct ast_xml_doc_item *tail;
+ if (item || !ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) {
+ break;
+ }
+ if (!(item = xmldoc_build_documentation_item(node, ast_xml_get_attribute(node, "name"), "configInfo"))) {
+ break;
+ }
+ tail = item;
+ build_config_docs(node, &tail);
+ break;
+ }
default:
item = xmldoc_build_documentation_item(node, name, type);
}
@@ -2299,6 +2493,9 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type)
return docs;
}
+int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item);
+
+
#if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
{
@@ -2342,11 +2539,47 @@ static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbu
}
#endif
+static char *handle_dump_docs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct documentation_tree *doctree;
+ FILE *f;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "xmldoc dump";
+ e->usage =
+ "Usage: xmldoc dump <filename>\n"
+ " Dump XML documentation to a file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+ if (!(f = fopen(a->argv[2], "w"))) {
+ ast_log(LOG_ERROR, "Could not open file '%s': %s\n", a->argv[2], strerror(errno));
+ return CLI_FAILURE;
+ }
+ AST_RWLIST_RDLOCK(&xmldoc_tree);
+ AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
+ ast_xml_doc_dump_file(f, doctree->doc);
+ }
+ AST_RWLIST_UNLOCK(&xmldoc_tree);
+ fclose(f);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_dump_xmldocs = AST_CLI_DEFINE(handle_dump_docs, "Dump the XML docs to the specified file");
+
/*! \brief Close and unload XML documentation. */
static void xmldoc_unload_documentation(void)
{
struct documentation_tree *doctree;
+ ast_cli_unregister(&cli_dump_xmldocs);
+
AST_RWLIST_WRLOCK(&xmldoc_tree);
while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
ast_free(doctree->filename);
@@ -2390,6 +2623,7 @@ int ast_xmldoc_load_documentation(void)
/* initialize the XML library. */
ast_xml_init();
+ ast_cli_register(&cli_dump_xmldocs);
/* register function to be run when asterisk finish. */
ast_register_atexit(xmldoc_unload_documentation);
diff --git a/res/res_xmpp.c b/res/res_xmpp.c
index 3ba849e56..966432dc0 100644
--- a/res/res_xmpp.c
+++ b/res/res_xmpp.c
@@ -288,6 +288,126 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
account defined in <literal>xmpp.conf</literal> to send the message from.
Note that this field is required for XMPP messages.</para>
</info>
+ <configInfo name="res_xmpp" language="en_US">
+ <synopsis>XMPP Messaging</synopsis>
+ <configFile name="xmpp.conf">
+ <configObject name="global">
+ <synopsis>Global configuration settings</synopsis>
+ <configOption name="debug">
+ <synopsis>Enable/disable XMPP message debugging</synopsis>
+ </configOption>
+ <configOption name="autoprune">
+ <synopsis>Auto-remove users from buddy list.</synopsis>
+ <description><para>Auto-remove users from buddy list. Depending on the setup
+ (e.g., using your personal Gtalk account for a test) this could cause loss of
+ the contact list.
+ </para></description>
+ </configOption>
+ <configOption name="autoregister">
+ <synopsis>Auto-register users from buddy list</synopsis>
+ </configOption>
+ <configOption name="collection_nodes">
+ <synopsis>Enable support for XEP-0248 for use with distributed device state</synopsis>
+ </configOption>
+ <configOption name="pubsub_autocreate">
+ <synopsis>Whether or not the PubSub server supports/is using auto-create for nodes</synopsis>
+ </configOption>
+ <configOption name="auth_policy">
+ <synopsis>Whether to automatically accept or deny users' subscription requests</synopsis>
+ </configOption>
+ </configObject>
+ <configObject name="client">
+ <synopsis>Configuration options for an XMPP client</synopsis>
+ <configOption name="username">
+ <synopsis>XMPP username with optional resource</synopsis>
+ </configOption>
+ <configOption name="secret">
+ <synopsis>XMPP password</synopsis>
+ </configOption>
+ <configOption name="serverhost">
+ <synopsis>Route to server, e.g. talk.google.com</synopsis>
+ </configOption>
+ <configOption name="statusmessage">
+ <synopsis>Custom status message</synopsis>
+ </configOption>
+ <configOption name="pubsub_node">
+ <synopsis>Node for publishing events via PubSub</synopsis>
+ </configOption>
+ <configOption name="context">
+ <synopsis>Dialplan context to send incoming messages to</synopsis>
+ </configOption>
+ <configOption name="priority">
+ <synopsis>XMPP resource priority</synopsis>
+ </configOption>
+ <configOption name="port">
+ <synopsis>XMPP server port</synopsis>
+ </configOption>
+ <configOption name="timeout">
+ <synopsis>Timeout in seconds to hold incoming messages</synopsis>
+ <description><para>Timeout (in seconds) on the message stack. Messages stored longer
+ than this value will be deleted by Asterisk. This option applies to incoming messages only
+ which are intended to be processed by the <literal>JABBER_RECEIVE</literal> dialplan function.
+ </para></description>
+ </configOption>
+ <configOption name="debug">
+ <synopsis>Enable debugging</synopsis>
+ </configOption>
+ <configOption name="type">
+ <synopsis>Connection is either a client or a component</synopsis>
+ </configOption>
+ <configOption name="distribute_events">
+ <synopsis>Whether or not to distribute events using this connection</synopsis>
+ </configOption>
+ <configOption name="usetls">
+ <synopsis>Whether to use TLS for the connection or not</synopsis>
+ </configOption>
+ <configOption name="usesasl">
+ <synopsis>Whether to use SASL for the connection or not</synopsis>
+ </configOption>
+ <configOption name="forceoldssl">
+ <synopsis>Force the use of old-style SSL for the connection</synopsis>
+ </configOption>
+ <configOption name="keepalive">
+ <synopsis>If enabled, periodically send an XMPP message from this client with an empty message</synopsis>
+ </configOption>
+ <configOption name="autoprune">
+ <synopsis>Auto-remove users from buddy list.</synopsis>
+ <description><para>Auto-remove users from buddy list. Depending on the setup
+ (e.g., using your personal Gtalk account for a test) this could cause loss of
+ the contact list.
+ </para></description>
+ </configOption>
+ <configOption name="autoregister">
+ <synopsis>Auto-register users bfrom buddy list</synopsis>
+ </configOption>
+ <configOption name="auth_policy">
+ <synopsis>Whether to automatically accept or deny users' subscription requests</synopsis>
+ </configOption>
+ <configOption name="sendtodialplan">
+ <synopsis>Send incoming messages into the dialplan</synopsis>
+ </configOption>
+ <configOption name="status">
+ <synopsis>Default XMPP status for the client</synopsis>
+ <description><para>Can be one of the following XMPP statuses:</para>
+ <enumlist>
+ <enum name="chat"/>
+ <enum name="available"/>
+ <enum name="away"/>
+ <enum name="xaway"/>
+ <enum name="dnd"/>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="buddy">
+ <synopsis>Manual addition of buddy to list</synopsis>
+ <description><para>
+ Manual addition of buddy to the buddy list. For distributed events, these budies are
+ automatically added in the whitelist as 'owners' of the node(s).
+ </para></description>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
***/
/*! \brief Supported general configuration flags */
@@ -658,6 +778,7 @@ static void xmpp_config_post_apply(void)
static struct aco_type global_option = {
.type = ACO_GLOBAL,
+ .name = "global",
.item_offset = offsetof(struct xmpp_config, global),
.category_match = ACO_WHITELIST,
.category = "^general$",
@@ -667,6 +788,7 @@ struct aco_type *global_options[] = ACO_TYPES(&global_option);
static struct aco_type client_option = {
.type = ACO_ITEM,
+ .name = "client",
.category_match = ACO_BLACKLIST,
.category = "^(general)$",
.item_alloc = ast_xmpp_client_config_alloc,