#include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
#include "asterisk/_private.h"
#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
#include <ctype.h>
#include "asterisk/astobj2.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/dial.h"
+#include "asterisk/vector.h"
+#include "pbx_private.h"
/*!
* \note I M P O R T A N T :
*/
/*** DOCUMENTATION
- <application name="Answer" language="en_US">
- <synopsis>
- Answer a channel if ringing.
- </synopsis>
- <syntax>
- <parameter name="delay">
- <para>Asterisk will wait this number of milliseconds before returning to
- the dialplan after answering the call.</para>
- </parameter>
- </syntax>
- <description>
- <para>If the call has not been answered, this application will
- answer it. Otherwise, it has no effect on the call.</para>
- </description>
- <see-also>
- <ref type="application">Hangup</ref>
- </see-also>
- </application>
- <application name="BackGround" language="en_US">
- <synopsis>
- Play an audio file while waiting for digits of an extension to go to.
- </synopsis>
- <syntax>
- <parameter name="filenames" required="true" argsep="&">
- <argument name="filename1" required="true" />
- <argument name="filename2" multiple="true" />
- </parameter>
- <parameter name="options">
- <optionlist>
- <option name="s">
- <para>Causes the playback of the message to be skipped
- if the channel is not in the <literal>up</literal> state (i.e. it
- hasn't been answered yet). If this happens, the
- application will return immediately.</para>
- </option>
- <option name="n">
- <para>Don't answer the channel before playing the files.</para>
- </option>
- <option name="m">
- <para>Only break if a digit hit matches a one digit
- extension in the destination context.</para>
- </option>
- </optionlist>
- </parameter>
- <parameter name="langoverride">
- <para>Explicitly specifies which language to attempt to use for the requested sound files.</para>
- </parameter>
- <parameter name="context">
- <para>This is the dialplan context that this application will use when exiting
- to a dialed extension.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application will play the given list of files <emphasis>(do not put extension)</emphasis>
- while waiting for an extension to be dialed by the calling channel. To continue waiting
- for digits after this application has finished playing files, the <literal>WaitExten</literal>
- application should be used.</para>
- <para>If one of the requested sound files does not exist, call processing will be terminated.</para>
- <para>This application sets the following channel variable upon completion:</para>
- <variablelist>
- <variable name="BACKGROUNDSTATUS">
- <para>The status of the background attempt as a text string.</para>
- <value name="SUCCESS" />
- <value name="FAILED" />
- </variable>
- </variablelist>
- </description>
- <see-also>
- <ref type="application">ControlPlayback</ref>
- <ref type="application">WaitExten</ref>
- <ref type="application">BackgroundDetect</ref>
- <ref type="function">TIMEOUT</ref>
- </see-also>
- </application>
- <application name="Busy" language="en_US">
- <synopsis>
- Indicate the Busy condition.
- </synopsis>
- <syntax>
- <parameter name="timeout">
- <para>If specified, the calling channel will be hung up after the specified number of seconds.
- Otherwise, this application will wait until the calling channel hangs up.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application will indicate the busy condition to the calling channel.</para>
- </description>
- <see-also>
- <ref type="application">Congestion</ref>
- <ref type="application">Progress</ref>
- <ref type="application">Playtones</ref>
- <ref type="application">Hangup</ref>
- </see-also>
- </application>
- <application name="Congestion" language="en_US">
- <synopsis>
- Indicate the Congestion condition.
- </synopsis>
- <syntax>
- <parameter name="timeout">
- <para>If specified, the calling channel will be hung up after the specified number of seconds.
- Otherwise, this application will wait until the calling channel hangs up.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application will indicate the congestion condition to the calling channel.</para>
- </description>
- <see-also>
- <ref type="application">Busy</ref>
- <ref type="application">Progress</ref>
- <ref type="application">Playtones</ref>
- <ref type="application">Hangup</ref>
- </see-also>
- </application>
- <application name="ExecIfTime" language="en_US">
- <synopsis>
- Conditional application execution based on the current time.
- </synopsis>
- <syntax argsep="?">
- <parameter name="day_condition" required="true">
- <argument name="times" required="true" />
- <argument name="weekdays" required="true" />
- <argument name="mdays" required="true" />
- <argument name="months" required="true" />
- <argument name="timezone" required="false" />
- </parameter>
- <parameter name="appname" required="true" hasparams="optional">
- <argument name="appargs" required="true" />
- </parameter>
- </syntax>
- <description>
- <para>This application will execute the specified dialplan application, with optional
- arguments, if the current time matches the given time specification.</para>
- </description>
- <see-also>
- <ref type="application">Exec</ref>
- <ref type="application">ExecIf</ref>
- <ref type="application">TryExec</ref>
- <ref type="application">GotoIfTime</ref>
- </see-also>
- </application>
- <application name="Goto" language="en_US">
- <synopsis>
- Jump to a particular priority, extension, or context.
- </synopsis>
- <syntax>
- <parameter name="context" />
- <parameter name="extensions" />
- <parameter name="priority" required="true" />
- </syntax>
- <description>
- <para>This application will set the current context, extension, and priority in the channel structure.
- After it completes, the pbx engine will continue dialplan execution at the specified location.
- If no specific <replaceable>extension</replaceable>, or <replaceable>extension</replaceable> and
- <replaceable>context</replaceable>, are specified, then this application will
- just set the specified <replaceable>priority</replaceable> of the current extension.</para>
- <para>At least a <replaceable>priority</replaceable> is required as an argument, or the goto will
- return a <literal>-1</literal>, and the channel and call will be terminated.</para>
- <para>If the location that is put into the channel information is bogus, and asterisk cannot
- find that location in the dialplan, then the execution engine will try to find and execute the code in
- the <literal>i</literal> (invalid) extension in the current context. If that does not exist, it will try to execute the
- <literal>h</literal> extension. If neither the <literal>h</literal> nor <literal>i</literal> extensions
- have been defined, the channel is hung up, and the execution of instructions on the channel is terminated.
- What this means is that, for example, you specify a context that does not exist, then
- it will not be possible to find the <literal>h</literal> or <literal>i</literal> extensions,
- and the call will terminate!</para>
- </description>
- <see-also>
- <ref type="application">GotoIf</ref>
- <ref type="application">GotoIfTime</ref>
- <ref type="application">Gosub</ref>
- <ref type="application">Macro</ref>
- </see-also>
- </application>
- <application name="GotoIf" language="en_US">
- <synopsis>
- Conditional goto.
- </synopsis>
- <syntax argsep="?">
- <parameter name="condition" required="true" />
- <parameter name="destination" required="true" argsep=":">
- <argument name="labeliftrue">
- <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
- Takes the form similar to Goto() of [[context,]extension,]priority.</para>
- </argument>
- <argument name="labeliffalse">
- <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
- Takes the form similar to Goto() of [[context,]extension,]priority.</para>
- </argument>
- </parameter>
- </syntax>
- <description>
- <para>This application will set the current context, extension, and priority in the channel structure
- based on the evaluation of the given condition. After this application completes, the
- pbx engine will continue dialplan execution at the specified location in the dialplan.
- The labels are specified with the same syntax as used within the Goto application.
- If the label chosen by the condition is omitted, no jump is performed, and the execution passes to the
- next instruction. If the target location is bogus, and does not exist, the execution engine will try
- to find and execute the code in the <literal>i</literal> (invalid) extension in the current context.
- If that does not exist, it will try to execute the <literal>h</literal> extension.
- If neither the <literal>h</literal> nor <literal>i</literal> extensions have been defined,
- the channel is hung up, and the execution of instructions on the channel is terminated.
- Remember that this command can set the current context, and if the context specified
- does not exist, then it will not be able to find any 'h' or 'i' extensions there, and
- the channel and call will both be terminated!.</para>
- </description>
- <see-also>
- <ref type="application">Goto</ref>
- <ref type="application">GotoIfTime</ref>
- <ref type="application">GosubIf</ref>
- <ref type="application">MacroIf</ref>
- </see-also>
- </application>
- <application name="GotoIfTime" language="en_US">
- <synopsis>
- Conditional Goto based on the current time.
- </synopsis>
- <syntax argsep="?">
- <parameter name="condition" required="true">
- <argument name="times" required="true" />
- <argument name="weekdays" required="true" />
- <argument name="mdays" required="true" />
- <argument name="months" required="true" />
- <argument name="timezone" required="false" />
- </parameter>
- <parameter name="destination" required="true" argsep=":">
- <argument name="labeliftrue">
- <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
- Takes the form similar to Goto() of [[context,]extension,]priority.</para>
- </argument>
- <argument name="labeliffalse">
- <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
- Takes the form similar to Goto() of [[context,]extension,]priority.</para>
- </argument>
- </parameter>
- </syntax>
- <description>
- <para>This application will set the context, extension, and priority in the channel structure
- based on the evaluation of the given time specification. After this application completes,
- the pbx engine will continue dialplan execution at the specified location in the dialplan.
- If the current time is within the given time specification, the channel will continue at
- <replaceable>labeliftrue</replaceable>. Otherwise the channel will continue at <replaceable>labeliffalse</replaceable>.
- If the label chosen by the condition is omitted, no jump is performed, and execution passes to the next
- instruction. If the target jump location is bogus, the same actions would be taken as for <literal>Goto</literal>.
- Further information on the time specification can be found in examples
- illustrating how to do time-based context includes in the dialplan.</para>
- </description>
- <see-also>
- <ref type="application">GotoIf</ref>
- <ref type="application">Goto</ref>
- <ref type="function">IFTIME</ref>
- <ref type="function">TESTTIME</ref>
- </see-also>
- </application>
- <application name="ImportVar" language="en_US">
- <synopsis>
- Import a variable from a channel into a new variable.
- </synopsis>
- <syntax argsep="=">
- <parameter name="newvar" required="true" />
- <parameter name="vardata" required="true">
- <argument name="channelname" required="true" />
- <argument name="variable" required="true" />
- </parameter>
- </syntax>
- <description>
- <para>This application imports a <replaceable>variable</replaceable> from the specified
- <replaceable>channel</replaceable> (as opposed to the current one) and stores it as a variable
- (<replaceable>newvar</replaceable>) in the current channel (the channel that is calling this
- application). Variables created by this application have the same inheritance properties as those
- created with the <literal>Set</literal> application.</para>
- </description>
- <see-also>
- <ref type="application">Set</ref>
- </see-also>
- </application>
- <application name="Hangup" language="en_US">
- <synopsis>
- Hang up the calling channel.
- </synopsis>
- <syntax>
- <parameter name="causecode">
- <para>If a <replaceable>causecode</replaceable> is given the channel's
- hangup cause will be set to the given value.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application will hang up the calling channel.</para>
- </description>
- <see-also>
- <ref type="application">Answer</ref>
- <ref type="application">Busy</ref>
- <ref type="application">Congestion</ref>
- </see-also>
- </application>
- <application name="Incomplete" language="en_US">
- <synopsis>
- Returns AST_PBX_INCOMPLETE value.
- </synopsis>
- <syntax>
- <parameter name="n">
- <para>If specified, then Incomplete will not attempt to answer the channel first.</para>
- <note><para>Most channel types need to be in Answer state in order to receive DTMF.</para></note>
- </parameter>
- </syntax>
- <description>
- <para>Signals the PBX routines that the previous matched extension is incomplete
- and that further input should be allowed before matching can be considered
- to be complete. Can be used within a pattern match when certain criteria warrants
- a longer match.</para>
- </description>
- </application>
- <application name="NoOp" language="en_US">
- <synopsis>
- Do Nothing (No Operation).
- </synopsis>
- <syntax>
- <parameter name="text">
- <para>Any text provided can be viewed at the Asterisk CLI.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application does nothing. However, it is useful for debugging purposes.</para>
- <para>This method can be used to see the evaluations of variables or functions without having any effect.</para>
- </description>
- <see-also>
- <ref type="application">Verbose</ref>
- <ref type="application">Log</ref>
- </see-also>
- </application>
- <application name="Proceeding" language="en_US">
- <synopsis>
- Indicate proceeding.
- </synopsis>
- <syntax />
- <description>
- <para>This application will request that a proceeding message be provided to the calling channel.</para>
- </description>
- </application>
- <application name="Progress" language="en_US">
- <synopsis>
- Indicate progress.
- </synopsis>
- <syntax />
- <description>
- <para>This application will request that in-band progress information be provided to the calling channel.</para>
- </description>
- <see-also>
- <ref type="application">Busy</ref>
- <ref type="application">Congestion</ref>
- <ref type="application">Ringing</ref>
- <ref type="application">Playtones</ref>
- </see-also>
- </application>
- <application name="RaiseException" language="en_US">
- <synopsis>
- Handle an exceptional condition.
- </synopsis>
- <syntax>
- <parameter name="reason" required="true" />
- </syntax>
- <description>
- <para>This application will jump to the <literal>e</literal> extension in the current context, setting the
- dialplan function EXCEPTION(). If the <literal>e</literal> extension does not exist, the call will hangup.</para>
- </description>
- <see-also>
- <ref type="function">Exception</ref>
- </see-also>
- </application>
- <application name="Ringing" language="en_US">
- <synopsis>
- Indicate ringing tone.
- </synopsis>
- <syntax />
- <description>
- <para>This application will request that the channel indicate a ringing tone to the user.</para>
- </description>
- <see-also>
- <ref type="application">Busy</ref>
- <ref type="application">Congestion</ref>
- <ref type="application">Progress</ref>
- <ref type="application">Playtones</ref>
- </see-also>
- </application>
- <application name="SayAlpha" language="en_US">
- <synopsis>
- Say Alpha.
- </synopsis>
- <syntax>
- <parameter name="string" required="true" />
- </syntax>
- <description>
- <para>This application will play the sounds that correspond to the letters
- of the given <replaceable>string</replaceable>. If the channel variable
- <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive),
- then this application will react to DTMF in the same way as
- <literal>Background</literal>.</para>
- </description>
- <see-also>
- <ref type="application">SayDigits</ref>
- <ref type="application">SayNumber</ref>
- <ref type="application">SayPhonetic</ref>
- <ref type="function">CHANNEL</ref>
- </see-also>
- </application>
- <application name="SayAlphaCase" language="en_US">
- <synopsis>
- Say Alpha.
- </synopsis>
- <syntax>
- <parameter name="casetype" required="true" >
- <enumlist>
- <enum name="a">
- <para>Case sensitive (all) pronunciation.
- (Ex: SayAlphaCase(a,aBc); - lowercase a uppercase b lowercase c).</para>
- </enum>
- <enum name="l">
- <para>Case sensitive (lower) pronunciation.
- (Ex: SayAlphaCase(l,aBc); - lowercase a b lowercase c).</para>
- </enum>
- <enum name="n">
- <para>Case insensitive pronunciation. Equivalent to SayAlpha.
- (Ex: SayAlphaCase(n,aBc) - a b c).</para>
- </enum>
- <enum name="u">
- <para>Case sensitive (upper) pronunciation.
- (Ex: SayAlphaCase(u,aBc); - a uppercase b c).</para>
- </enum>
- </enumlist>
- </parameter>
- <parameter name="string" required="true" />
- </syntax>
- <description>
- <para>This application will play the sounds that correspond to the letters of the
- given <replaceable>string</replaceable>. Optionally, a <replaceable>casetype</replaceable> may be
- specified. This will be used for case-insensitive or case-sensitive pronunciations. If the channel
- variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
- application will react to DTMF in the same way as <literal>Background</literal>.</para>
- </description>
- <see-also>
- <ref type="application">SayDigits</ref>
- <ref type="application">SayNumber</ref>
- <ref type="application">SayPhonetic</ref>
- <ref type="application">SayAlpha</ref>
- <ref type="function">CHANNEL</ref>
- </see-also>
- </application>
- <application name="SayDigits" language="en_US">
- <synopsis>
- Say Digits.
- </synopsis>
- <syntax>
- <parameter name="digits" required="true" />
- </syntax>
- <description>
- <para>This application will play the sounds that correspond to the digits of
- the given number. This will use the language that is currently set for the channel.
- If the channel variable <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true'
- (case insensitive), then this application will react to DTMF in the same way as
- <literal>Background</literal>.</para>
- </description>
- <see-also>
- <ref type="application">SayAlpha</ref>
- <ref type="application">SayNumber</ref>
- <ref type="application">SayPhonetic</ref>
- <ref type="function">CHANNEL</ref>
- </see-also>
- </application>
- <application name="SayNumber" language="en_US">
- <synopsis>
- Say Number.
- </synopsis>
- <syntax>
- <parameter name="digits" required="true" />
- <parameter name="gender" />
- </syntax>
- <description>
- <para>This application will play the sounds that correspond to the given
- <replaceable>digits</replaceable>. Optionally, a <replaceable>gender</replaceable> may be
- specified. This will use the language that is currently set for the channel. See the CHANNEL()
- function for more information on setting the language for the channel. If the channel variable
- <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
- application will react to DTMF in the same way as <literal>Background</literal>.</para>
- </description>
- <see-also>
- <ref type="application">SayAlpha</ref>
- <ref type="application">SayDigits</ref>
- <ref type="application">SayPhonetic</ref>
- <ref type="function">CHANNEL</ref>
- </see-also>
- </application>
- <application name="SayPhonetic" language="en_US">
- <synopsis>
- Say Phonetic.
- </synopsis>
- <syntax>
- <parameter name="string" required="true" />
- </syntax>
- <description>
- <para>This application will play the sounds from the phonetic alphabet that correspond to the
- letters in the given <replaceable>string</replaceable>. If the channel variable
- <variable>SAY_DTMF_INTERRUPT</variable> is set to 'true' (case insensitive), then this
- application will react to DTMF in the same way as <literal>Background</literal>.</para>
- </description>
- <see-also>
- <ref type="application">SayAlpha</ref>
- <ref type="application">SayDigits</ref>
- <ref type="application">SayNumber</ref>
- </see-also>
- </application>
- <application name="Set" language="en_US">
- <synopsis>
- Set channel variable or function value.
- </synopsis>
- <syntax argsep="=">
- <parameter name="name" required="true" />
- <parameter name="value" required="true" />
- </syntax>
- <description>
- <para>This function can be used to set the value of channel variables or dialplan functions.
- When setting variables, if the variable name is prefixed with <literal>_</literal>,
- the variable will be inherited into channels created from the current channel.
- If the variable name is prefixed with <literal>__</literal>, the variable will be
- inherited into channels created from the current channel and all children channels.</para>
- <note><para>If (and only if), in <filename>/etc/asterisk/asterisk.conf</filename>, you have
- a <literal>[compat]</literal> category, and you have <literal>app_set = 1.4</literal> under that, then
- the behavior of this app changes, and strips surrounding quotes from the right hand side as
- it did previously in 1.4.
- The advantages of not stripping out quoting, and not caring about the separator characters (comma and vertical bar)
- were sufficient to make these changes in 1.6. Confusion about how many backslashes would be needed to properly
- protect separators and quotes in various database access strings has been greatly
- reduced by these changes.</para></note>
- </description>
- <see-also>
- <ref type="application">MSet</ref>
- <ref type="function">GLOBAL</ref>
- <ref type="function">SET</ref>
- <ref type="function">ENV</ref>
- </see-also>
- </application>
- <application name="MSet" language="en_US">
- <synopsis>
- Set channel variable(s) or function value(s).
- </synopsis>
- <syntax>
- <parameter name="set1" required="true" argsep="=">
- <argument name="name1" required="true" />
- <argument name="value1" required="true" />
- </parameter>
- <parameter name="set2" multiple="true" argsep="=">
- <argument name="name2" required="true" />
- <argument name="value2" required="true" />
- </parameter>
- </syntax>
- <description>
- <para>This function can be used to set the value of channel variables or dialplan functions.
- When setting variables, if the variable name is prefixed with <literal>_</literal>,
- the variable will be inherited into channels created from the current channel
- If the variable name is prefixed with <literal>__</literal>, the variable will be
- inherited into channels created from the current channel and all children channels.
- MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus
- prone to doing things that you may not expect. For example, it strips surrounding
- double-quotes from the right-hand side (value). If you need to put a separator
- character (comma or vert-bar), you will need to escape them by inserting a backslash
- before them. Avoid its use if possible.</para>
- </description>
- <see-also>
- <ref type="application">Set</ref>
- </see-also>
- </application>
- <application name="SetAMAFlags" language="en_US">
- <synopsis>
- Set the AMA Flags.
- </synopsis>
- <syntax>
- <parameter name="flag" />
- </syntax>
- <description>
- <para>This application will set the channel's AMA Flags for billing purposes.</para>
- <warning><para>This application is deprecated. Please use the CHANNEL function instead.</para></warning>
- </description>
- <see-also>
- <ref type="function">CDR</ref>
- <ref type="function">CHANNEL</ref>
- </see-also>
- </application>
- <application name="Wait" language="en_US">
- <synopsis>
- Waits for some time.
- </synopsis>
- <syntax>
- <parameter name="seconds" required="true">
- <para>Can be passed with fractions of a second. For example, <literal>1.5</literal> will ask the
- application to wait for 1.5 seconds.</para>
- </parameter>
- </syntax>
- <description>
- <para>This application waits for a specified number of <replaceable>seconds</replaceable>.</para>
- </description>
- </application>
- <application name="WaitExten" language="en_US">
- <synopsis>
- Waits for an extension to be entered.
- </synopsis>
- <syntax>
- <parameter name="seconds">
- <para>Can be passed with fractions of a second. For example, <literal>1.5</literal> will ask the
- application to wait for 1.5 seconds.</para>
- </parameter>
- <parameter name="options">
- <optionlist>
- <option name="m">
- <para>Provide music on hold to the caller while waiting for an extension.</para>
- <argument name="x">
- <para>Specify the class for music on hold. <emphasis>CHANNEL(musicclass) will
- be used instead if set</emphasis></para>
- </argument>
- </option>
- </optionlist>
- </parameter>
- </syntax>
- <description>
- <para>This application waits for the user to enter a new extension for a specified number
- of <replaceable>seconds</replaceable>.</para>
- <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
- </description>
- <see-also>
- <ref type="application">Background</ref>
- <ref type="function">TIMEOUT</ref>
- </see-also>
- </application>
<function name="EXCEPTION" language="en_US">
<synopsis>
Retrieve the details of the current dialplan exception.
#define SWITCH_DATA_LENGTH 256
-#define VAR_BUF_SIZE 4096
-
#define VAR_NORMAL 1
#define VAR_SOFTTRAN 2
#define VAR_HARDTRAN 3
-#define BACKGROUND_SKIP (1 << 0)
-#define BACKGROUND_NOANSWER (1 << 1)
-#define BACKGROUND_MATCHEXTEN (1 << 2)
-#define BACKGROUND_PLAYBACK (1 << 3)
-
-AST_APP_OPTIONS(background_opts, {
- AST_APP_OPTION('s', BACKGROUND_SKIP),
- AST_APP_OPTION('n', BACKGROUND_NOANSWER),
- AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
- AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
-});
-
-#define WAITEXTEN_MOH (1 << 0)
-#define WAITEXTEN_DIALTONE (1 << 1)
-
-AST_APP_OPTIONS(waitexten_opts, {
- AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
- AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
-});
-
struct ast_context;
struct ast_app;
AST_THREADSTORAGE(switch_data);
AST_THREADSTORAGE(extensionstate_buf);
-/*!
- * \brief A thread local indicating whether the current thread can run
- * 'dangerous' dialplan functions.
- */
-AST_THREADSTORAGE(thread_inhibit_escalations_tl);
-
-/*!
- * \brief Set to true (non-zero) to globally allow all dangerous dialplan
- * functions to run.
- */
-static int live_dangerously;
/*!
\brief ast_exten: An extension
priority.
*/
struct ast_exten {
- char *exten; /*!< Extension name */
+ char *exten; /*!< Clean Extension id */
+ char *name; /*!< Extension name (may include '-' eye candy) */
int matchcid; /*!< Match caller id ? */
const char *cidmatch; /*!< Caller id to match for this extension */
+ const char *cidmatch_display; /*!< Caller id to match (display version) */
int priority; /*!< Priority */
const char *label; /*!< Label */
struct ast_context *parent; /*!< The context this extension belongs to */
struct ast_hashtab *peer_table; /*!< Priorities list in hashtab form -- only on the head of the peer list */
struct ast_hashtab *peer_label_table; /*!< labeled priorities in the peers -- only on the head of the peer list */
const char *registrar; /*!< Registrar */
+ const char *registrar_file; /*!< File name used to register extension */
+ int registrar_line; /*!< Line number the extension was registered in text */
struct ast_exten *next; /*!< Extension with a greater ID */
char stuff[0];
};
-/*! \brief ast_include: include= support in extensions.conf */
-struct ast_include {
- const char *name;
- const char *rname; /*!< Context to include */
- const char *registrar; /*!< Registrar */
- int hastime; /*!< If time construct exists */
- struct ast_timing timing; /*!< time construct */
- struct ast_include *next; /*!< Link them together */
- char stuff[0];
-};
-
-/*! \brief ast_sw: Switch statement in extensions.conf */
-struct ast_sw {
- char *name;
- const char *registrar; /*!< Registrar */
- char *data; /*!< Data load */
- int eval;
- AST_LIST_ENTRY(ast_sw) list;
- char stuff[0];
-};
-
-/*! \brief ast_ignorepat: Ignore patterns in dial plan */
-struct ast_ignorepat {
- const char *registrar;
- struct ast_ignorepat *next;
- const char pattern[0];
-};
-
/*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
struct match_char
{
struct ast_exten *exten;
};
-/*! \brief ast_context: An extension context */
+/*! \brief ast_context: An extension context - must remain in sync with fake_context */
struct ast_context {
ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
struct ast_exten *root; /*!< The root of the list of extensions */
struct ast_hashtab *root_table; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */
struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */
struct ast_context *next; /*!< Link them together */
- struct ast_include *includes; /*!< Include other contexts */
- struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ struct ast_includes includes; /*!< Include other contexts */
+ struct ast_ignorepats ignorepats; /*!< Patterns for which to continue playing dialtone */
+ struct ast_sws alts; /*!< Alternative switches */
char *registrar; /*!< Registrar -- make sure you malloc this, as the registrar may have to survive module unloads */
int refcount; /*!< each module that would have created this context should inc/dec this as appropriate */
- AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ int autohints; /*!< Whether autohints support is enabled or not */
ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
char name[0]; /*!< Name of the context */
};
-/*! \brief ast_app: A registered application */
-struct ast_app {
- int (*execute)(struct ast_channel *chan, const char *data);
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(synopsis); /*!< Synopsis text for 'show applications' */
- AST_STRING_FIELD(description); /*!< Description (help text) for 'show application <name>' */
- AST_STRING_FIELD(syntax); /*!< Syntax text for 'core show applications' */
- AST_STRING_FIELD(arguments); /*!< Arguments description */
- AST_STRING_FIELD(seealso); /*!< See also */
- );
-#ifdef AST_XML_DOCS
- enum ast_doc_src docsrc; /*!< Where the documentation come from. */
-#endif
- AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
- struct ast_module *module; /*!< Module this app belongs to */
- char name[0]; /*!< Name of the application */
-};
-
/*! \brief ast_state_cb: An extension state notify register item */
struct ast_state_cb {
/*! Watcher ID returned when registered. */
char context_name[AST_MAX_CONTEXT];/*!< Context of destroyed hint extension. */
char exten_name[AST_MAX_EXTENSION];/*!< Extension of destroyed hint extension. */
+
+ AST_VECTOR(, char *) devices; /*!< Devices associated with the hint */
};
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(hint_change_message_type);
#define HINTDEVICE_DATA_LENGTH 16
AST_THREADSTORAGE(hintdevice_data);
char hintdevice[1];
};
+/*! \brief Container for autohint contexts */
+static struct ao2_container *autohints;
+
+/*!
+ * \brief Structure for dial plan autohints
+ */
+struct ast_autohint {
+ /*! \brief Name of the registrar */
+ char *registrar;
+ /*! \brief Name of the context */
+ char context[1];
+};
/*!
* \note Using the device for hash
*/
static int hintdevice_hash_cb(const void *obj, const int flags)
{
- const struct ast_hintdevice *ext = obj;
+ const struct ast_hintdevice *ext;
+ const char *key;
- return ast_str_case_hash(ext->hintdevice);
-}
-/*!
- * \note Devices on hints are not unique so no CMP_STOP is returned
- * Dont use ao2_find against hintdevices container cause there always
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ ext = obj;
+ key = ext->hintdevice;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+
+ return ast_str_case_hash(key);
+}
+
+/*!
+ * \note Devices on hints are not unique so no CMP_STOP is returned
+ * Dont use ao2_find against hintdevices container cause there always
* could be more than one result.
*/
static int hintdevice_cmp_multiple(void *obj, void *arg, int flags)
{
- struct ast_hintdevice *ext = obj, *ext2 = arg;
+ struct ast_hintdevice *left = obj;
+ struct ast_hintdevice *right = arg;
+ const char *right_key = arg;
+ int cmp;
- return !strcasecmp(ext->hintdevice, ext2->hintdevice) ? CMP_MATCH : 0;
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = right->hintdevice;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcasecmp(left->hintdevice, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /*
+ * We could also use a partial key struct containing a length
+ * so strlen() does not get called for every comparison instead.
+ */
+ cmp = strncmp(left->hintdevice, right_key, strlen(right_key));
+ break;
+ default:
+ ast_assert(0);
+ cmp = 0;
+ break;
+ }
+ return cmp ? 0 : CMP_MATCH;
}
-/*
- * \details This is used with ao2_callback to remove old devices
- * when they are linked to the hint
-*/
-static int hintdevice_remove_cb(void *deviceobj, void *arg, int flags)
+/*!
+ * \note Using the context name for hash
+ */
+static int autohint_hash_cb(const void *obj, const int flags)
+{
+ const struct ast_autohint *autohint;
+ const char *key;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_KEY:
+ key = obj;
+ break;
+ case OBJ_SEARCH_OBJECT:
+ autohint = obj;
+ key = autohint->context;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+
+ return ast_str_case_hash(key);
+}
+
+static int autohint_cmp(void *obj, void *arg, int flags)
+{
+ struct ast_autohint *left = obj;
+ struct ast_autohint *right = arg;
+ const char *right_key = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = right->context;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcasecmp(left->context, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ /*
+ * We could also use a partial key struct containing a length
+ * so strlen() does not get called for every comparison instead.
+ */
+ cmp = strncmp(left->context, right_key, strlen(right_key));
+ break;
+ default:
+ ast_assert(0);
+ cmp = 0;
+ break;
+ }
+ return cmp ? 0 : CMP_MATCH | CMP_STOP;
+}
+
+/*! \internal \brief \c ao2_callback function to remove hintdevices */
+static int hintdevice_remove_cb(void *obj, void *arg, void *data, int flags)
{
- struct ast_hintdevice *device = deviceobj;
- struct ast_hint *hint = arg;
+ struct ast_hintdevice *candidate = obj;
+ char *device = arg;
+ struct ast_hint *hint = data;
- return (device->hint == hint) ? CMP_MATCH : 0;
+ if (!strcasecmp(candidate->hintdevice, device)
+ && candidate->hint == hint) {
+ return CMP_MATCH;
+ }
+ return 0;
}
static int remove_hintdevice(struct ast_hint *hint)
{
- /* iterate through all devices and remove the devices which are linked to this hint */
- ao2_t_callback(hintdevices, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
- hintdevice_remove_cb, hint,
- "callback to remove all devices which are linked to a hint");
+ while (AST_VECTOR_SIZE(&hint->devices) > 0) {
+ char *device = AST_VECTOR_GET(&hint->devices, 0);
+
+ ao2_t_callback_data(hintdevices, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA,
+ hintdevice_remove_cb, device, hint, "Remove device from container");
+ AST_VECTOR_REMOVE_UNORDERED(&hint->devices, 0);
+ ast_free(device);
+ }
+
return 0;
}
/* Spit on '&' and ',' to handle presence hints as well */
while ((cur = strsep(&parse, "&,"))) {
+ char *device_name;
+
devicelength = strlen(cur);
if (!devicelength) {
continue;
}
+
+ device_name = ast_strdup(cur);
+ if (!device_name) {
+ return -1;
+ }
+
device = ao2_t_alloc(sizeof(*device) + devicelength, hintdevice_destroy,
"allocating a hintdevice structure");
if (!device) {
+ ast_free(device_name);
return -1;
}
strcpy(device->hintdevice, cur);
ao2_ref(hint, +1);
device->hint = hint;
+ if (AST_VECTOR_APPEND(&hint->devices, device_name)) {
+ ast_free(device_name);
+ ao2_ref(device, -1);
+ return -1;
+ }
ao2_t_link(hintdevices, device, "Linking device into hintdevice container.");
ao2_t_ref(device, -1, "hintdevice is linked so we can unref");
}
int priority; /*!< Priority associated with this exception */
};
-static int pbx_builtin_answer(struct ast_channel *, const char *);
-static int pbx_builtin_goto(struct ast_channel *, const char *);
-static int pbx_builtin_hangup(struct ast_channel *, const char *);
-static int pbx_builtin_background(struct ast_channel *, const char *);
-static int pbx_builtin_wait(struct ast_channel *, const char *);
-static int pbx_builtin_waitexten(struct ast_channel *, const char *);
-static int pbx_builtin_incomplete(struct ast_channel *, const char *);
-static int pbx_builtin_setamaflags(struct ast_channel *, const char *);
-static int pbx_builtin_ringing(struct ast_channel *, const char *);
-static int pbx_builtin_proceeding(struct ast_channel *, const char *);
-static int pbx_builtin_progress(struct ast_channel *, const char *);
-static int pbx_builtin_congestion(struct ast_channel *, const char *);
-static int pbx_builtin_busy(struct ast_channel *, const char *);
-static int pbx_builtin_noop(struct ast_channel *, const char *);
-static int pbx_builtin_gotoif(struct ast_channel *, const char *);
-static int pbx_builtin_gotoiftime(struct ast_channel *, const char *);
-static int pbx_builtin_execiftime(struct ast_channel *, const char *);
-static int pbx_builtin_saynumber(struct ast_channel *, const char *);
-static int pbx_builtin_saydigits(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters(struct ast_channel *, const char *);
-static int pbx_builtin_saycharacters_case(struct ast_channel *, const char *);
-static int pbx_builtin_sayphonetic(struct ast_channel *, const char *);
static int matchcid(const char *cidpattern, const char *callerid);
#ifdef NEED_DEBUG
static void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
#endif
-static int pbx_builtin_importvar(struct ast_channel *, const char *);
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
static void new_find_extension(const char *str, struct scoreboard *score,
struct match_char *tree, int length, int spec, const char *callerid,
const char *label, enum ext_match_t action);
static int ast_add_extension2_lockopt(struct ast_context *con,
int replace, const char *extension, int priority, const char *label, const char *callerid,
const char *application, void *data, void (*datad)(void *),
- const char *registrar, int lock_context);
+ const char *registrar, const char *registrar_file, int registrar_line,
+ int lock_context);
static struct ast_context *find_context_locked(const char *context);
static struct ast_context *find_context(const char *context);
static void get_device_state_causing_channels(struct ao2_container *c);
+static unsigned int ext_strncpy(char *dst, const char *src, size_t dst_size, int nofluff);
/*!
* \internal
return ast_hashtab_hash_string(S_OR(ac->label, ""));
}
-
-AST_RWLOCK_DEFINE_STATIC(globalslock);
-static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
-
static int autofallthrough = 1;
static int extenpatternmatchnew = 0;
static char *overrideswitch = NULL;
static int countcalls;
static int totalcalls;
-/*!
- * \brief Registered functions container.
- *
- * It is sorted by function name.
- */
-static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
-
-/*! \brief Declaration of builtin applications */
-static struct pbx_builtin {
- char name[AST_MAX_APP];
- int (*execute)(struct ast_channel *chan, const char *data);
-} builtins[] =
-{
- /* These applications are built into the PBX core and do not
- need separate modules */
-
- { "Answer", pbx_builtin_answer },
- { "BackGround", pbx_builtin_background },
- { "Busy", pbx_builtin_busy },
- { "Congestion", pbx_builtin_congestion },
- { "ExecIfTime", pbx_builtin_execiftime },
- { "Goto", pbx_builtin_goto },
- { "GotoIf", pbx_builtin_gotoif },
- { "GotoIfTime", pbx_builtin_gotoiftime },
- { "ImportVar", pbx_builtin_importvar },
- { "Hangup", pbx_builtin_hangup },
- { "Incomplete", pbx_builtin_incomplete },
- { "NoOp", pbx_builtin_noop },
- { "Proceeding", pbx_builtin_proceeding },
- { "Progress", pbx_builtin_progress },
- { "RaiseException", pbx_builtin_raise_exception },
- { "Ringing", pbx_builtin_ringing },
- { "SayAlpha", pbx_builtin_saycharacters },
- { "SayAlphaCase", pbx_builtin_saycharacters_case },
- { "SayDigits", pbx_builtin_saydigits },
- { "SayNumber", pbx_builtin_saynumber },
- { "SayPhonetic", pbx_builtin_sayphonetic },
- { "Set", pbx_builtin_setvar },
- { "MSet", pbx_builtin_setvar_multiple },
- { "SetAMAFlags", pbx_builtin_setamaflags },
- { "Wait", pbx_builtin_wait },
- { "WaitExten", pbx_builtin_waitexten }
-};
-
static struct ast_context *contexts;
static struct ast_hashtab *contexts_table = NULL;
*/
AST_MUTEX_DEFINE_STATIC(context_merge_lock);
-/*!
- * \brief Registered applications container.
- *
- * It is sorted by application name.
- */
-static AST_RWLIST_HEAD_STATIC(apps, ast_app);
-
-static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
-
static int stateid = 1;
/*!
* \note When holding this container's lock, do _not_ do
e2 = ast_hashtab_lookup(c1->root_table, &ex);
if (!e2) {
if (e1->matchcid == AST_EXT_MATCHCID_ON) {
- ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s (CID match: %s) but it is not in its root_table\n", file, line, c2->name, dummy_name, e1->cidmatch );
+ ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+ "the exten %s (CID match: %s) but it is not in its root_table\n",
+ file, line, c2->name, dummy_name, e1->cidmatch_display);
} else {
- ast_log(LOG_NOTICE,"Called from: %s:%d: The %s context records the exten %s but it is not in its root_table\n", file, line, c2->name, dummy_name );
+ ast_log(LOG_NOTICE, "Called from: %s:%d: The %s context records "
+ "the exten %s but it is not in its root_table\n",
+ file, line, c2->name, dummy_name);
}
check_contexts_trouble();
}
}
#endif
-/*
- \note This function is special. It saves the stack so that no matter
- how many times it is called, it returns to the same place */
-int pbx_exec(struct ast_channel *c, /*!< Channel */
- struct ast_app *app, /*!< Application */
- const char *data) /*!< Data for execution */
-{
- int res;
- struct ast_module_user *u = NULL;
- const char *saved_c_appl;
- const char *saved_c_data;
-
- /* save channel values */
- saved_c_appl= ast_channel_appl(c);
- saved_c_data= ast_channel_data(c);
-
- ast_channel_lock(c);
- ast_channel_appl_set(c, app->name);
- ast_channel_data_set(c, data);
- ast_channel_publish_snapshot(c);
- ast_channel_unlock(c);
-
- if (app->module)
- u = __ast_module_user_add(app->module, c);
- res = app->execute(c, S_OR(data, ""));
- if (app->module && u)
- __ast_module_user_remove(app->module, u);
- /* restore channel values */
- ast_channel_appl_set(c, saved_c_appl);
- ast_channel_data_set(c, saved_c_data);
- return res;
-}
-
-static struct ast_app *pbx_findapp_nolock(const char *name)
-{
- struct ast_app *cur;
- int cmp;
-
- AST_RWLIST_TRAVERSE(&apps, cur, list) {
- cmp = strcasecmp(name, cur->name);
- if (cmp > 0) {
- continue;
- }
- if (!cmp) {
- /* Found it. */
- break;
- }
- /* Not in container. */
- cur = NULL;
- break;
- }
-
- return cur;
-}
-
-struct ast_app *pbx_findapp(const char *app)
-{
- struct ast_app *ret;
-
- AST_RWLIST_RDLOCK(&apps);
- ret = pbx_findapp_nolock(app);
- AST_RWLIST_UNLOCK(&apps);
-
- return ret;
-}
-
-static struct ast_switch *pbx_findswitch(const char *sw)
-{
- struct ast_switch *asw;
-
- AST_RWLIST_RDLOCK(&switches);
- AST_RWLIST_TRAVERSE(&switches, asw, list) {
- if (!strcasecmp(asw->name, sw))
- break;
- }
- AST_RWLIST_UNLOCK(&switches);
-
- return asw;
-}
-
-static inline int include_valid(struct ast_include *i)
-{
- if (!i->hastime)
- return 1;
-
- return ast_check_timing(&(i->timing));
-}
-
static void pbx_destroy(struct ast_pbx *p)
{
ast_free(p);
if (strlen(node->x) > 1) {
ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
- node->exten ? node->exten->exten : "", extenstr);
+ node->exten ? node->exten->name : "", extenstr);
} else {
ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y' : 'N',
node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:" : "",
- node->exten ? node->exten->exten : "", extenstr);
+ node->exten ? node->exten->name : "", extenstr);
}
ast_str_set(&my_prefix, 0, "%s+ ", prefix);
return; \
} \
} else { \
- ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->exten); \
+ ast_debug(4, "returning an exact match-- first found-- %s\n", p->exten->name); \
return; /* the first match, by definition, will be the best, because of the sorted tree */ \
} \
} \
if (*(str + 1) || p->next_char->x[0] == '!') { \
new_find_extension(str + 1, score, p->next_char, length + 1, spec + p->specificity, callerid, label, action); \
if (score->exten) { \
- ast_debug(4 ,"returning an exact match-- %s\n", score->exten->exten); \
+ ast_debug(4 ,"returning an exact match-- %s\n", score->exten->name); \
return; /* the first match is all we need */ \
} \
} else { \
new_find_extension("/", score, p->next_char, length + 1, spec + p->specificity, callerid, label, action); \
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) { \
- ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->exten : \
+ ast_debug(4,"returning a (can/more) match--- %s\n", score->exten ? score->exten->name : \
"NULL"); \
return; /* the first match is all we need */ \
} \
if (p->exten && *str2 != '/') {
update_scoreboard(score, length + i, spec + (i * p->specificity), p->exten, '.', callerid, p->deleted, p);
if (score->exten) {
- ast_debug(4,"return because scoreboard has a match with '/'--- %s\n", score->exten->exten);
+ ast_debug(4, "return because scoreboard has a match with '/'--- %s\n",
+ score->exten->name);
return; /* the first match is all we need */
}
}
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length + i, spec+(p->specificity*i), callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
if (p->exten && *str2 != '/') {
update_scoreboard(score, length + 1, spec + (p->specificity * i), p->exten, '!', callerid, p->deleted, p);
if (score->exten) {
- ast_debug(4, "return because scoreboard has a '!' match--- %s\n", score->exten->exten);
+ ast_debug(4, "return because scoreboard has a '!' match--- %s\n",
+ score->exten->name);
return; /* the first match is all we need */
}
}
if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
new_find_extension("/", score, p->next_char, length + i, spec + (p->specificity * i), callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set with '/' and '!'--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
if (p->next_char && callerid && *callerid) {
new_find_extension(callerid, score, p->next_char, length + 1, spec, callerid, label, action);
if (score->exten || ((action == E_CANMATCH || action == E_MATCHMORE) && score->canmatch)) {
- ast_debug(4, "return because scoreboard has exact match OR CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n", score->exten ? score->exten->exten : "NULL");
+ ast_debug(4, "return because scoreboard has exact match OR "
+ "CANMATCH/MATCHMORE & canmatch set with '/'--- %s\n",
+ score->exten ? score->exten->name : "NULL");
return; /* the first match is all we need */
}
}
}
if (m2->exten) {
ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
- m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+ m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
}
m2->exten = e1;
m2->deleted = 0;
if (!pat_node[idx_next].buf[0]) {
if (m2 && m2->exten) {
ast_log(LOG_WARNING, "Found duplicate exten. Had %s found %s\n",
- m2->deleted ? "(deleted/invalid)" : m2->exten->exten, e1->exten);
+ m2->deleted ? "(deleted/invalid)" : m2->exten->name, e1->name);
}
m1->deleted = 0;
m1->exten = e1;
return ext_cmp_pattern(left + 1, right + 1);
}
+static int ext_fluff_count(const char *exten)
+{
+ int fluff = 0;
+
+ if (*exten != '_') {
+ /* not a pattern, simple check. */
+ while (*exten) {
+ if (*exten == '-') {
+ fluff++;
+ }
+ exten++;
+ }
+
+ return fluff;
+ }
+
+ /* do pattern check */
+ while (*exten) {
+ if (*exten == '-') {
+ fluff++;
+ } else if (*exten == '[') {
+ /* skip set, dashes here matter. */
+ exten = strchr(exten, ']');
+
+ if (!exten) {
+ /* we'll end up warning about this later, don't spam logs */
+ return fluff;
+ }
+ }
+ exten++;
+ }
+
+ return fluff;
+}
+
int ast_extension_cmp(const char *a, const char *b)
{
int cmp;
return extension_match_core(pattern, data, needmore);
}
+/* This structure must remain in sync with ast_context for proper hashtab matching */
struct fake_context /* this struct is purely for matching in the hashtab */
{
ast_rwlock_t lock;
struct ast_hashtab *root_table;
struct match_char *pattern_tree;
struct ast_context *next;
- struct ast_include *includes;
- struct ast_ignorepat *ignorepats;
+ struct ast_includes includes;
+ struct ast_ignorepats ignorepats;
+ struct ast_sws alts;
const char *registrar;
int refcount;
- AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
+ int autohints;
ast_mutex_t macrolock;
char name[256];
};
int x, res;
struct ast_context *tmp = NULL;
struct ast_exten *e = NULL, *eroot = NULL;
- struct ast_include *i = NULL;
- struct ast_sw *sw = NULL;
struct ast_exten pattern = {NULL, };
struct scoreboard score = {0, };
struct ast_str *tmpdata = NULL;
+ int idx;
pattern.label = label;
pattern.priority = priority;
}
/* Check alternative switches */
- AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
- struct ast_switch *asw = pbx_findswitch(sw->name);
+ for (idx = 0; idx < ast_context_switches_count(tmp); idx++) {
+ const struct ast_sw *sw = ast_context_switches_get(tmp, idx);
+ struct ast_switch *asw = pbx_findswitch(ast_get_switch_name(sw));
ast_switch_f *aswf = NULL;
- char *datap;
+ const char *datap;
if (!asw) {
- ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ ast_log(LOG_WARNING, "No such switch '%s'\n", ast_get_switch_name(sw));
continue;
}
/* Substitute variables now */
- if (sw->eval) {
+ if (ast_get_switch_eval(sw)) {
if (!(tmpdata = ast_str_thread_get(&switch_data, 512))) {
ast_log(LOG_WARNING, "Can't evaluate switch?!\n");
continue;
}
- pbx_substitute_variables_helper(chan, sw->data, ast_str_buffer(tmpdata), ast_str_size(tmpdata));
+ pbx_substitute_variables_helper(chan, ast_get_switch_data(sw),
+ ast_str_buffer(tmpdata), ast_str_size(tmpdata));
+ datap = ast_str_buffer(tmpdata);
+ } else {
+ datap = ast_get_switch_data(sw);
}
/* equivalent of extension_match_core() at the switch level */
aswf = asw->matchmore;
else /* action == E_MATCH */
aswf = asw->exists;
- datap = sw->eval ? ast_str_buffer(tmpdata) : sw->data;
if (!aswf)
res = 0;
else {
}
q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
/* Now try any includes we have in this context */
- for (i = tmp->includes; i; i = i->next) {
+ for (idx = 0; idx < ast_context_includes_count(tmp); idx++) {
+ const struct ast_include *i = ast_context_includes_get(tmp, idx);
+
if (include_valid(i)) {
- if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
+ if ((e = pbx_find_extension(chan, bypass, q, include_rname(i), exten, priority, label, callerid, action))) {
#ifdef NEED_DEBUG_HERE
ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
#endif
return NULL;
}
-/*!
- * \brief extract offset:length from variable name.
- * \return 1 if there is a offset:length part, which is
- * trimmed off (values go into variables)
- */
-static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
-{
- int parens = 0;
-
- *offset = 0;
- *length = INT_MAX;
- *isfunc = 0;
- for (; *var; var++) {
- if (*var == '(') {
- (*isfunc)++;
- parens++;
- } else if (*var == ')') {
- parens--;
- } else if (*var == ':' && parens == 0) {
- *var++ = '\0';
- sscanf(var, "%30d:%30d", offset, length);
- return 1; /* offset:length valid */
- }
- }
- return 0;
-}
-
-/*!
- *\brief takes a substring. It is ok to call with value == workspace.
- * \param value
- * \param offset < 0 means start from the end of the string and set the beginning
- * to be that many characters back.
- * \param length is the length of the substring, a value less than 0 means to leave
- * that many off the end.
- * \param workspace
- * \param workspace_len
- * Always return a copy in workspace.
- */
-static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
-{
- char *ret = workspace;
- int lr; /* length of the input string after the copy */
-
- ast_copy_string(workspace, value, workspace_len); /* always make a copy */
-
- lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
-
- /* Quick check if no need to do anything */
- if (offset == 0 && length >= lr) /* take the whole string */
- return ret;
-
- if (offset < 0) { /* translate negative offset into positive ones */
- offset = lr + offset;
- if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
- offset = 0;
- }
-
- /* too large offset result in empty string so we know what to return */
- if (offset >= lr)
- return ret + lr; /* the final '\0' */
-
- ret += offset; /* move to the start position */
- if (length >= 0 && length < lr - offset) /* truncate if necessary */
- ret[length] = '\0';
- else if (length < 0) {
- if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
- ret[lr + length - offset] = '\0';
- else
- ret[0] = '\0';
- }
-
- return ret;
-}
-
-static const char *ast_str_substring(struct ast_str *value, int offset, int length)
-{
- int lr; /* length of the input string after the copy */
-
- lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
-
- /* Quick check if no need to do anything */
- if (offset == 0 && length >= lr) /* take the whole string */
- return ast_str_buffer(value);
-
- if (offset < 0) { /* translate negative offset into positive ones */
- offset = lr + offset;
- if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
- offset = 0;
- }
-
- /* too large offset result in empty string so we know what to return */
- if (offset >= lr) {
- ast_str_reset(value);
- return ast_str_buffer(value);
- }
-
- if (offset > 0) {
- /* Go ahead and chop off the beginning */
- memmove(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset + 1);
- lr -= offset;
- }
-
- if (length >= 0 && length < lr) { /* truncate if necessary */
- char *tmp = ast_str_buffer(value);
- tmp[length] = '\0';
- ast_str_update(value);
- } else if (length < 0) {
- if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
- char *tmp = ast_str_buffer(value);
- tmp[lr + length] = '\0';
- ast_str_update(value);
- } else {
- ast_str_reset(value);
- }
- } else {
- /* Nothing to do, but update the buffer length */
- ast_str_update(value);
- }
-
- return ast_str_buffer(value);
-}
-
-/*! \brief Support for Asterisk built-in variables in the dialplan
-
-\note See also
- - \ref AstVar Channel variables
- - \ref AstCauses The HANGUPCAUSE variable
- */
-void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
-{
- struct ast_str *str = ast_str_create(16);
- const char *cret;
-
- cret = ast_str_retrieve_variable(&str, 0, c, headp, var);
- ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
- *ret = cret ? workspace : NULL;
- ast_free(str);
-}
-
-const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
-{
- const char not_found = '\0';
- char *tmpvar;
- const char *ret;
- const char *s; /* the result */
- int offset, length;
- int i, need_substring;
- struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
- char workspace[20];
-
- if (c) {
- ast_channel_lock(c);
- places[0] = ast_channel_varshead(c);
- }
- /*
- * Make a copy of var because parse_variable_name() modifies the string.
- * Then if called directly, we might need to run substring() on the result;
- * remember this for later in 'need_substring', 'offset' and 'length'
- */
- tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
- need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
-
- /*
- * Look first into predefined variables, then into variable lists.
- * Variable 's' points to the result, according to the following rules:
- * s == ¬_found (set at the beginning) means that we did not find a
- * matching variable and need to look into more places.
- * If s != ¬_found, s is a valid result string as follows:
- * s = NULL if the variable does not have a value;
- * you typically do this when looking for an unset predefined variable.
- * s = workspace if the result has been assembled there;
- * typically done when the result is built e.g. with an snprintf(),
- * so we don't need to do an additional copy.
- * s != workspace in case we have a string, that needs to be copied
- * (the ast_copy_string is done once for all at the end).
- * Typically done when the result is already available in some string.
- */
- s = ¬_found; /* default value */
- if (c) { /* This group requires a valid channel */
- /* Names with common parts are looked up a piece at a time using strncmp. */
- if (!strncmp(var, "CALL", 4)) {
- if (!strncmp(var + 4, "ING", 3)) {
- if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
- ast_str_set(str, maxlen, "%d",
- ast_party_id_presentation(&ast_channel_caller(c)->id));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
- ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->ani2);
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
- ast_str_set(str, maxlen, "%d", ast_channel_caller(c)->id.number.plan);
- s = ast_str_buffer(*str);
- } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
- ast_str_set(str, maxlen, "%d", ast_channel_dialed(c)->transit_network_select);
- s = ast_str_buffer(*str);
- }
- }
- } else if (!strcmp(var, "HINT")) {
- s = ast_str_get_hint(str, maxlen, NULL, 0, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
- } else if (!strcmp(var, "HINTNAME")) {
- s = ast_str_get_hint(NULL, 0, str, maxlen, c, ast_channel_context(c), ast_channel_exten(c)) ? ast_str_buffer(*str) : NULL;
- } else if (!strcmp(var, "EXTEN")) {
- s = ast_channel_exten(c);
- } else if (!strcmp(var, "CONTEXT")) {
- s = ast_channel_context(c);
- } else if (!strcmp(var, "PRIORITY")) {
- ast_str_set(str, maxlen, "%d", ast_channel_priority(c));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var, "CHANNEL")) {
- s = ast_channel_name(c);
- } else if (!strcmp(var, "UNIQUEID")) {
- s = ast_channel_uniqueid(c);
- } else if (!strcmp(var, "HANGUPCAUSE")) {
- ast_str_set(str, maxlen, "%d", ast_channel_hangupcause(c));
- s = ast_str_buffer(*str);
- }
- }
- if (s == ¬_found) { /* look for more */
- if (!strcmp(var, "EPOCH")) {
- ast_str_set(str, maxlen, "%d", (int) time(NULL));
- s = ast_str_buffer(*str);
- } else if (!strcmp(var, "SYSTEMNAME")) {
- s = ast_config_AST_SYSTEM_NAME;
- } else if (!strcmp(var, "ASTETCDIR")) {
- s = ast_config_AST_CONFIG_DIR;
- } else if (!strcmp(var, "ASTMODDIR")) {
- s = ast_config_AST_MODULE_DIR;
- } else if (!strcmp(var, "ASTVARLIBDIR")) {
- s = ast_config_AST_VAR_DIR;
- } else if (!strcmp(var, "ASTDBDIR")) {
- s = ast_config_AST_DB;
- } else if (!strcmp(var, "ASTKEYDIR")) {
- s = ast_config_AST_KEY_DIR;
- } else if (!strcmp(var, "ASTDATADIR")) {
- s = ast_config_AST_DATA_DIR;
- } else if (!strcmp(var, "ASTAGIDIR")) {
- s = ast_config_AST_AGI_DIR;
- } else if (!strcmp(var, "ASTSPOOLDIR")) {
- s = ast_config_AST_SPOOL_DIR;
- } else if (!strcmp(var, "ASTRUNDIR")) {
- s = ast_config_AST_RUN_DIR;
- } else if (!strcmp(var, "ASTLOGDIR")) {
- s = ast_config_AST_LOG_DIR;
- } else if (!strcmp(var, "ENTITYID")) {
- ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
- s = workspace;
- }
- }
- /* if not found, look into chanvars or global vars */
- for (i = 0; s == ¬_found && i < ARRAY_LEN(places); i++) {
- struct ast_var_t *variables;
- if (!places[i])
- continue;
- if (places[i] == &globals)
- ast_rwlock_rdlock(&globalslock);
- AST_LIST_TRAVERSE(places[i], variables, entries) {
- if (!strcmp(ast_var_name(variables), var)) {
- s = ast_var_value(variables);
- break;
- }
- }
- if (places[i] == &globals)
- ast_rwlock_unlock(&globalslock);
- }
- if (s == ¬_found || s == NULL) {
- ast_debug(5, "Result of '%s' is NULL\n", var);
- ret = NULL;
- } else {
- ast_debug(5, "Result of '%s' is '%s'\n", var, s);
- if (s != ast_str_buffer(*str)) {
- ast_str_set(str, maxlen, "%s", s);
- }
- ret = ast_str_buffer(*str);
- if (need_substring) {
- ret = ast_str_substring(*str, offset, length);
- ast_debug(2, "Final result of '%s' is '%s'\n", var, ret);
- }
- }
-
- if (c) {
- ast_channel_unlock(c);
- }
- return ret;
-}
-
static void exception_store_free(void *data)
{
struct pbx_exception *exception = data;
* \retval 0 on success.
* \retval -1 on error.
*/
-static int raise_exception(struct ast_channel *chan, const char *reason, int priority)
+int raise_exception(struct ast_channel *chan, const char *reason, int priority)
{
struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
struct pbx_exception *exception = NULL;
return 0;
}
-int pbx_builtin_raise_exception(struct ast_channel *chan, const char *reason)
-{
- /* Priority will become 1, next time through the AUTOLOOP */
- return raise_exception(chan, reason, 0);
-}
-
static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
{
struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
.read = acf_exception_read,
};
-static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+/*!
+ * \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ * and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ * the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ *
+ * \note The channel is auto-serviced in this function, because doing an extension
+ * match may block for a long time. For example, if the lookup has to use a network
+ * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
+ * auto-service code will queue up any important signalling frames to be processed
+ * after this is done.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
{
- struct ast_custom_function *acf;
- int count_acf = 0;
- int like = 0;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show functions [like]";
- e->usage =
- "Usage: core show functions [like <text>]\n"
- " List builtin functions, optionally only those matching a given string\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
- like = 1;
- } else if (a->argc != 3) {
- return CLI_SHOWUSAGE;
- }
-
- ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
-
- AST_RWLIST_RDLOCK(&acf_root);
- AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
- if (!like || strstr(acf->name, a->argv[4])) {
- count_acf++;
- ast_cli(a->fd, "%-20.20s %-35.35s %s\n",
- S_OR(acf->name, ""),
- S_OR(acf->syntax, ""),
- S_OR(acf->synopsis, ""));
- }
- }
- AST_RWLIST_UNLOCK(&acf_root);
-
- ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+ struct ast_exten *e;
+ struct ast_app *app;
+ char *substitute = NULL;
+ int res;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+ char passdata[EXT_DATA_SIZE];
+ int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
- return CLI_SUCCESS;
-}
+ ast_rdlock_contexts();
+ if (found)
+ *found = 0;
-static char *complete_functions(const char *word, int pos, int state)
-{
- struct ast_custom_function *cur;
- char *ret = NULL;
- int which = 0;
- int wordlen;
- int cmp;
-
- if (pos != 3) {
- return NULL;
- }
-
- wordlen = strlen(word);
- AST_RWLIST_RDLOCK(&acf_root);
- AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
- /*
- * Do a case-insensitive search for convenience in this
- * 'complete' function.
- *
- * We must search the entire container because the functions are
- * sorted and normally found case sensitively.
- */
- cmp = strncasecmp(word, cur->name, wordlen);
- if (!cmp) {
- /* Found match. */
- if (++which <= state) {
- /* Not enough matches. */
- continue;
+ e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ if (found)
+ *found = 1;
+ if (matching_action) {
+ ast_unlock_contexts();
+ return -1; /* success, we found it */
+ } else if (action == E_FINDLABEL) { /* map the label to a priority */
+ res = e->priority;
+ ast_unlock_contexts();
+ return res; /* the priority we were looking for */
+ } else { /* spawn */
+ if (!e->cached_app)
+ e->cached_app = pbx_findapp(e->app);
+ app = e->cached_app;
+ if (ast_strlen_zero(e->data)) {
+ *passdata = '\0';
+ } else {
+ const char *tmp;
+ if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
+ /* no variables to substitute, copy on through */
+ ast_copy_string(passdata, e->data, sizeof(passdata));
+ } else {
+ /* save e->data on stack for later processing after lock released */
+ substitute = ast_strdupa(e->data);
+ }
+ }
+ ast_unlock_contexts();
+ if (!app) {
+ ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+ return -1;
+ }
+ if (ast_channel_context(c) != context)
+ ast_channel_context_set(c, context);
+ if (ast_channel_exten(c) != exten)
+ ast_channel_exten_set(c, exten);
+ ast_channel_priority_set(c, priority);
+ if (substitute) {
+ pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1);
+ }
+ ast_debug(1, "Launching '%s'\n", app_name(app));
+ if (VERBOSITY_ATLEAST(3)) {
+ ast_verb(3, "Executing [%s@%s:%d] " COLORIZE_FMT "(\"" COLORIZE_FMT "\", \"" COLORIZE_FMT "\") %s\n",
+ exten, context, priority,
+ COLORIZE(COLOR_BRCYAN, 0, app_name(app)),
+ COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(c)),
+ COLORIZE(COLOR_BRMAGENTA, 0, passdata),
+ "in new stack");
+ }
+ return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
+ }
+ } else if (q.swo) { /* not found here, but in another switch */
+ if (found)
+ *found = 1;
+ ast_unlock_contexts();
+ if (matching_action) {
+ return -1;
+ } else {
+ if (!q.swo->exec) {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+ res = -1;
}
- ret = ast_strdup(cur->name);
+ return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+ }
+ } else { /* not found anywhere, see what happened */
+ ast_unlock_contexts();
+ /* Using S_OR here because Solaris doesn't like NULL being passed to ast_log */
+ switch (q.status) {
+ case STATUS_NO_CONTEXT:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", S_OR(context, ""));
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, S_OR(context, ""));
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, S_OR(context, ""));
+ break;
+ case STATUS_NO_LABEL:
+ if (context && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, S_OR(context, ""));
break;
+ default:
+ ast_debug(1, "Shouldn't happen!\n");
}
+
+ return (matching_action) ? 0 : -1;
}
- AST_RWLIST_UNLOCK(&acf_root);
+}
- return ret;
+/*! \brief Find hint for given extension in context */
+static struct ast_exten *ast_hint_extension_nolock(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
+ return pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
}
-static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
{
- struct ast_custom_function *acf;
- /* Maximum number of characters added by terminal coloring is 22 */
- char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40], argtitle[40], seealsotitle[40];
- char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL, *seealso = NULL;
- char stxtitle[40], *syntax = NULL, *arguments = NULL;
- int syntax_size, description_size, synopsis_size, arguments_size, seealso_size;
+ struct ast_exten *e;
+ ast_rdlock_contexts();
+ e = ast_hint_extension_nolock(c, context, exten);
+ ast_unlock_contexts();
+ return e;
+}
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show function";
- e->usage =
- "Usage: core show function <function>\n"
- " Describe a particular dialplan function.\n";
- return NULL;
- case CLI_GENERATE:
- return complete_functions(a->word, a->pos, a->n);
+enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
+{
+ switch (devstate) {
+ case AST_DEVICE_ONHOLD:
+ return AST_EXTENSION_ONHOLD;
+ case AST_DEVICE_BUSY:
+ return AST_EXTENSION_BUSY;
+ case AST_DEVICE_UNKNOWN:
+ return AST_EXTENSION_NOT_INUSE;
+ case AST_DEVICE_UNAVAILABLE:
+ case AST_DEVICE_INVALID:
+ return AST_EXTENSION_UNAVAILABLE;
+ case AST_DEVICE_RINGINUSE:
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+ case AST_DEVICE_RINGING:
+ return AST_EXTENSION_RINGING;
+ case AST_DEVICE_INUSE:
+ return AST_EXTENSION_INUSE;
+ case AST_DEVICE_NOT_INUSE:
+ return AST_EXTENSION_NOT_INUSE;
+ case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
+ break;
}
- if (a->argc != 4) {
- return CLI_SHOWUSAGE;
- }
+ return AST_EXTENSION_NOT_INUSE;
+}
- if (!(acf = ast_custom_function_find(a->argv[3]))) {
- ast_cli(a->fd, "No function by that name registered.\n");
- return CLI_FAILURE;
- }
+/*!
+ * \internal
+ * \brief Parse out the presence portion of the hint string
+ */
+static char *parse_hint_presence(struct ast_str *hint_args)
+{
+ char *copy = ast_strdupa(ast_str_buffer(hint_args));
+ char *tmp = "";
- syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
- if (!(syntax = ast_malloc(syntax_size))) {
- ast_cli(a->fd, "Memory allocation failure!\n");
- return CLI_FAILURE;
+ if ((tmp = strrchr(copy, ','))) {
+ *tmp = '\0';
+ tmp++;
+ } else {
+ return NULL;
}
+ ast_str_set(&hint_args, 0, "%s", tmp);
+ return ast_str_buffer(hint_args);
+}
- snprintf(info, sizeof(info), "\n -= Info about function '%s' =- \n\n", acf->name);
- term_color(infotitle, info, COLOR_MAGENTA, 0, sizeof(infotitle));
- term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
- term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
- term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
- term_color(argtitle, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
- term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, 40);
- term_color(syntax, S_OR(acf->syntax, "Not available"), COLOR_CYAN, 0, syntax_size);
-#ifdef AST_XML_DOCS
- if (acf->docsrc == AST_XML_DOC) {
- arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
- synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
- description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
- seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1);
- } else
-#endif
- {
- synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
- synopsis = ast_malloc(synopsis_size);
+/*!
+ * \internal
+ * \brief Parse out the device portion of the hint string
+ */
+static char *parse_hint_device(struct ast_str *hint_args)
+{
+ char *copy = ast_strdupa(ast_str_buffer(hint_args));
+ char *tmp;
- description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
- description = ast_malloc(description_size);
+ if ((tmp = strrchr(copy, ','))) {
+ *tmp = '\0';
+ }
- arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
- arguments = ast_malloc(arguments_size);
+ ast_str_set(&hint_args, 0, "%s", copy);
+ return ast_str_buffer(hint_args);
+}
- seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
- seealso = ast_malloc(seealso_size);
+static void device_state_info_dt(void *obj)
+{
+ struct ast_device_state_info *info = obj;
- /* check allocated memory. */
- if (!synopsis || !description || !arguments || !seealso) {
- ast_free(synopsis);
- ast_free(description);
- ast_free(arguments);
- ast_free(seealso);
- ast_free(syntax);
- return CLI_FAILURE;
- }
+ ao2_cleanup(info->causing_channel);
+}
- term_color(arguments, S_OR(acf->arguments, "Not available"), COLOR_CYAN, 0, arguments_size);
- term_color(synopsis, S_OR(acf->synopsis, "Not available"), COLOR_CYAN, 0, synopsis_size);
- term_color(description, S_OR(acf->desc, "Not available"), COLOR_CYAN, 0, description_size);
- term_color(seealso, S_OR(acf->seealso, "Not available"), COLOR_CYAN, 0, seealso_size);
- }
+static struct ao2_container *alloc_device_state_info(void)
+{
+ return ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+}
- ast_cli(a->fd, "%s%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n",
- infotitle, syntitle, synopsis, destitle, description,
- stxtitle, syntax, argtitle, arguments, seealsotitle, seealso);
+static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
+{
+ char *cur;
+ char *rest;
+ struct ast_devstate_aggregate agg;
- ast_free(arguments);
- ast_free(synopsis);
- ast_free(description);
- ast_free(seealso);
- ast_free(syntax);
+ /* One or more devices separated with a & character */
+ rest = parse_hint_device(hint_app);
- return CLI_SUCCESS;
-}
+ ast_devstate_aggregate_init(&agg);
+ while ((cur = strsep(&rest, "&"))) {
+ enum ast_device_state state = ast_device_state(cur);
-static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
-{
- struct ast_custom_function *cur;
- int cmp;
+ ast_devstate_aggregate_add(&agg, state);
+ if (device_state_info) {
+ struct ast_device_state_info *obj;
- AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
- cmp = strcmp(name, cur->name);
- if (cmp > 0) {
- continue;
- }
- if (!cmp) {
- /* Found it. */
- break;
+ obj = ao2_alloc_options(sizeof(*obj) + strlen(cur), device_state_info_dt, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ /* if failed we cannot add this device */
+ if (obj) {
+ obj->device_state = state;
+ strcpy(obj->device_name, cur);
+ ao2_link(device_state_info, obj);
+ ao2_ref(obj, -1);
+ }
}
- /* Not in container. */
- cur = NULL;
- break;
}
- return cur;
+ return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
}
-struct ast_custom_function *ast_custom_function_find(const char *name)
+/*! \brief Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
{
- struct ast_custom_function *acf;
+ struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
- AST_RWLIST_RDLOCK(&acf_root);
- acf = ast_custom_function_find_nolock(name);
- AST_RWLIST_UNLOCK(&acf_root);
+ if (!e || !hint_app) {
+ return -1;
+ }
- return acf;
+ ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
+ return ast_extension_state3(hint_app, device_state_info);
}
-int ast_custom_function_unregister(struct ast_custom_function *acf)
+/*! \brief Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
{
- struct ast_custom_function *cur;
-
- if (!acf) {
- return -1;
- }
+ int i;
- AST_RWLIST_WRLOCK(&acf_root);
- if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist))) {
-#ifdef AST_XML_DOCS
- if (cur->docsrc == AST_XML_DOC) {
- ast_string_field_free_memory(acf);
- }
-#endif
- ast_verb(2, "Unregistered custom function %s\n", cur->name);
+ for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
+ if (extension_states[i].extension_state == extension_state)
+ return extension_states[i].text;
}
- AST_RWLIST_UNLOCK(&acf_root);
-
- return cur ? 0 : -1;
-}
-
-/*!
- * \brief Returns true if given custom function escalates privileges on read.
- *
- * \param acf Custom function to query.
- * \return True (non-zero) if reads escalate privileges.
- * \return False (zero) if reads just read.
- */
-static int read_escalates(const struct ast_custom_function *acf) {
- return acf->read_escalates;
+ return "Unknown";
}
/*!
- * \brief Returns true if given custom function escalates privileges on write.
- *
- * \param acf Custom function to query.
- * \return True (non-zero) if writes escalate privileges.
- * \return False (zero) if writes just write.
- */
-static int write_escalates(const struct ast_custom_function *acf) {
- return acf->write_escalates;
-}
-
-/*! \internal
- * \brief Retrieve the XML documentation of a specified ast_custom_function,
- * and populate ast_custom_function string fields.
- * \param acf ast_custom_function structure with empty 'desc' and 'synopsis'
- * but with a function 'name'.
- * \retval -1 On error.
- * \retval 0 On succes.
+ * \internal
+ * \brief Check extension state for an extension by using hint
*/
-static int acf_retrieve_docs(struct ast_custom_function *acf)
+static int internal_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
+ struct ao2_container *device_state_info)
{
-#ifdef AST_XML_DOCS
- char *tmpxml;
+ struct ast_exten *e;
- /* Let's try to find it in the Documentation XML */
- if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) {
- return 0;
+ if (!(e = ast_hint_extension(c, context, exten))) { /* Do we have a hint for this extension ? */
+ return -1; /* No hint, return -1 */
}
- if (ast_string_field_init(acf, 128)) {
- return -1;
+ if (e->exten[0] == '_') {
+ /* Create this hint on-the-fly */
+ ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
+ e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
+ e->registrar);
+ if (!(e = ast_hint_extension(c, context, exten))) {
+ /* Improbable, but not impossible */
+ return -1;
+ }
}
- /* load synopsis */
- tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
- ast_string_field_set(acf, synopsis, tmpxml);
- ast_free(tmpxml);
+ return ast_extension_state2(e, device_state_info); /* Check all devices in the hint */
+}
- /* load description */
- tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
- ast_string_field_set(acf, desc, tmpxml);
- ast_free(tmpxml);
+/*! \brief Check extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
+{
+ return internal_extension_state_extended(c, context, exten, NULL);
+}
- /* load syntax */
- tmpxml = ast_xmldoc_build_syntax("function", acf->name, ast_module_name(acf->mod));
- ast_string_field_set(acf, syntax, tmpxml);
- ast_free(tmpxml);
+/*! \brief Check extended extension state for an extension by using hint */
+int ast_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
+ struct ao2_container **device_state_info)
+{
+ struct ao2_container *container = NULL;
+ int ret;
- /* load arguments */
- tmpxml = ast_xmldoc_build_arguments("function", acf->name, ast_module_name(acf->mod));
- ast_string_field_set(acf, arguments, tmpxml);
- ast_free(tmpxml);
+ if (device_state_info) {
+ container = alloc_device_state_info();
+ }
- /* load seealso */
- tmpxml = ast_xmldoc_build_seealso("function", acf->name, ast_module_name(acf->mod));
- ast_string_field_set(acf, seealso, tmpxml);
- ast_free(tmpxml);
+ ret = internal_extension_state_extended(c, context, exten, container);
+ if (ret < 0 && container) {
+ ao2_ref(container, -1);
+ container = NULL;
+ }
- acf->docsrc = AST_XML_DOC;
-#endif
+ if (device_state_info) {
+ get_device_state_causing_channels(container);
+ *device_state_info = container;
+ }
- return 0;
+ return ret;
}
-int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
{
- struct ast_custom_function *cur;
+ struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+ char *presence_provider;
+ const char *app;
- if (!acf) {
+ if (!e || !hint_app) {
return -1;
}
- acf->mod = mod;
-#ifdef AST_XML_DOCS
- acf->docsrc = AST_STATIC_DOC;
-#endif
-
- if (acf_retrieve_docs(acf)) {
+ app = ast_get_extension_app(e);
+ if (ast_strlen_zero(app)) {
return -1;
}
- AST_RWLIST_WRLOCK(&acf_root);
-
- cur = ast_custom_function_find_nolock(acf->name);
- if (cur) {
- ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
- AST_RWLIST_UNLOCK(&acf_root);
- return -1;
- }
+ ast_str_set(&hint_app, 0, "%s", app);
+ presence_provider = parse_hint_presence(hint_app);
- /* Store in alphabetical order */
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
- if (strcmp(acf->name, cur->name) < 0) {
- AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
- if (!cur) {
- AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
+ if (ast_strlen_zero(presence_provider)) {
+ /* No presence string in the hint */
+ return 0;
}
- AST_RWLIST_UNLOCK(&acf_root);
-
- ast_verb(2, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
-
- return 0;
+ return ast_presence_state(presence_provider, subtype, message);
}
-int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
+int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
{
- int res;
+ struct ast_exten *e;
- res = __ast_custom_function_register(acf, mod);
- if (res != 0) {
- return -1;
+ if (!(e = ast_hint_extension(c, context, exten))) { /* Do we have a hint for this extension ? */
+ return -1; /* No hint, return -1 */
}
- switch (escalation) {
- case AST_CFE_NONE:
- break;
- case AST_CFE_READ:
- acf->read_escalates = 1;
- break;
- case AST_CFE_WRITE:
- acf->write_escalates = 1;
- break;
- case AST_CFE_BOTH:
- acf->read_escalates = 1;
- acf->write_escalates = 1;
- break;
+ if (e->exten[0] == '_') {
+ /* Create this hint on-the-fly */
+ ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
+ e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
+ e->registrar);
+ if (!(e = ast_hint_extension(c, context, exten))) {
+ /* Improbable, but not impossible */
+ return -1;
+ }
}
- return 0;
+ return extension_presence_state_helper(e, subtype, message);
}
-/*! \brief return a pointer to the arguments of the function,
- * and terminates the function name with '\\0'
- */
-static char *func_args(char *function)
+static int execute_state_callback(ast_state_cb_type cb,
+ const char *context,
+ const char *exten,
+ void *data,
+ enum ast_state_cb_update_reason reason,
+ struct ast_hint *hint,
+ struct ao2_container *device_state_info)
{
- char *args = strchr(function, '(');
+ int res = 0;
+ struct ast_state_cb_info info = { 0, };
- if (!args) {
- ast_log(LOG_WARNING, "Function '%s' doesn't contain parentheses. Assuming null argument.\n", function);
- } else {
- char *p;
- *args++ = '\0';
- if ((p = strrchr(args, ')'))) {
- *p = '\0';
+ info.reason = reason;
+
+ /* Copy over current hint data */
+ if (hint) {
+ ao2_lock(hint);
+ info.exten_state = hint->laststate;
+ info.device_state_info = device_state_info;
+ info.presence_state = hint->last_presence_state;
+ if (!(ast_strlen_zero(hint->last_presence_subtype))) {
+ info.presence_subtype = ast_strdupa(hint->last_presence_subtype);
} else {
- ast_log(LOG_WARNING, "Can't find trailing parenthesis for function '%s(%s'?\n", function, args);
+ info.presence_subtype = "";
}
+ if (!(ast_strlen_zero(hint->last_presence_message))) {
+ info.presence_message = ast_strdupa(hint->last_presence_message);
+ } else {
+ info.presence_message = "";
+ }
+ ao2_unlock(hint);
+ } else {
+ info.exten_state = AST_EXTENSION_REMOVED;
}
- return args;
-}
-
-void pbx_live_dangerously(int new_live_dangerously)
-{
- if (new_live_dangerously && !live_dangerously) {
- ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
- "See https://wiki.asterisk.org/wiki/x/1gKfAQ for more details.\n");
- }
-
- if (!new_live_dangerously && live_dangerously) {
- ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
- }
- live_dangerously = new_live_dangerously;
-}
-
-int ast_thread_inhibit_escalations(void)
-{
- int *thread_inhibit_escalations;
- thread_inhibit_escalations = ast_threadstorage_get(
- &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+ res = cb(context, exten, &info, data);
- if (thread_inhibit_escalations == NULL) {
- ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
- return -1;
- }
-
- *thread_inhibit_escalations = 1;
- return 0;
+ return res;
}
/*!
- * \brief Indicates whether the current thread inhibits the execution of
- * dangerous functions.
+ * /internal
+ * /brief Identify a channel for every device which is supposedly responsible for the device state.
*
- * \return True (non-zero) if dangerous function execution is inhibited.
- * \return False (zero) if dangerous function execution is allowed.
+ * Especially when the device is ringing, the oldest ringing channel is chosen.
+ * For all other cases the first encountered channel in the specific state is chosen.
*/
-static int thread_inhibits_escalations(void)
+static void get_device_state_causing_channels(struct ao2_container *c)
{
- int *thread_inhibit_escalations;
-
- thread_inhibit_escalations = ast_threadstorage_get(
- &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+ struct ao2_iterator iter;
+ struct ast_device_state_info *info;
+ struct ast_channel *chan;
- if (thread_inhibit_escalations == NULL) {
- ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
- /* On error, assume that we are inhibiting */
- return 1;
+ if (!c || !ao2_container_count(c)) {
+ return;
}
+ iter = ao2_iterator_init(c, 0);
+ for (; (info = ao2_iterator_next(&iter)); ao2_ref(info, -1)) {
+ enum ast_channel_state search_state = 0; /* prevent false uninit warning */
+ char match[AST_CHANNEL_NAME];
+ struct ast_channel_iterator *chan_iter;
+ struct timeval chantime = {0, }; /* prevent false uninit warning */
- return *thread_inhibit_escalations;
-}
+ switch (info->device_state) {
+ case AST_DEVICE_RINGING:
+ case AST_DEVICE_RINGINUSE:
+ /* find ringing channel */
+ search_state = AST_STATE_RINGING;
+ break;
+ case AST_DEVICE_BUSY:
+ /* find busy channel */
+ search_state = AST_STATE_BUSY;
+ break;
+ case AST_DEVICE_ONHOLD:
+ case AST_DEVICE_INUSE:
+ /* find up channel */
+ search_state = AST_STATE_UP;
+ break;
+ case AST_DEVICE_UNKNOWN:
+ case AST_DEVICE_NOT_INUSE:
+ case AST_DEVICE_INVALID:
+ case AST_DEVICE_UNAVAILABLE:
+ case AST_DEVICE_TOTAL /* not a state */:
+ /* no channels are of interest */
+ continue;
+ }
-/*!
- * \brief Determines whether execution of a custom function's read function
- * is allowed.
- *
- * \param acfptr Custom function to check
- * \return True (non-zero) if reading is allowed.
- * \return False (zero) if reading is not allowed.
- */
-static int is_read_allowed(struct ast_custom_function *acfptr)
-{
- if (!acfptr) {
- return 1;
+ /* iterate over all channels of the device */
+ snprintf(match, sizeof(match), "%s-", info->device_name);
+ chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
+ for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
+ ast_channel_lock(chan);
+ /* this channel's state doesn't match */
+ if (search_state != ast_channel_state(chan)) {
+ ast_channel_unlock(chan);
+ continue;
+ }
+ /* any non-ringing channel will fit */
+ if (search_state != AST_STATE_RINGING) {
+ ast_channel_unlock(chan);
+ info->causing_channel = chan; /* is kept ref'd! */
+ break;
+ }
+ /* but we need the oldest ringing channel of the device to match with undirected pickup */
+ if (!info->causing_channel) {
+ chantime = ast_channel_creationtime(chan);
+ ast_channel_ref(chan); /* must ref it! */
+ info->causing_channel = chan;
+ } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
+ chantime = ast_channel_creationtime(chan);
+ ast_channel_unref(info->causing_channel);
+ ast_channel_ref(chan); /* must ref it! */
+ info->causing_channel = chan;
+ }
+ ast_channel_unlock(chan);
+ }
+ ast_channel_iterator_destroy(chan_iter);
}
+ ao2_iterator_destroy(&iter);
+}
- if (!read_escalates(acfptr)) {
- return 1;
- }
+static void device_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app)
+{
+ struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
+ int state;
+ int same_state;
+ struct ao2_container *device_state_info;
+ int first_extended_cb_call = 1;
+ char context_name[AST_MAX_CONTEXT];
+ char exten_name[AST_MAX_EXTENSION];
- if (!thread_inhibits_escalations()) {
- return 1;
+ ao2_lock(hint);
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
}
- if (live_dangerously) {
- /* Global setting overrides the thread's preference */
- ast_debug(2, "Reading %s from a dangerous context\n",
- acfptr->name);
- return 1;
- }
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+ ao2_unlock(hint);
- /* We have no reason to allow this function to execute */
- return 0;
-}
+ /*
+ * Get device state for this hint.
+ *
+ * NOTE: We cannot hold any locks while determining the hint
+ * device state or notifying the watchers without causing a
+ * deadlock. (conlock, hints, and hint)
+ */
-/*!
- * \brief Determines whether execution of a custom function's write function
- * is allowed.
- *
- * \param acfptr Custom function to check
- * \return True (non-zero) if writing is allowed.
- * \return False (zero) if writing is not allowed.
- */
-static int is_write_allowed(struct ast_custom_function *acfptr)
-{
- if (!acfptr) {
- return 1;
- }
+ /* Make a container so state3 can fill it if we wish.
+ * If that failed we simply do not provide the extended state info.
+ */
+ device_state_info = alloc_device_state_info();
- if (!write_escalates(acfptr)) {
- return 1;
+ state = ast_extension_state3(*hint_app, device_state_info);
+ same_state = state == hint->laststate;
+ if (same_state && (~state & AST_EXTENSION_RINGING)) {
+ ao2_cleanup(device_state_info);
+ return;
}
- if (!thread_inhibits_escalations()) {
- return 1;
- }
+ /* Device state changed since last check - notify the watchers. */
+ hint->laststate = state; /* record we saw the change */
- if (live_dangerously) {
- /* Global setting overrides the thread's preference */
- ast_debug(2, "Writing %s from a dangerous context\n",
- acfptr->name);
- return 1;
+ /* For general callbacks */
+ if (!same_state) {
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
}
- /* We have no reason to allow this function to execute */
- return 0;
-}
-
-int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
-{
- char *copy = ast_strdupa(function);
- char *args = func_args(copy);
- struct ast_custom_function *acfptr = ast_custom_function_find(copy);
- int res;
- struct ast_module_user *u = NULL;
-
- if (acfptr == NULL) {
- ast_log(LOG_ERROR, "Function %s not registered\n", copy);
- } else if (!acfptr->read && !acfptr->read2) {
- ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
- } else if (!is_read_allowed(acfptr)) {
- ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
- } else if (acfptr->read) {
- if (acfptr->mod) {
- u = __ast_module_user_add(acfptr->mod, chan);
- }
- res = acfptr->read(chan, copy, args, workspace, len);
- if (acfptr->mod && u) {
- __ast_module_user_remove(acfptr->mod, u);
- }
- return res;
- } else {
- struct ast_str *str = ast_str_create(16);
- if (acfptr->mod) {
- u = __ast_module_user_add(acfptr->mod, chan);
- }
- res = acfptr->read2(chan, copy, args, &str, 0);
- if (acfptr->mod && u) {
- __ast_module_user_remove(acfptr->mod, u);
+ /* For extension callbacks */
+ /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
+ * included. Normal callbacks are only called when the state changed.
+ */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ if (state_cb->extended && first_extended_cb_call) {
+ /* Fill detailed device_state_info now that we know it is used by extd. callback */
+ first_extended_cb_call = 0;
+ get_device_state_causing_channels(device_state_info);
+ }
+ if (state_cb->extended || !same_state) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ state_cb->extended ? device_state_info : NULL);
}
- ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
- ast_free(str);
- return res;
}
- return -1;
-}
+ ao2_iterator_destroy(&cb_iter);
-int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
-{
- char *copy = ast_strdupa(function);
- char *args = func_args(copy);
- struct ast_custom_function *acfptr = ast_custom_function_find(copy);
- int res;
- struct ast_module_user *u = NULL;
-
- if (acfptr == NULL) {
- ast_log(LOG_ERROR, "Function %s not registered\n", copy);
- } else if (!acfptr->read && !acfptr->read2) {
- ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
- } else if (!is_read_allowed(acfptr)) {
- ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
- } else {
- if (acfptr->mod) {
- u = __ast_module_user_add(acfptr->mod, chan);
- }
- ast_str_reset(*str);
- if (acfptr->read2) {
- /* ast_str enabled */
- res = acfptr->read2(chan, copy, args, str, maxlen);
- } else {
- /* Legacy function pointer, allocate buffer for result */
- int maxsize = ast_str_size(*str);
- if (maxlen > -1) {
- if (maxlen == 0) {
- if (acfptr->read_max) {
- maxsize = acfptr->read_max;
- } else {
- maxsize = VAR_BUF_SIZE;
- }
- } else {
- maxsize = maxlen;
- }
- ast_str_make_space(str, maxsize);
- }
- res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
- }
- if (acfptr->mod && u) {
- __ast_module_user_remove(acfptr->mod, u);
- }
- return res;
- }
- return -1;
+ ao2_cleanup(device_state_info);
}
-int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+static void presence_state_notify_callbacks(struct ast_hint *hint, struct ast_str **hint_app,
+ struct ast_presence_state_message *presence_state)
{
- char *copy = ast_strdupa(function);
- char *args = func_args(copy);
- struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+ struct ao2_iterator cb_iter;
+ struct ast_state_cb *state_cb;
+ char context_name[AST_MAX_CONTEXT];
+ char exten_name[AST_MAX_EXTENSION];
- if (acfptr == NULL) {
- ast_log(LOG_ERROR, "Function %s not registered\n", copy);
- } else if (!acfptr->write) {
- ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
- } else if (!is_write_allowed(acfptr)) {
- ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
- } else {
- int res;
- struct ast_module_user *u = NULL;
- if (acfptr->mod)
- u = __ast_module_user_add(acfptr->mod, chan);
- res = acfptr->write(chan, copy, args, value);
- if (acfptr->mod && u)
- __ast_module_user_remove(acfptr->mod, u);
- return res;
+ ao2_lock(hint);
+ if (!hint->exten) {
+ /* The extension has already been destroyed */
+ ao2_unlock(hint);
+ return;
}
- return -1;
-}
-
-void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
-{
- /* Substitutes variables into buf, based on string templ */
- char *cp4 = NULL;
- const char *whereweare;
- int orig_size = 0;
- int offset, offset2, isfunction;
- const char *nextvar, *nextexp, *nextthing;
- const char *vars, *vare;
- char *finalvars;
- int pos, brackets, needsub, len;
- struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
-
- ast_str_reset(*buf);
- whereweare = templ;
- while (!ast_strlen_zero(whereweare)) {
- /* reset our buffer */
- ast_str_reset(substr3);
-
- /* Assume we're copying the whole remaining string */
- pos = strlen(whereweare);
- nextvar = NULL;
- nextexp = NULL;
- nextthing = strchr(whereweare, '$');
- if (nextthing) {
- switch (nextthing[1]) {
- case '{':
- nextvar = nextthing;
- pos = nextvar - whereweare;
- break;
- case '[':
- nextexp = nextthing;
- pos = nextexp - whereweare;
- break;
- default:
- pos = 1;
- }
- }
-
- if (pos) {
- /* Copy that many bytes */
- ast_str_append_substr(buf, maxlen, whereweare, pos);
-
- templ += pos;
- whereweare += pos;
- }
-
- if (nextvar) {
- /* We have a variable. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextvar + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- } else if (vare[0] == '{') {
- brackets++;
- } else if (vare[0] == '}') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '['))
- needsub++;
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
- len = vare - vars - 1;
-
- /* Skip totally over variable string */
- whereweare += (len + 3);
+ /*
+ * Save off strings in case the hint extension gets destroyed
+ * while we are notifying the watchers.
+ */
+ ast_copy_string(context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(context_name));
+ ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
+ sizeof(exten_name));
+ ast_str_set(hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+ ao2_unlock(hint);
- /* Store variable name (and truncate) */
- ast_str_set_substr(&substr1, 0, vars, len);
- ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
+ /* Check to see if update is necessary */
+ if ((hint->last_presence_state == presence_state->state) &&
+ ((hint->last_presence_subtype && presence_state->subtype &&
+ !strcmp(hint->last_presence_subtype, presence_state->subtype)) ||
+ (!hint->last_presence_subtype && !presence_state->subtype)) &&
+ ((hint->last_presence_message && presence_state->message &&
+ !strcmp(hint->last_presence_message, presence_state->message)) ||
+ (!hint->last_presence_message && !presence_state->message))) {
+ /* this update is the same as the last, do nothing */
+ return;
+ }
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
+ /* update new values */
+ ast_free(hint->last_presence_subtype);
+ ast_free(hint->last_presence_message);
+ hint->last_presence_state = presence_state->state;
+ hint->last_presence_subtype = presence_state->subtype ? ast_strdup(presence_state->subtype) : NULL;
+ hint->last_presence_message = presence_state->message ? ast_strdup(presence_state->message) : NULL;
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+
+ /* For extension callbacks */
+ cb_iter = ao2_iterator_init(hint->callbacks, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_cleanup(state_cb)) {
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_PRESENCE,
+ hint,
+ NULL);
+ }
+ ao2_iterator_destroy(&cb_iter);
+}
+
+static int handle_hint_change_message_type(struct stasis_message *msg, enum ast_state_cb_update_reason reason)
+{
+ struct ast_hint *hint;
+ struct ast_str *hint_app;
- if (!substr2) {
- substr2 = ast_str_create(16);
- }
- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
- finalvars = ast_str_buffer(substr2);
- } else {
- finalvars = ast_str_buffer(substr1);
- }
+ if (hint_change_message_type() != stasis_message_type(msg)) {
+ return 0;
+ }
- parse_variable_name(finalvars, &offset, &offset2, &isfunction);
- if (isfunction) {
- /* Evaluate function */
- if (c || !headp) {
- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
- } else {
- struct varshead old;
- struct ast_channel *bogus = ast_dummy_channel_alloc();
- if (bogus) {
- memcpy(&old, ast_channel_varshead(bogus), sizeof(old));
- memcpy(ast_channel_varshead(bogus), headp, sizeof(*ast_channel_varshead(bogus)));
- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
- /* Don't deallocate the varshead that was passed in */
- memcpy(ast_channel_varshead(bogus), &old, sizeof(*ast_channel_varshead(bogus)));
- ast_channel_unref(bogus);
- } else {
- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
- }
- }
- ast_debug(2, "Function %s result is '%s'\n", finalvars, cp4 ? cp4 : "(null)");
- } else {
- /* Retrieve variable value */
- ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
- cp4 = ast_str_buffer(substr3);
- }
- if (cp4) {
- ast_str_substring(substr3, offset, offset2);
- ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
- }
- } else if (nextexp) {
- /* We have an expression. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextexp + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '[')) {
- needsub++;
- brackets++;
- vare++;
- } else if (vare[0] == '[') {
- brackets++;
- } else if (vare[0] == ']') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- vare++;
- }
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
- len = vare - vars - 1;
+ if (!(hint_app = ast_str_create(1024))) {
+ return -1;
+ }
- /* Skip totally over expression */
- whereweare += (len + 3);
+ hint = stasis_message_data(msg);
- /* Store variable name (and truncate) */
- ast_str_set_substr(&substr1, 0, vars, len);
+ switch (reason) {
+ case AST_HINT_UPDATE_DEVICE:
+ device_state_notify_callbacks(hint, &hint_app);
+ break;
+ case AST_HINT_UPDATE_PRESENCE:
+ {
+ char *presence_subtype = NULL;
+ char *presence_message = NULL;
+ int state;
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
+ state = extension_presence_state_helper(
+ hint->exten, &presence_subtype, &presence_message);
+ {
+ struct ast_presence_state_message presence_state = {
+ .state = state > 0 ? state : AST_PRESENCE_INVALID,
+ .subtype = presence_subtype,
+ .message = presence_message
+ };
- if (!substr2) {
- substr2 = ast_str_create(16);
- }
- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);
- finalvars = ast_str_buffer(substr2);
- } else {
- finalvars = ast_str_buffer(substr1);
+ presence_state_notify_callbacks(hint, &hint_app, &presence_state);
}
- if (ast_str_expr(&substr3, 0, c, finalvars)) {
- ast_debug(2, "Expression result is '%s'\n", ast_str_buffer(substr3));
- }
- ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+ ast_free(presence_subtype);
+ ast_free(presence_message);
}
+ break;
}
- *used = ast_str_strlen(*buf) - orig_size;
- ast_free(substr1);
- ast_free(substr2);
- ast_free(substr3);
-}
-
-void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
-{
- size_t used;
- ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
-}
-void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
-{
- size_t used;
- ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
+ ast_free(hint_app);
+ return 1;
}
-void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
+static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
{
- /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
- char *cp4 = NULL;
- const char *whereweare, *orig_cp2 = cp2;
- int length, offset, offset2, isfunction;
- char *workspace = NULL;
- char *ltmp = NULL, *var = NULL;
- char *nextvar, *nextexp, *nextthing;
- char *vars, *vare;
- int pos, brackets, needsub, len;
+ struct ast_device_state_message *dev_state;
+ struct ast_str *hint_app;
+ struct ast_hintdevice *device;
+ struct ast_hintdevice *cmpdevice;
+ struct ao2_iterator *dev_iter;
+ struct ao2_iterator auto_iter;
+ struct ast_autohint *autohint;
+ char *virtual_device;
+ char *type;
+ char *device_name;
- *cp2 = 0; /* just in case nothing ends up there */
- whereweare = cp1;
- while (!ast_strlen_zero(whereweare) && count) {
- /* Assume we're copying the whole remaining string */
- pos = strlen(whereweare);
- nextvar = NULL;
- nextexp = NULL;
- nextthing = strchr(whereweare, '$');
- if (nextthing) {
- switch (nextthing[1]) {
- case '{':
- nextvar = nextthing;
- pos = nextvar - whereweare;
- break;
- case '[':
- nextexp = nextthing;
- pos = nextexp - whereweare;
- break;
- default:
- pos = 1;
- }
- }
+ if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
+ return;
+ }
- if (pos) {
- /* Can't copy more than 'count' bytes */
- if (pos > count)
- pos = count;
-
- /* Copy that many bytes */
- memcpy(cp2, whereweare, pos);
-
- count -= pos;
- cp2 += pos;
- whereweare += pos;
- *cp2 = 0;
- }
-
- if (nextvar) {
- /* We have a variable. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextvar + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- } else if (vare[0] == '{') {
- brackets++;
- } else if (vare[0] == '}') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '['))
- needsub++;
- vare++;
- }
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
- len = vare - vars - 1;
+ if (ast_device_state_message_type() != stasis_message_type(msg)) {
+ return;
+ }
- /* Skip totally over variable string */
- whereweare += (len + 3);
+ dev_state = stasis_message_data(msg);
+ if (dev_state->eid) {
+ /* ignore non-aggregate states */
+ return;
+ }
- if (!var)
- var = ast_alloca(VAR_BUF_SIZE);
+ if (ao2_container_count(hintdevices) == 0 && ao2_container_count(autohints) == 0) {
+ /* There are no hints monitoring devices. */
+ return;
+ }
- /* Store variable name (and truncate) */
- ast_copy_string(var, vars, len + 1);
+ hint_app = ast_str_create(1024);
+ if (!hint_app) {
+ return;
+ }
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
+ cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
+ strcpy(cmpdevice->hintdevice, dev_state->device);
- if (!ltmp) {
- ltmp = ast_alloca(VAR_BUF_SIZE);
- }
- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
- vars = ltmp;
- } else {
- vars = var;
- }
+ ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
- if (!workspace)
- workspace = ast_alloca(VAR_BUF_SIZE);
-
- workspace[0] = '\0';
-
- parse_variable_name(vars, &offset, &offset2, &isfunction);
- if (isfunction) {
- /* Evaluate function */
- if (c || !headp)
- cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
- else {
- struct varshead old;
- struct ast_channel *c = ast_dummy_channel_alloc();
- if (c) {
- memcpy(&old, ast_channel_varshead(c), sizeof(old));
- memcpy(ast_channel_varshead(c), headp, sizeof(*ast_channel_varshead(c)));
- cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
- /* Don't deallocate the varshead that was passed in */
- memcpy(ast_channel_varshead(c), &old, sizeof(*ast_channel_varshead(c)));
- c = ast_channel_unref(c);
- } else {
- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
- }
- }
- ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
- } else {
- /* Retrieve variable value */
- pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
- }
- if (cp4) {
- cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
-
- length = strlen(cp4);
- if (length > count)
- length = count;
- memcpy(cp2, cp4, length);
- count -= length;
- cp2 += length;
- *cp2 = 0;
- }
- } else if (nextexp) {
- /* We have an expression. Find the start and end, and determine
- if we are going to have to recursively call ourselves on the
- contents */
- vars = vare = nextexp + 2;
- brackets = 1;
- needsub = 0;
-
- /* Find the end of it */
- while (brackets && *vare) {
- if ((vare[0] == '$') && (vare[1] == '[')) {
- needsub++;
- brackets++;
- vare++;
- } else if (vare[0] == '[') {
- brackets++;
- } else if (vare[0] == ']') {
- brackets--;
- } else if ((vare[0] == '$') && (vare[1] == '{')) {
- needsub++;
- vare++;
- }
- vare++;
+ /* Initially we find all hints for the device and notify them */
+ dev_iter = ao2_t_callback(hintdevices,
+ OBJ_SEARCH_OBJECT | OBJ_MULTIPLE,
+ hintdevice_cmp_multiple,
+ cmpdevice,
+ "find devices in container");
+ if (dev_iter) {
+ for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
+ if (device->hint) {
+ device_state_notify_callbacks(device->hint, &hint_app);
}
- if (brackets)
- ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
- len = vare - vars - 1;
+ }
+ ao2_iterator_destroy(dev_iter);
+ }
- /* Skip totally over expression */
- whereweare += (len + 3);
+ /* Second stage we look for any autohint contexts and if the device is not already in the hints
+ * we create it.
+ */
+ type = ast_strdupa(dev_state->device);
+ if (ast_strlen_zero(type)) {
+ goto end;
+ }
- if (!var)
- var = ast_alloca(VAR_BUF_SIZE);
+ /* Determine if this is a virtual/custom device or a real device */
+ virtual_device = strchr(type, ':');
+ device_name = strchr(type, '/');
+ if (virtual_device && (!device_name || (virtual_device < device_name))) {
+ device_name = virtual_device;
+ }
- /* Store variable name (and truncate) */
- ast_copy_string(var, vars, len + 1);
+ /* Invalid device state name - not a virtual/custom device and not a real device */
+ if (ast_strlen_zero(device_name)) {
+ goto end;
+ }
- /* Substitute if necessary */
- if (needsub) {
- size_t my_used;
+ *device_name++ = '\0';
- if (!ltmp) {
- ltmp = ast_alloca(VAR_BUF_SIZE);
- }
- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);
- vars = ltmp;
- } else {
- vars = var;
- }
+ auto_iter = ao2_iterator_init(autohints, 0);
+ for (; (autohint = ao2_iterator_next(&auto_iter)); ao2_t_ref(autohint, -1, "Next autohint")) {
+ if (ast_get_hint(NULL, 0, NULL, 0, NULL, autohint->context, device_name)) {
+ continue;
+ }
- length = ast_expr(vars, cp2, count, c);
+ /* The device has no hint in the context referenced by this autohint so create one */
+ ast_add_extension(autohint->context, 0, device_name,
+ PRIORITY_HINT, NULL, NULL, dev_state->device,
+ ast_strdup(dev_state->device), ast_free_ptr, autohint->registrar);
- if (length) {
- ast_debug(1, "Expression result is '%s'\n", cp2);
- count -= length;
- cp2 += length;
- *cp2 = 0;
- }
- }
+ /* Since this hint was just created there are no watchers, so we don't need to notify anyone */
}
- *used = cp2 - orig_cp2;
-}
+ ao2_iterator_destroy(&auto_iter);
-void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
-{
- size_t used;
- pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, &used);
+end:
+ ast_mutex_unlock(&context_merge_lock);
+ ast_free(hint_app);
+ return;
}
-void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+/*!
+ * \internal
+ * \brief Destroy the given state callback object.
+ *
+ * \param doomed State callback to destroy.
+ *
+ * \return Nothing
+ */
+static void destroy_state_cb(void *doomed)
{
- size_t used;
- pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);
+ struct ast_state_cb *state_cb = doomed;
+
+ if (state_cb->destroy_cb) {
+ state_cb->destroy_cb(state_cb->id, state_cb->data);
+ }
}
/*!
- * \brief The return value depends on the action:
- *
- * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
- * and return 0 on failure, -1 on match;
- * E_FINDLABEL maps the label to a priority, and returns
- * the priority on success, ... XXX
- * E_SPAWN, spawn an application,
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- *
- * \note The channel is auto-serviced in this function, because doing an extension
- * match may block for a long time. For example, if the lookup has to use a network
- * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
- * auto-service code will queue up any important signalling frames to be processed
- * after this is done.
+ * \internal
+ * \brief Add watcher for extension states with destructor
*/
-static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
- const char *context, const char *exten, int priority,
- const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
+static int extension_state_add_destroy(const char *context, const char *exten,
+ ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data, int extended)
{
+ struct ast_hint *hint;
+ struct ast_state_cb *state_cb;
struct ast_exten *e;
- struct ast_app *app;
- char *substitute = NULL;
- int res;
- struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
- char passdata[EXT_DATA_SIZE];
- int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+ int id;
- ast_rdlock_contexts();
- if (found)
- *found = 0;
+ /* If there's no context and extension: add callback to statecbs list */
+ if (!context && !exten) {
+ /* Prevent multiple adds from adding the same change_cb at the same time. */
+ ao2_lock(statecbs);
- e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
- if (e) {
- if (found)
- *found = 1;
- if (matching_action) {
- ast_unlock_contexts();
- return -1; /* success, we found it */
- } else if (action == E_FINDLABEL) { /* map the label to a priority */
- res = e->priority;
- ast_unlock_contexts();
- return res; /* the priority we were looking for */
- } else { /* spawn */
- if (!e->cached_app)
- e->cached_app = pbx_findapp(e->app);
- app = e->cached_app;
- if (ast_strlen_zero(e->data)) {
- *passdata = '\0';
- } else {
- const char *tmp;
- if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
- /* no variables to substitute, copy on through */
- ast_copy_string(passdata, e->data, sizeof(passdata));
- } else {
- /* save e->data on stack for later processing after lock released */
- substitute = ast_strdupa(e->data);
- }
- }
- ast_unlock_contexts();
- if (!app) {
- ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
- return -1;
- }
- if (ast_channel_context(c) != context)
- ast_channel_context_set(c, context);
- if (ast_channel_exten(c) != exten)
- ast_channel_exten_set(c, exten);
- ast_channel_priority_set(c, priority);
- if (substitute) {
- pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1);
- }
- ast_debug(1, "Launching '%s'\n", app->name);
- if (VERBOSITY_ATLEAST(3)) {
- ast_verb(3, "Executing [%s@%s:%d] " COLORIZE_FMT "(\"" COLORIZE_FMT "\", \"" COLORIZE_FMT "\") %s\n",
- exten, context, priority,
- COLORIZE(COLOR_BRCYAN, 0, app->name),
- COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(c)),
- COLORIZE(COLOR_BRMAGENTA, 0, passdata),
- "in new stack");
- }
- return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
- }
- } else if (q.swo) { /* not found here, but in another switch */
- if (found)
- *found = 1;
- ast_unlock_contexts();
- if (matching_action) {
+ /* Remove any existing change_cb. */
+ ao2_find(statecbs, change_cb, OBJ_UNLINK | OBJ_NODATA);
+
+ /* Now insert the change_cb */
+ if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
+ ao2_unlock(statecbs);
return -1;
- } else {
- if (!q.swo->exec) {
- ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
- res = -1;
- }
- return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
- }
- } else { /* not found anywhere, see what happened */
- ast_unlock_contexts();
- /* Using S_OR here because Solaris doesn't like NULL being passed to ast_log */
- switch (q.status) {
- case STATUS_NO_CONTEXT:
- if (!matching_action && !combined_find_spawn)
- ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", S_OR(context, ""));
- break;
- case STATUS_NO_EXTENSION:
- if (!matching_action && !combined_find_spawn)
- ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, S_OR(context, ""));
- break;
- case STATUS_NO_PRIORITY:
- if (!matching_action && !combined_find_spawn)
- ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, S_OR(context, ""));
- break;
- case STATUS_NO_LABEL:
- if (context && !combined_find_spawn)
- ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, S_OR(context, ""));
- break;
- default:
- ast_debug(1, "Shouldn't happen!\n");
}
+ state_cb->id = 0;
+ state_cb->change_cb = change_cb;
+ state_cb->destroy_cb = destroy_cb;
+ state_cb->data = data;
+ state_cb->extended = extended;
+ ao2_link(statecbs, state_cb);
- return (matching_action) ? 0 : -1;
+ ao2_ref(state_cb, -1);
+ ao2_unlock(statecbs);
+ return 0;
}
-}
-/*! \brief Find hint for given extension in context */
-static struct ast_exten *ast_hint_extension_nolock(struct ast_channel *c, const char *context, const char *exten)
-{
- struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
- return pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
-}
-
-static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
-{
- struct ast_exten *e;
- ast_rdlock_contexts();
- e = ast_hint_extension_nolock(c, context, exten);
- ast_unlock_contexts();
- return e;
-}
+ if (!context || !exten)
+ return -1;
-enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devstate)
-{
- switch (devstate) {
- case AST_DEVICE_ONHOLD:
- return AST_EXTENSION_ONHOLD;
- case AST_DEVICE_BUSY:
- return AST_EXTENSION_BUSY;
- case AST_DEVICE_UNKNOWN:
- return AST_EXTENSION_NOT_INUSE;
- case AST_DEVICE_UNAVAILABLE:
- case AST_DEVICE_INVALID:
- return AST_EXTENSION_UNAVAILABLE;
- case AST_DEVICE_RINGINUSE:
- return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
- case AST_DEVICE_RINGING:
- return AST_EXTENSION_RINGING;
- case AST_DEVICE_INUSE:
- return AST_EXTENSION_INUSE;
- case AST_DEVICE_NOT_INUSE:
- return AST_EXTENSION_NOT_INUSE;
- case AST_DEVICE_TOTAL: /* not a device state, included for completeness */
- break;
+ /* This callback type is for only one hint, so get the hint */
+ e = ast_hint_extension(NULL, context, exten);
+ if (!e) {
+ return -1;
}
- return AST_EXTENSION_NOT_INUSE;
-}
+ /* If this is a pattern, dynamically create a new extension for this
+ * particular match. Note that this will only happen once for each
+ * individual extension, because the pattern will no longer match first.
+ */
+ if (e->exten[0] == '_') {
+ ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
+ e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
+ e->registrar);
+ e = ast_hint_extension(NULL, context, exten);
+ if (!e || e->exten[0] == '_') {
+ return -1;
+ }
+ }
-/*!
- * \internal
- * \brief Parse out the presence portion of the hint string
- */
-static char *parse_hint_presence(struct ast_str *hint_args)
-{
- char *copy = ast_strdupa(ast_str_buffer(hint_args));
- char *tmp = "";
+ /* Find the hint in the hints container */
+ ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
+ hint = ao2_find(hints, e, 0);
+ if (!hint) {
+ ao2_unlock(hints);
+ return -1;
+ }
- if ((tmp = strrchr(copy, ','))) {
- *tmp = '\0';
- tmp++;
- } else {
- return NULL;
+ /* Now insert the callback in the callback list */
+ if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
+ ao2_ref(hint, -1);
+ ao2_unlock(hints);
+ return -1;
}
- ast_str_set(&hint_args, 0, "%s", tmp);
- return ast_str_buffer(hint_args);
-}
+ do {
+ id = stateid++; /* Unique ID for this callback */
+ /* Do not allow id to ever be -1 or 0. */
+ } while (id == -1 || id == 0);
+ state_cb->id = id;
+ state_cb->change_cb = change_cb; /* Pointer to callback routine */
+ state_cb->destroy_cb = destroy_cb;
+ state_cb->data = data; /* Data for the callback */
+ state_cb->extended = extended;
+ ao2_link(hint->callbacks, state_cb);
-/*!
- * \internal
- * \brief Parse out the device portion of the hint string
- */
-static char *parse_hint_device(struct ast_str *hint_args)
-{
- char *copy = ast_strdupa(ast_str_buffer(hint_args));
- char *tmp;
+ ao2_ref(state_cb, -1);
+ ao2_ref(hint, -1);
+ ao2_unlock(hints);
- if ((tmp = strrchr(copy, ','))) {
- *tmp = '\0';
- }
+ return id;
+}
- ast_str_set(&hint_args, 0, "%s", copy);
- return ast_str_buffer(hint_args);
+int ast_extension_state_add_destroy(const char *context, const char *exten,
+ ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
+{
+ return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
}
-static void device_state_info_dt(void *obj)
+int ast_extension_state_add(const char *context, const char *exten,
+ ast_state_cb_type change_cb, void *data)
{
- struct ast_device_state_info *info = obj;
+ return extension_state_add_destroy(context, exten, change_cb, NULL, data, 0);
+}
- ao2_cleanup(info->causing_channel);
+int ast_extension_state_add_destroy_extended(const char *context, const char *exten,
+ ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
+{
+ return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 1);
}
-static struct ao2_container *alloc_device_state_info(void)
+int ast_extension_state_add_extended(const char *context, const char *exten,
+ ast_state_cb_type change_cb, void *data)
{
- return ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+ return extension_state_add_destroy(context, exten, change_cb, NULL, data, 1);
}
-static int ast_extension_state3(struct ast_str *hint_app, struct ao2_container *device_state_info)
+/*! \brief Find Hint by callback id */
+static int find_hint_by_cb_id(void *obj, void *arg, int flags)
{
- char *cur;
- char *rest;
- struct ast_devstate_aggregate agg;
+ struct ast_state_cb *state_cb;
+ const struct ast_hint *hint = obj;
+ int *id = arg;
- /* One or more devices separated with a & character */
- rest = parse_hint_device(hint_app);
+ if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
+ ao2_ref(state_cb, -1);
+ return CMP_MATCH | CMP_STOP;
+ }
- ast_devstate_aggregate_init(&agg);
- while ((cur = strsep(&rest, "&"))) {
- enum ast_device_state state = ast_device_state(cur);
+ return 0;
+}
- ast_devstate_aggregate_add(&agg, state);
- if (device_state_info) {
- struct ast_device_state_info *obj;
+int ast_extension_state_del(int id, ast_state_cb_type change_cb)
+{
+ struct ast_state_cb *p_cur;
+ int ret = -1;
- obj = ao2_alloc_options(sizeof(*obj) + strlen(cur), device_state_info_dt, AO2_ALLOC_OPT_LOCK_NOLOCK);
- /* if failed we cannot add this device */
- if (obj) {
- obj->device_state = state;
- strcpy(obj->device_name, cur);
- ao2_link(device_state_info, obj);
- ao2_ref(obj, -1);
+ if (!id) { /* id == 0 is a callback without extension */
+ if (!change_cb) {
+ return ret;
+ }
+ p_cur = ao2_find(statecbs, change_cb, OBJ_UNLINK);
+ if (p_cur) {
+ ret = 0;
+ ao2_ref(p_cur, -1);
+ }
+ } else { /* callback with extension, find the callback based on ID */
+ struct ast_hint *hint;
+
+ ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
+ hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
+ if (hint) {
+ p_cur = ao2_find(hint->callbacks, &id, OBJ_UNLINK);
+ if (p_cur) {
+ ret = 0;
+ ao2_ref(p_cur, -1);
}
+ ao2_ref(hint, -1);
}
+ ao2_unlock(hints);
}
- return ast_devstate_to_extenstate(ast_devstate_aggregate_result(&agg));
+ return ret;
}
-/*! \brief Check state of extension by using hints */
-static int ast_extension_state2(struct ast_exten *e, struct ao2_container *device_state_info)
+static int hint_id_cmp(void *obj, void *arg, int flags)
{
- struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
+ const struct ast_state_cb *cb = obj;
+ int *id = arg;
- if (!e || !hint_app) {
- return -1;
- }
-
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(e));
- return ast_extension_state3(hint_app, device_state_info);
-}
-
-/*! \brief Return extension_state as string */
-const char *ast_extension_state2str(int extension_state)
-{
- int i;
-
- for (i = 0; (i < ARRAY_LEN(extension_states)); i++) {
- if (extension_states[i].extension_state == extension_state)
- return extension_states[i].text;
- }
- return "Unknown";
+ return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
}
/*!
* \internal
- * \brief Check extension state for an extension by using hint
+ * \brief Destroy the given hint object.
+ *
+ * \param obj Hint to destroy.
+ *
+ * \return Nothing
*/
-static int internal_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
- struct ao2_container *device_state_info)
+static void destroy_hint(void *obj)
{
- struct ast_exten *e;
+ struct ast_hint *hint = obj;
+ int i;
- if (!(e = ast_hint_extension(c, context, exten))) { /* Do we have a hint for this extension ? */
- return -1; /* No hint, return -1 */
- }
+ if (hint->callbacks) {
+ struct ast_state_cb *state_cb;
+ const char *context_name;
+ const char *exten_name;
- if (e->exten[0] == '_') {
- /* Create this hint on-the-fly */
- ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
- e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
- e->registrar);
- if (!(e = ast_hint_extension(c, context, exten))) {
- /* Improbable, but not impossible */
- return -1;
+ if (hint->exten) {
+ context_name = ast_get_context_name(ast_get_extension_context(hint->exten));
+ exten_name = ast_get_extension_name(hint->exten);
+ hint->exten = NULL;
+ } else {
+ /* The extension has already been destroyed */
+ context_name = hint->context_name;
+ exten_name = hint->exten_name;
+ }
+ hint->laststate = AST_EXTENSION_DEACTIVATED;
+ while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
+ /* Notify with -1 and remove all callbacks */
+ execute_state_callback(state_cb->change_cb,
+ context_name,
+ exten_name,
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint,
+ NULL);
+ ao2_ref(state_cb, -1);
}
+ ao2_ref(hint->callbacks, -1);
}
- return ast_extension_state2(e, device_state_info); /* Check all devices in the hint */
-}
-
-/*! \brief Check extension state for an extension by using hint */
-int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
-{
- return internal_extension_state_extended(c, context, exten, NULL);
+ for (i = 0; i < AST_VECTOR_SIZE(&hint->devices); i++) {
+ char *device = AST_VECTOR_GET(&hint->devices, i);
+ ast_free(device);
+ }
+ AST_VECTOR_FREE(&hint->devices);
+ ast_free(hint->last_presence_subtype);
+ ast_free(hint->last_presence_message);
}
-/*! \brief Check extended extension state for an extension by using hint */
-int ast_extension_state_extended(struct ast_channel *c, const char *context, const char *exten,
- struct ao2_container **device_state_info)
+/*! \brief Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
{
- struct ao2_container *container = NULL;
- int ret;
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *hint;
- if (device_state_info) {
- container = alloc_device_state_info();
+ if (!e) {
+ return -1;
}
- ret = internal_extension_state_extended(c, context, exten, container);
- if (ret < 0 && container) {
- ao2_ref(container, -1);
- container = NULL;
+ hint = ao2_find(hints, e, OBJ_UNLINK);
+ if (!hint) {
+ return -1;
}
- if (device_state_info) {
- get_device_state_causing_channels(container);
- *device_state_info = container;
- }
+ remove_hintdevice(hint);
- return ret;
+ /*
+ * The extension is being destroyed so we must save some
+ * information to notify that the extension is deactivated.
+ */
+ ao2_lock(hint);
+ ast_copy_string(hint->context_name,
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ sizeof(hint->context_name));
+ ast_copy_string(hint->exten_name, ast_get_extension_name(hint->exten),
+ sizeof(hint->exten_name));
+ hint->exten = NULL;
+ ao2_unlock(hint);
+
+ ao2_ref(hint, -1);
+
+ return 0;
}
-static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
+/*! \brief Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
{
- struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
- char *presence_provider;
- const char *app;
+ struct ast_hint *hint_new;
+ struct ast_hint *hint_found;
+ char *message = NULL;
+ char *subtype = NULL;
+ int presence_state;
- if (!e || !hint_app) {
+ if (!e) {
return -1;
}
- app = ast_get_extension_app(e);
- if (ast_strlen_zero(app)) {
+ /*
+ * We must create the hint we wish to add before determining if
+ * it is already in the hints container to avoid possible
+ * deadlock when getting the current extension state.
+ */
+ hint_new = ao2_alloc(sizeof(*hint_new), destroy_hint);
+ if (!hint_new) {
return -1;
}
+ AST_VECTOR_INIT(&hint_new->devices, 8);
- ast_str_set(&hint_app, 0, "%s", app);
- presence_provider = parse_hint_presence(hint_app);
-
- if (ast_strlen_zero(presence_provider)) {
- /* No presence string in the hint */
- return 0;
+ /* Initialize new hint. */
+ hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
+ if (!hint_new->callbacks) {
+ ao2_ref(hint_new, -1);
+ return -1;
+ }
+ hint_new->exten = e;
+ if (strstr(e->app, "${") && e->exten[0] == '_') {
+ /* The hint is dynamic and hasn't been evaluted yet */
+ hint_new->laststate = AST_DEVICE_INVALID;
+ hint_new->last_presence_state = AST_PRESENCE_INVALID;
+ } else {
+ hint_new->laststate = ast_extension_state2(e, NULL);
+ if ((presence_state = extension_presence_state_helper(e, &subtype, &message)) > 0) {
+ hint_new->last_presence_state = presence_state;
+ hint_new->last_presence_subtype = subtype;
+ hint_new->last_presence_message = message;
+ }
}
- return ast_presence_state(presence_provider, subtype, message);
-}
+ /* Prevent multiple add hints from adding the same hint at the same time. */
+ ao2_lock(hints);
-int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
-{
- struct ast_exten *e;
+ /* Search if hint exists, do nothing */
+ hint_found = ao2_find(hints, e, 0);
+ if (hint_found) {
+ ao2_ref(hint_found, -1);
+ ao2_unlock(hints);
+ ao2_ref(hint_new, -1);
+ ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n",
+ ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
- if (!(e = ast_hint_extension(c, context, exten))) { /* Do we have a hint for this extension ? */
- return -1; /* No hint, return -1 */
+ /* Add new hint to the hints container */
+ ast_debug(2, "HINTS: Adding hint %s: %s\n",
+ ast_get_extension_name(e), ast_get_extension_app(e));
+ ao2_link(hints, hint_new);
+ if (add_hintdevice(hint_new, ast_get_extension_app(e))) {
+ ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
+ ast_get_extension_name(e),
+ ast_get_context_name(ast_get_extension_context(e)));
}
- if (e->exten[0] == '_') {
- /* Create this hint on-the-fly */
- ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
- e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
- e->registrar);
- if (!(e = ast_hint_extension(c, context, exten))) {
- /* Improbable, but not impossible */
- return -1;
+ /* if not dynamic */
+ if (!(strstr(e->app, "${") && e->exten[0] == '_')) {
+ struct ast_state_cb *state_cb;
+ struct ao2_iterator cb_iter;
+
+ /* For general callbacks */
+ cb_iter = ao2_iterator_init(statecbs, 0);
+ for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
+ execute_state_callback(state_cb->change_cb,
+ ast_get_context_name(ast_get_extension_context(e)),
+ ast_get_extension_name(e),
+ state_cb->data,
+ AST_HINT_UPDATE_DEVICE,
+ hint_new,
+ NULL);
}
+ ao2_iterator_destroy(&cb_iter);
}
+ ao2_unlock(hints);
+ ao2_ref(hint_new, -1);
- return extension_presence_state_helper(e, subtype, message);
+ return 0;
}
-static int execute_state_callback(ast_state_cb_type cb,
- const char *context,
- const char *exten,
- void *data,
- enum ast_state_cb_update_reason reason,
- struct ast_hint *hint,
- struct ao2_container *device_state_info)
+/*! \brief Publish a hint changed event */
+static int publish_hint_change(struct ast_hint *hint, struct ast_exten *ne)
{
- int res = 0;
- struct ast_state_cb_info info = { 0, };
+ struct stasis_message *message;
- info.reason = reason;
+ if (!hint_change_message_type()) {
+ return -1;
+ }
- /* Copy over current hint data */
- if (hint) {
- ao2_lock(hint);
- info.exten_state = hint->laststate;
- info.device_state_info = device_state_info;
- info.presence_state = hint->last_presence_state;
- if (!(ast_strlen_zero(hint->last_presence_subtype))) {
- info.presence_subtype = ast_strdupa(hint->last_presence_subtype);
- } else {
- info.presence_subtype = "";
- }
- if (!(ast_strlen_zero(hint->last_presence_message))) {
- info.presence_message = ast_strdupa(hint->last_presence_message);
- } else {
- info.presence_message = "";
- }
- ao2_unlock(hint);
- } else {
- info.exten_state = AST_EXTENSION_REMOVED;
+ /* The message is going to be published to two topics so the hint needs two refs */
+ if (!(message = stasis_message_create(hint_change_message_type(), ao2_bump(hint)))) {
+ ao2_ref(hint, -1);
+ return -1;
}
- /* NOTE: The casts will not be needed for v10 and later */
- res = cb((char *) context, (char *) exten, &info, data);
+ stasis_publish(ast_device_state_topic_all(), message);
+ stasis_publish(ast_presence_state_topic_all(), message);
- return res;
+ ao2_ref(message, -1);
+
+ return 0;
}
-/*!
- * /internal
- * /brief Identify a channel for every device which is supposedly responsible for the device state.
- *
- * Especially when the device is ringing, the oldest ringing channel is chosen.
- * For all other cases the first encountered channel in the specific state is chosen.
- */
-static void get_device_state_causing_channels(struct ao2_container *c)
-{
- struct ao2_iterator iter;
- struct ast_device_state_info *info;
- struct ast_channel *chan;
-
- if (!c || !ao2_container_count(c)) {
- return;
- }
- iter = ao2_iterator_init(c, 0);
- for (; (info = ao2_iterator_next(&iter)); ao2_ref(info, -1)) {
- enum ast_channel_state search_state = 0; /* prevent false uninit warning */
- char match[AST_CHANNEL_NAME];
- struct ast_channel_iterator *chan_iter;
- struct timeval chantime = {0, }; /* prevent false uninit warning */
-
- switch (info->device_state) {
- case AST_DEVICE_RINGING:
- case AST_DEVICE_RINGINUSE:
- /* find ringing channel */
- search_state = AST_STATE_RINGING;
- break;
- case AST_DEVICE_BUSY:
- /* find busy channel */
- search_state = AST_STATE_BUSY;
- break;
- case AST_DEVICE_ONHOLD:
- case AST_DEVICE_INUSE:
- /* find up channel */
- search_state = AST_STATE_UP;
- break;
- case AST_DEVICE_UNKNOWN:
- case AST_DEVICE_NOT_INUSE:
- case AST_DEVICE_INVALID:
- case AST_DEVICE_UNAVAILABLE:
- case AST_DEVICE_TOTAL /* not a state */:
- /* no channels are of interest */
- continue;
- }
-
- /* iterate over all channels of the device */
- snprintf(match, sizeof(match), "%s-", info->device_name);
- chan_iter = ast_channel_iterator_by_name_new(match, strlen(match));
- for (; (chan = ast_channel_iterator_next(chan_iter)); ast_channel_unref(chan)) {
- ast_channel_lock(chan);
- /* this channel's state doesn't match */
- if (search_state != ast_channel_state(chan)) {
- ast_channel_unlock(chan);
- continue;
- }
- /* any non-ringing channel will fit */
- if (search_state != AST_STATE_RINGING) {
- ast_channel_unlock(chan);
- info->causing_channel = chan; /* is kept ref'd! */
- break;
- }
- /* but we need the oldest ringing channel of the device to match with undirected pickup */
- if (!info->causing_channel) {
- chantime = ast_channel_creationtime(chan);
- ast_channel_ref(chan); /* must ref it! */
- info->causing_channel = chan;
- } else if (ast_tvcmp(ast_channel_creationtime(chan), chantime) < 0) {
- chantime = ast_channel_creationtime(chan);
- ast_channel_unref(info->causing_channel);
- ast_channel_ref(chan); /* must ref it! */
- info->causing_channel = chan;
- }
- ast_channel_unlock(chan);
- }
- ast_channel_iterator_destroy(chan_iter);
- }
- ao2_iterator_destroy(&iter);
-}
-
-static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
+/*! \brief Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
{
- struct ast_device_state_message *dev_state;
struct ast_hint *hint;
- struct ast_str *hint_app;
- struct ast_hintdevice *device;
- struct ast_hintdevice *cmpdevice;
- struct ao2_iterator *dev_iter;
- struct ao2_iterator cb_iter;
- char context_name[AST_MAX_CONTEXT];
- char exten_name[AST_MAX_EXTENSION];
- if (ast_device_state_message_type() != stasis_message_type(msg)) {
- return;
+ if (!oe || !ne) {
+ return -1;
}
- dev_state = stasis_message_data(msg);
- if (dev_state->eid) {
- /* ignore non-aggregate states */
- return;
- }
+ ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
- if (ao2_container_count(hintdevices) == 0) {
- /* There are no hints monitoring devices. */
- return;
+ /*
+ * Unlink the hint from the hints container as the extension
+ * name (which is the hash value) could change.
+ */
+ hint = ao2_find(hints, oe, OBJ_UNLINK);
+ if (!hint) {
+ ao2_unlock(hints);
+ ast_mutex_unlock(&context_merge_lock);
+ return -1;
}
- hint_app = ast_str_create(1024);
- if (!hint_app) {
- return;
- }
+ remove_hintdevice(hint);
- cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(dev_state->device));
- strcpy(cmpdevice->hintdevice, dev_state->device);
+ /* Update the hint and put it back in the hints container. */
+ ao2_lock(hint);
+ hint->exten = ne;
- ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
- dev_iter = ao2_t_callback(hintdevices,
- OBJ_POINTER | OBJ_MULTIPLE,
- hintdevice_cmp_multiple,
- cmpdevice,
- "find devices in container");
- if (!dev_iter) {
- ast_mutex_unlock(&context_merge_lock);
- ast_free(hint_app);
- return;
+ ao2_unlock(hint);
+
+ ao2_link(hints, hint);
+ if (add_hintdevice(hint, ast_get_extension_app(ne))) {
+ ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
+ ast_get_extension_name(ne),
+ ast_get_context_name(ast_get_extension_context(ne)));
}
+ ao2_unlock(hints);
- for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
- struct ast_state_cb *state_cb;
- int state;
- int same_state;
- struct ao2_container *device_state_info;
- int first_extended_cb_call = 1;
+ publish_hint_change(hint, ne);
- if (!device->hint) {
- /* Should never happen. */
- continue;
- }
- hint = device->hint;
+ ao2_ref(hint, -1);
- ao2_lock(hint);
- if (!hint->exten) {
- /* The extension has already been destroyed */
- ao2_unlock(hint);
- continue;
- }
+ return 0;
+}
- /*
- * Save off strings in case the hint extension gets destroyed
- * while we are notifying the watchers.
- */
- ast_copy_string(context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(context_name));
- ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
- sizeof(exten_name));
- ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
- ao2_unlock(hint);
+/*! \brief Get hint for channel */
+int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e = ast_hint_extension(c, context, exten);
- /*
- * Get device state for this hint.
- *
- * NOTE: We cannot hold any locks while determining the hint
- * device state or notifying the watchers without causing a
- * deadlock. (conlock, hints, and hint)
- */
- /* Make a container so state3 can fill it if we wish.
- * If that failed we simply do not provide the extended state info.
- */
- device_state_info = alloc_device_state_info();
- state = ast_extension_state3(hint_app, device_state_info);
- if ((same_state = state == hint->laststate) && (~state & AST_EXTENSION_RINGING)) {
- ao2_cleanup(device_state_info);
- continue;
+ if (e) {
+ if (hint)
+ ast_copy_string(hint, ast_get_extension_app(e), hintsize);
+ if (name) {
+ const char *tmp = ast_get_extension_app_data(e);
+ if (tmp)
+ ast_copy_string(name, tmp, namesize);
}
+ return -1;
+ }
+ return 0;
+}
- /* Device state changed since last check - notify the watchers. */
- hint->laststate = state; /* record we saw the change */
+/*! \brief Get hint for channel */
+int ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e = ast_hint_extension(c, context, exten);
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; !same_state && (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
+ if (!e) {
+ return 0;
+ }
- /* For extension callbacks */
- /* extended callbacks are called when the state changed or when AST_STATE_RINGING is
- * included. Normal callbacks are only called when the state changed.
- */
- cb_iter = ao2_iterator_init(hint->callbacks, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- if (state_cb->extended && first_extended_cb_call) {
- /* Fill detailed device_state_info now that we know it is used by extd. callback */
- first_extended_cb_call = 0;
- get_device_state_causing_channels(device_state_info);
- }
- if (state_cb->extended || !same_state) {
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- state_cb->extended ? device_state_info : NULL);
- }
+ if (hint) {
+ ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
+ }
+ if (name) {
+ const char *tmp = ast_get_extension_app_data(e);
+ if (tmp) {
+ ast_str_set(name, namesize, "%s", tmp);
}
- ao2_iterator_destroy(&cb_iter);
-
- ao2_cleanup(device_state_info);
}
- ast_mutex_unlock(&context_merge_lock);
+ return -1;
+}
- ao2_iterator_destroy(dev_iter);
- ast_free(hint_app);
- return;
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
}
-/*!
- * \internal
- * \brief Destroy the given state callback object.
- *
- * \param doomed State callback to destroy.
- *
- * \return Nothing
- */
-static void destroy_state_cb(void *doomed)
+int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
{
- struct ast_state_cb *state_cb = doomed;
+ return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
+}
- if (state_cb->destroy_cb) {
- state_cb->destroy_cb(state_cb->id, state_cb->data);
- }
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
}
-/*!
- * \internal
- * \brief Add watcher for extension states with destructor
- */
-static int extension_state_add_destroy(const char *context, const char *exten,
- ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data, int extended)
+int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
{
- struct ast_hint *hint;
- struct ast_state_cb *state_cb;
- struct ast_exten *e;
- int id;
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
+}
- /* If there's no context and extension: add callback to statecbs list */
- if (!context && !exten) {
- /* Prevent multiple adds from adding the same change_cb at the same time. */
- ao2_lock(statecbs);
+int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
+}
- /* Remove any existing change_cb. */
- ao2_find(statecbs, change_cb, OBJ_UNLINK | OBJ_NODATA);
-
- /* Now insert the change_cb */
- if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
- ao2_unlock(statecbs);
- return -1;
- }
- state_cb->id = 0;
- state_cb->change_cb = change_cb;
- state_cb->destroy_cb = destroy_cb;
- state_cb->data = data;
- state_cb->extended = extended;
- ao2_link(statecbs, state_cb);
-
- ao2_ref(state_cb, -1);
- ao2_unlock(statecbs);
- return 0;
- }
+int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
+}
- if (!context || !exten)
- return -1;
+void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
+{
+ int autoloopflag;
+ int found;
+ int spawn_error;
- /* This callback type is for only one hint, so get the hint */
- e = ast_hint_extension(NULL, context, exten);
- if (!e) {
- return -1;
- }
+ ast_channel_lock(chan);
- /* If this is a pattern, dynamically create a new extension for this
- * particular match. Note that this will only happen once for each
- * individual extension, because the pattern will no longer match first.
+ /*
+ * Make sure that the channel is marked as hungup since we are
+ * going to run the h exten on it.
*/
- if (e->exten[0] == '_') {
- ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
- e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
- e->registrar);
- e = ast_hint_extension(NULL, context, exten);
- if (!e || e->exten[0] == '_') {
- return -1;
- }
- }
-
- /* Find the hint in the hints container */
- ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
- hint = ao2_find(hints, e, 0);
- if (!hint) {
- ao2_unlock(hints);
- return -1;
- }
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
- /* Now insert the callback in the callback list */
- if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
- ao2_ref(hint, -1);
- ao2_unlock(hints);
- return -1;
+ /* Set h exten location */
+ if (context != ast_channel_context(chan)) {
+ ast_channel_context_set(chan, context);
}
- do {
- id = stateid++; /* Unique ID for this callback */
- /* Do not allow id to ever be -1 or 0. */
- } while (id == -1 || id == 0);
- state_cb->id = id;
- state_cb->change_cb = change_cb; /* Pointer to callback routine */
- state_cb->destroy_cb = destroy_cb;
- state_cb->data = data; /* Data for the callback */
- state_cb->extended = extended;
- ao2_link(hint->callbacks, state_cb);
+ ast_channel_exten_set(chan, "h");
+ ast_channel_priority_set(chan, 1);
- ao2_ref(state_cb, -1);
- ao2_ref(hint, -1);
- ao2_unlock(hints);
+ /* Save autoloop flag */
+ autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+ ast_channel_unlock(chan);
- return id;
-}
+ for (;;) {
+ spawn_error = ast_spawn_extension(chan, ast_channel_context(chan),
+ ast_channel_exten(chan), ast_channel_priority(chan),
+ S_COR(ast_channel_caller(chan)->id.number.valid,
+ ast_channel_caller(chan)->id.number.str, NULL), &found, 1);
-/*! \brief Add watcher for extension states with destructor */
-int ast_extension_state_add_destroy(const char *context, const char *exten,
- ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
-{
- return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 0);
-}
+ ast_channel_lock(chan);
+ if (spawn_error) {
+ /* The code after the loop needs the channel locked. */
+ break;
+ }
+ ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
+ ast_channel_unlock(chan);
+ }
+ if (found && spawn_error) {
+ /* Something bad happened, or a hangup has been requested. */
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n",
+ ast_channel_context(chan), ast_channel_exten(chan),
+ ast_channel_priority(chan), ast_channel_name(chan));
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
+ ast_channel_context(chan), ast_channel_exten(chan),
+ ast_channel_priority(chan), ast_channel_name(chan));
+ }
-/*! \brief Add watcher for extension states */
-int ast_extension_state_add(const char *context, const char *exten,
- ast_state_cb_type change_cb, void *data)
-{
- return extension_state_add_destroy(context, exten, change_cb, NULL, data, 0);
-}
+ /* An "h" exten has been run, so indicate that one has been run. */
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
-/*! \brief Add watcher for extended extension states with destructor */
-int ast_extension_state_add_destroy_extended(const char *context, const char *exten,
- ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
-{
- return extension_state_add_destroy(context, exten, change_cb, destroy_cb, data, 1);
+ /* Restore autoloop flag */
+ ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
+ ast_channel_unlock(chan);
}
-/*! \brief Add watcher for extended extension states */
-int ast_extension_state_add_extended(const char *context, const char *exten,
- ast_state_cb_type change_cb, void *data)
+/*! helper function to set extension and priority */
+void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
{
- return extension_state_add_destroy(context, exten, change_cb, NULL, data, 1);
+ ast_channel_lock(c);
+ ast_channel_exten_set(c, exten);
+ ast_channel_priority_set(c, pri);
+ ast_channel_unlock(c);
}
-/*! \brief Find Hint by callback id */
-static int find_hint_by_cb_id(void *obj, void *arg, int flags)
+/*!
+ * \brief collect digits from the channel into the buffer.
+ * \param c, buf, buflen, pos
+ * \param waittime is in milliseconds
+ * \retval 0 on timeout or done.
+ * \retval -1 on error.
+*/
+static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
{
- struct ast_state_cb *state_cb;
- const struct ast_hint *hint = obj;
- int *id = arg;
+ int digit;
- if ((state_cb = ao2_find(hint->callbacks, id, 0))) {
- ao2_ref(state_cb, -1);
- return CMP_MATCH | CMP_STOP;
+ buf[pos] = '\0'; /* make sure it is properly terminated */
+ while (ast_matchmore_extension(c, ast_channel_context(c), buf, 1,
+ S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
+ /* As long as we're willing to wait, and as long as it's not defined,
+ keep reading digits until we can't possibly get a right answer anymore. */
+ digit = ast_waitfordigit(c, waittime);
+ if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
+ ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
+ } else {
+ if (!digit) /* No entry */
+ break;
+ if (digit < 0) /* Error, maybe a hangup */
+ return -1;
+ if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
+ buf[pos++] = digit;
+ buf[pos] = '\0';
+ }
+ waittime = ast_channel_pbx(c)->dtimeoutms;
+ }
}
-
return 0;
}
-/*! \brief ast_extension_state_del: Remove a watcher from the callback list */
-int ast_extension_state_del(int id, ast_state_cb_type change_cb)
+static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
+ struct ast_pbx_args *args)
{
- struct ast_state_cb *p_cur;
- int ret = -1;
+ int found = 0; /* set if we find at least one match */
+ int res = 0;
+ int autoloopflag;
+ int error = 0; /* set an error conditions */
+ struct ast_pbx *pbx;
+ ast_callid callid;
- if (!id) { /* id == 0 is a callback without extension */
- if (!change_cb) {
- return ret;
- }
- p_cur = ao2_find(statecbs, change_cb, OBJ_UNLINK);
- if (p_cur) {
- ret = 0;
- ao2_ref(p_cur, -1);
- }
- } else { /* callback with extension, find the callback based on ID */
- struct ast_hint *hint;
+ /* A little initial setup here */
+ if (ast_channel_pbx(c)) {
+ ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
+ /* XXX and now what ? */
+ ast_free(ast_channel_pbx(c));
+ }
+ if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
+ return AST_PBX_FAILED;
+ }
- ao2_lock(hints);/* Locked to hold off ast_merge_contexts_and_delete */
- hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
- if (hint) {
- p_cur = ao2_find(hint->callbacks, &id, OBJ_UNLINK);
- if (p_cur) {
- ret = 0;
- ao2_ref(p_cur, -1);
+ callid = ast_read_threadstorage_callid();
+ /* If the thread isn't already associated with a callid, we should create that association. */
+ if (!callid) {
+ /* Associate new PBX thread with the channel call id if it is availble.
+ * If not, create a new one instead.
+ */
+ callid = ast_channel_callid(c);
+ if (!callid) {
+ callid = ast_create_callid();
+ if (callid) {
+ ast_channel_lock(c);
+ ast_channel_callid_set(c, callid);
+ ast_channel_unlock(c);
}
- ao2_ref(hint, -1);
}
- ao2_unlock(hints);
+ ast_callid_threadassoc_add(callid);
+ callid = 0;
}
- return ret;
-}
+ ast_channel_pbx_set(c, pbx);
+ /* Set reasonable defaults */
+ ast_channel_pbx(c)->rtimeoutms = 10000;
+ ast_channel_pbx(c)->dtimeoutms = 5000;
-static int hint_id_cmp(void *obj, void *arg, int flags)
-{
- const struct ast_state_cb *cb = obj;
- int *id = arg;
+ ast_channel_lock(c);
+ autoloopflag = ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
+ ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);
+ ast_channel_unlock(c);
- return (cb->id == *id) ? CMP_MATCH | CMP_STOP : 0;
-}
+ if (ast_strlen_zero(ast_channel_exten(c))) {
+ /* If not successful fall back to 's' - but only if there is no given exten */
+ ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
+ /* XXX the original code used the existing priority in the call to
+ * ast_exists_extension(), and reset it to 1 afterwards.
+ * I believe the correct thing is to set it to 1 immediately.
+ */
+ set_ext_pri(c, "s", 1);
+ }
-/*!
- * \internal
- * \brief Destroy the given hint object.
- *
- * \param obj Hint to destroy.
- *
- * \return Nothing
- */
-static void destroy_hint(void *obj)
-{
- struct ast_hint *hint = obj;
-
- if (hint->callbacks) {
- struct ast_state_cb *state_cb;
- const char *context_name;
- const char *exten_name;
-
- if (hint->exten) {
- context_name = ast_get_context_name(ast_get_extension_context(hint->exten));
- exten_name = ast_get_extension_name(hint->exten);
- hint->exten = NULL;
- } else {
- /* The extension has already been destroyed */
- context_name = hint->context_name;
- exten_name = hint->exten_name;
- }
- hint->laststate = AST_EXTENSION_DEACTIVATED;
- while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
- /* Notify with -1 and remove all callbacks */
- execute_state_callback(state_cb->change_cb,
- context_name,
- exten_name,
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint,
- NULL);
- ao2_ref(state_cb, -1);
- }
- ao2_ref(hint->callbacks, -1);
- }
- ast_free(hint->last_presence_subtype);
- ast_free(hint->last_presence_message);
-}
-
-/*! \brief Remove hint from extension */
-static int ast_remove_hint(struct ast_exten *e)
-{
- /* Cleanup the Notifys if hint is removed */
- struct ast_hint *hint;
-
- if (!e) {
- return -1;
- }
-
- hint = ao2_find(hints, e, OBJ_UNLINK);
- if (!hint) {
- return -1;
- }
-
- remove_hintdevice(hint);
-
- /*
- * The extension is being destroyed so we must save some
- * information to notify that the extension is deactivated.
- */
- ao2_lock(hint);
- ast_copy_string(hint->context_name,
- ast_get_context_name(ast_get_extension_context(hint->exten)),
- sizeof(hint->context_name));
- ast_copy_string(hint->exten_name, ast_get_extension_name(hint->exten),
- sizeof(hint->exten_name));
- hint->exten = NULL;
- ao2_unlock(hint);
-
- ao2_ref(hint, -1);
-
- return 0;
-}
-
-/*! \brief Add hint to hint list, check initial extension state */
-static int ast_add_hint(struct ast_exten *e)
-{
- struct ast_hint *hint_new;
- struct ast_hint *hint_found;
- char *message = NULL;
- char *subtype = NULL;
- int presence_state;
-
- if (!e) {
- return -1;
- }
-
- /*
- * We must create the hint we wish to add before determining if
- * it is already in the hints container to avoid possible
- * deadlock when getting the current extension state.
- */
- hint_new = ao2_alloc(sizeof(*hint_new), destroy_hint);
- if (!hint_new) {
- return -1;
- }
-
- /* Initialize new hint. */
- hint_new->callbacks = ao2_container_alloc(1, NULL, hint_id_cmp);
- if (!hint_new->callbacks) {
- ao2_ref(hint_new, -1);
- return -1;
- }
- hint_new->exten = e;
- if (strstr(e->app, "${") && e->exten[0] == '_') {
- /* The hint is dynamic and hasn't been evaluted yet */
- hint_new->laststate = AST_DEVICE_INVALID;
- hint_new->last_presence_state = AST_PRESENCE_INVALID;
- } else {
- hint_new->laststate = ast_extension_state2(e, NULL);
- if ((presence_state = extension_presence_state_helper(e, &subtype, &message)) > 0) {
- hint_new->last_presence_state = presence_state;
- hint_new->last_presence_subtype = subtype;
- hint_new->last_presence_message = message;
- message = subtype = NULL;
- }
- }
-
- /* Prevent multiple add hints from adding the same hint at the same time. */
- ao2_lock(hints);
-
- /* Search if hint exists, do nothing */
- hint_found = ao2_find(hints, e, 0);
- if (hint_found) {
- ao2_ref(hint_found, -1);
- ao2_unlock(hints);
- ao2_ref(hint_new, -1);
- ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n",
- ast_get_extension_name(e), ast_get_extension_app(e));
- return -1;
- }
-
- /* Add new hint to the hints container */
- ast_debug(2, "HINTS: Adding hint %s: %s\n",
- ast_get_extension_name(e), ast_get_extension_app(e));
- ao2_link(hints, hint_new);
- if (add_hintdevice(hint_new, ast_get_extension_app(e))) {
- ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
- ast_get_extension_name(e),
- ast_get_context_name(ast_get_extension_context(e)));
- }
-
- /* if not dynamic */
- if (!(strstr(e->app, "${") && e->exten[0] == '_')) {
- struct ast_state_cb *state_cb;
- struct ao2_iterator cb_iter;
-
- /* For general callbacks */
- cb_iter = ao2_iterator_init(statecbs, 0);
- for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
- execute_state_callback(state_cb->change_cb,
- ast_get_context_name(ast_get_extension_context(e)),
- ast_get_extension_name(e),
- state_cb->data,
- AST_HINT_UPDATE_DEVICE,
- hint_new,
- NULL);
- }
- ao2_iterator_destroy(&cb_iter);
- }
- ao2_unlock(hints);
- ao2_ref(hint_new, -1);
-
- return 0;
-}
-
-/*! \brief Change hint for an extension */
-static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
-{
- struct ast_hint *hint;
-
- if (!oe || !ne) {
- return -1;
- }
-
- ao2_lock(hints);/* Locked to hold off others while we move the hint around. */
-
- /*
- * Unlink the hint from the hints container as the extension
- * name (which is the hash value) could change.
- */
- hint = ao2_find(hints, oe, OBJ_UNLINK);
- if (!hint) {
- ao2_unlock(hints);
- return -1;
- }
-
- remove_hintdevice(hint);
-
- /* Update the hint and put it back in the hints container. */
- ao2_lock(hint);
- hint->exten = ne;
- ao2_unlock(hint);
- ao2_link(hints, hint);
- if (add_hintdevice(hint, ast_get_extension_app(ne))) {
- ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
- ast_get_extension_name(ne),
- ast_get_context_name(ast_get_extension_context(ne)));
- }
-
- ao2_unlock(hints);
- ao2_ref(hint, -1);
-
- return 0;
-}
-
-
-/*! \brief Get hint for channel */
-int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
-{
- struct ast_exten *e = ast_hint_extension(c, context, exten);
-
- if (e) {
- if (hint)
- ast_copy_string(hint, ast_get_extension_app(e), hintsize);
- if (name) {
- const char *tmp = ast_get_extension_app_data(e);
- if (tmp)
- ast_copy_string(name, tmp, namesize);
- }
- return -1;
- }
- return 0;
-}
-
-/*! \brief Get hint for channel */
-int ast_str_get_hint(struct ast_str **hint, ssize_t hintsize, struct ast_str **name, ssize_t namesize, struct ast_channel *c, const char *context, const char *exten)
-{
- struct ast_exten *e = ast_hint_extension(c, context, exten);
-
- if (!e) {
- return 0;
- }
-
- if (hint) {
- ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
- }
- if (name) {
- const char *tmp = ast_get_extension_app_data(e);
- if (tmp) {
- ast_str_set(name, namesize, "%s", tmp);
- }
- }
- return -1;
-}
-
-int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
- return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
-}
-
-int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
-{
- return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
-}
-
-int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
-{
- return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
-}
-
-int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
- return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
-}
-
-int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
-{
- return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
-}
-
-int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
-{
- return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
-}
-
-void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
-{
- int autoloopflag;
- int found;
- int spawn_error;
-
- ast_channel_lock(chan);
-
- /*
- * Make sure that the channel is marked as hungup since we are
- * going to run the h exten on it.
- */
- ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
-
- /* Set h exten location */
- if (context != ast_channel_context(chan)) {
- ast_channel_context_set(chan, context);
- }
- ast_channel_exten_set(chan, "h");
- ast_channel_priority_set(chan, 1);
-
- /* Save autoloop flag */
- autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- ast_channel_unlock(chan);
-
- for (;;) {
- spawn_error = ast_spawn_extension(chan, ast_channel_context(chan),
- ast_channel_exten(chan), ast_channel_priority(chan),
- S_COR(ast_channel_caller(chan)->id.number.valid,
- ast_channel_caller(chan)->id.number.str, NULL), &found, 1);
-
- ast_channel_lock(chan);
- if (spawn_error) {
- /* The code after the loop needs the channel locked. */
- break;
- }
- ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
- ast_channel_unlock(chan);
- }
- if (found && spawn_error) {
- /* Something bad happened, or a hangup has been requested. */
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n",
- ast_channel_context(chan), ast_channel_exten(chan),
- ast_channel_priority(chan), ast_channel_name(chan));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
- ast_channel_context(chan), ast_channel_exten(chan),
- ast_channel_priority(chan), ast_channel_name(chan));
- }
-
- /* An "h" exten has been run, so indicate that one has been run. */
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
-
- /* Restore autoloop flag */
- ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
- ast_channel_unlock(chan);
-}
-
-/*!
- * \internal
- * \brief Publish a hangup handler related message to \ref stasis
- */
-static void publish_hangup_handler_message(const char *action, struct ast_channel *chan, const char *handler)
-{
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
-
- blob = ast_json_pack("{s: s, s: s}",
- "type", action,
- "handler", S_OR(handler, ""));
- if (!blob) {
- return;
- }
-
- ast_channel_publish_blob(chan, ast_channel_hangup_handler_type(), blob);
-}
-
-int ast_pbx_hangup_handler_run(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- if (AST_LIST_EMPTY(handlers)) {
- ast_channel_unlock(chan);
- return 0;
- }
-
- /*
- * Make sure that the channel is marked as hungup since we are
- * going to run the hangup handlers on it.
- */
- ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
-
- for (;;) {
- handlers = ast_channel_hangup_handlers(chan);
- h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
- if (!h_handler) {
- break;
- }
-
- publish_hangup_handler_message("run", chan, h_handler->args);
- ast_channel_unlock(chan);
-
- ast_app_exec_sub(NULL, chan, h_handler->args, 1);
- ast_free(h_handler);
-
- ast_channel_lock(chan);
- }
- ast_channel_unlock(chan);
- return 1;
-}
-
-void ast_pbx_hangup_handler_init(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
-
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_HEAD_INIT_NOLOCK(handlers);
-}
-
-void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
-
- /* Get rid of each of the hangup handlers on the channel */
- handlers = ast_channel_hangup_handlers(chan);
- while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
- ast_free(h_handler);
- }
-
- ast_channel_unlock(chan);
-}
-
-int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
- if (h_handler) {
- publish_hangup_handler_message("pop", chan, h_handler->args);
- }
- ast_channel_unlock(chan);
- if (h_handler) {
- ast_free(h_handler);
- return 1;
- }
- return 0;
-}
-
-void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
- const char *expanded_handler;
-
- if (ast_strlen_zero(handler)) {
- return;
- }
-
- expanded_handler = ast_app_expand_sub_args(chan, handler);
- if (!expanded_handler) {
- return;
- }
- h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
- if (!h_handler) {
- ast_free((char *) expanded_handler);
- return;
- }
- strcpy(h_handler->args, expanded_handler);/* Safe */
- ast_free((char *) expanded_handler);
-
- ast_channel_lock(chan);
-
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_INSERT_HEAD(handlers, h_handler, node);
- publish_hangup_handler_message("push", chan, h_handler->args);
- ast_channel_unlock(chan);
-}
-
-#define HANDLER_FORMAT "%-30s %s\n"
-
-/*!
- * \internal
- * \brief CLI output the hangup handler headers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_headers(int fd)
-{
- ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
-}
-
-/*!
- * \internal
- * \brief CLI output the channel hangup handlers.
- * \since 11.0
- *
- * \param fd CLI file descriptor to use.
- * \param chan Channel to show hangup handlers.
- *
- * \return Nothing
- */
-static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
-{
- struct ast_hangup_handler_list *handlers;
- struct ast_hangup_handler *h_handler;
- int first = 1;
-
- ast_channel_lock(chan);
- handlers = ast_channel_hangup_handlers(chan);
- AST_LIST_TRAVERSE(handlers, h_handler, node) {
- ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
- first = 0;
- }
- ast_channel_unlock(chan);
-}
-
-/*
- * \brief 'show hanguphandlers <channel>' CLI command implementation function...
- */
-static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel *chan;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show hanguphandlers";
- e->usage =
- "Usage: core show hanguphandlers <channel>\n"
- " Show hangup handlers of a specified channel.\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
- }
-
- if (a->argc < 4) {
- return CLI_SHOWUSAGE;
- }
-
- chan = ast_channel_get_by_name(a->argv[3]);
- if (!chan) {
- ast_cli(a->fd, "Channel does not exist.\n");
- return CLI_FAILURE;
- }
-
- ast_pbx_hangup_handler_headers(a->fd);
- ast_pbx_hangup_handler_show(a->fd, chan);
-
- ast_channel_unref(chan);
-
- return CLI_SUCCESS;
-}
-
-/*
- * \brief 'show hanguphandlers all' CLI command implementation function...
- */
-static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_channel_iterator *iter;
- struct ast_channel *chan;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show hanguphandlers all";
- e->usage =
- "Usage: core show hanguphandlers all\n"
- " Show hangup handlers for all channels.\n";
- return NULL;
- case CLI_GENERATE:
- return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
- }
-
- if (a->argc < 4) {
- return CLI_SHOWUSAGE;
- }
-
- iter = ast_channel_iterator_all_new();
- if (!iter) {
- return CLI_FAILURE;
- }
-
- ast_pbx_hangup_handler_headers(a->fd);
- for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
- ast_pbx_hangup_handler_show(a->fd, chan);
- }
- ast_channel_iterator_destroy(iter);
-
- return CLI_SUCCESS;
-}
-
-/*! helper function to set extension and priority */
-static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
-{
- ast_channel_lock(c);
- ast_channel_exten_set(c, exten);
- ast_channel_priority_set(c, pri);
- ast_channel_unlock(c);
-}
-
-/*!
- * \brief collect digits from the channel into the buffer.
- * \param c, buf, buflen, pos
- * \param waittime is in milliseconds
- * \retval 0 on timeout or done.
- * \retval -1 on error.
-*/
-static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
-{
- int digit;
-
- buf[pos] = '\0'; /* make sure it is properly terminated */
- while (ast_matchmore_extension(c, ast_channel_context(c), buf, 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- /* As long as we're willing to wait, and as long as it's not defined,
- keep reading digits until we can't possibly get a right answer anymore. */
- digit = ast_waitfordigit(c, waittime);
- if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
- } else {
- if (!digit) /* No entry */
- break;
- if (digit < 0) /* Error, maybe a hangup */
- return -1;
- if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
- buf[pos++] = digit;
- buf[pos] = '\0';
- }
- waittime = ast_channel_pbx(c)->dtimeoutms;
- }
- }
- return 0;
-}
-
-static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
- struct ast_pbx_args *args)
-{
- int found = 0; /* set if we find at least one match */
- int res = 0;
- int autoloopflag;
- int error = 0; /* set an error conditions */
- struct ast_pbx *pbx;
- struct ast_callid *callid;
-
- /* A little initial setup here */
- if (ast_channel_pbx(c)) {
- ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c));
- /* XXX and now what ? */
- ast_free(ast_channel_pbx(c));
- }
- if (!(pbx = ast_calloc(1, sizeof(*pbx)))) {
- return AST_PBX_FAILED;
- }
-
- callid = ast_read_threadstorage_callid();
- /* If the thread isn't already associated with a callid, we should create that association. */
- if (!callid) {
- /* Associate new PBX thread with the channel call id if it is availble.
- * If not, create a new one instead.
- */
- callid = ast_channel_callid(c);
- if (!callid) {
- callid = ast_create_callid();
- if (callid) {
- ast_channel_lock(c);
- ast_channel_callid_set(c, callid);
- ast_channel_unlock(c);
- }
- }
- ast_callid_threadassoc_add(callid);
- callid = ast_callid_unref(callid);
- } else {
- /* Nothing to do here, The thread is already bound to a callid. Let's just get rid of the reference. */
- ast_callid_unref(callid);
- }
-
- ast_channel_pbx_set(c, pbx);
- /* Set reasonable defaults */
- ast_channel_pbx(c)->rtimeoutms = 10000;
- ast_channel_pbx(c)->dtimeoutms = 5000;
-
- autoloopflag = ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
- ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP);
-
- if (ast_strlen_zero(ast_channel_exten(c))) {
- /* If not successful fall back to 's' - but only if there is no given exten */
- ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
- /* XXX the original code used the existing priority in the call to
- * ast_exists_extension(), and reset it to 1 afterwards.
- * I believe the correct thing is to set it to 1 immediately.
- */
- set_ext_pri(c, "s", 1);
- }
-
- for (;;) {
- char dst_exten[256]; /* buffer to accumulate digits */
- int pos = 0; /* XXX should check bounds */
- int digit = 0;
- int invalid = 0;
- int timeout = 0;
-
- /* No digits pressed yet */
- dst_exten[pos] = '\0';
-
- /* loop on priorities in this context/exten */
- while (!(res = ast_spawn_extension(c, ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c),
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL),
- &found, 1))) {
-
- if (!ast_check_hangup(c)) {
- ast_channel_priority_set(c, ast_channel_priority(c) + 1);
- continue;
- }
-
- /* Check softhangup flags. */
- if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
- continue;
- }
- if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
- if (ast_exists_extension(c, ast_channel_context(c), "T", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- set_ext_pri(c, "T", 1);
- /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
- memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
- continue;
- } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- raise_exception(c, "ABSOLUTETIMEOUT", 1);
- /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
- memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
- continue;
- }
-
- /* Call timed out with no special extension to jump to. */
- error = 1;
- break;
- }
- ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
- ast_channel_exten(c), ast_channel_priority(c));
- error = 1;
- break;
- } /* end while - from here on we can use 'break' to go out */
- if (found && res) {
- /* Something bad happened, or a hangup has been requested. */
- if (strchr("0123456789ABCDEF*#", res)) {
- ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
- pos = 0;
- dst_exten[pos++] = digit = res;
- dst_exten[pos] = '\0';
- } else if (res == AST_PBX_INCOMPLETE) {
- ast_debug(1, "Spawn extension (%s,%s,%d) exited INCOMPLETE on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited INCOMPLETE on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-
- /* Don't cycle on incomplete - this will happen if the only extension that matches is our "incomplete" extension */
- if (!ast_matchmore_extension(c, ast_channel_context(c), ast_channel_exten(c), 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- invalid = 1;
- } else {
- ast_copy_string(dst_exten, ast_channel_exten(c), sizeof(dst_exten));
- digit = 1;
- pos = strlen(dst_exten);
- }
- } else {
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
-
- if ((res == AST_PBX_ERROR)
- && ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- /* if we are already on the 'e' exten, don't jump to it again */
- if (!strcmp(ast_channel_exten(c), "e")) {
- ast_verb(2, "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
- error = 1;
- } else {
- raise_exception(c, "ERROR", 1);
- continue;
- }
- }
-
- if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_ASYNCGOTO) {
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_ASYNCGOTO);
- continue;
- }
- if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
- if (ast_exists_extension(c, ast_channel_context(c), "T", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- set_ext_pri(c, "T", 1);
- /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
- memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
- continue;
- } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- raise_exception(c, "ABSOLUTETIMEOUT", 1);
- /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
- memset(ast_channel_whentohangup(c), 0, sizeof(*ast_channel_whentohangup(c)));
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
- continue;
- }
- /* Call timed out with no special extension to jump to. */
- }
- error = 1;
- break;
- }
- }
- if (error)
- break;
-
- /*!\note
- * We get here on a failure of some kind: non-existing extension or
- * hangup. We have options, here. We can either catch the failure
- * and continue, or we can drop out entirely. */
-
- if (invalid
- || (ast_strlen_zero(dst_exten) &&
- !ast_exists_extension(c, ast_channel_context(c), ast_channel_exten(c), 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL)))) {
- /*!\note
- * If there is no match at priority 1, it is not a valid extension anymore.
- * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
- * neither exist.
- */
- if (ast_exists_extension(c, ast_channel_context(c), "i", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- ast_verb(3, "Channel '%s' sent to invalid extension: context,exten,priority=%s,%s,%d\n",
- ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
- pbx_builtin_setvar_helper(c, "INVALID_EXTEN", ast_channel_exten(c));
- set_ext_pri(c, "i", 1);
- } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- raise_exception(c, "INVALID", 1);
- } else {
- ast_log(LOG_WARNING, "Channel '%s' sent to invalid extension but no invalid handler: context,exten,priority=%s,%s,%d\n",
- ast_channel_name(c), ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c));
- error = 1; /* we know what to do with it */
- break;
- }
- } else if (ast_channel_softhangup_internal_flag(c) & AST_SOFTHANGUP_TIMEOUT) {
- /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
- ast_channel_clear_softhangup(c, AST_SOFTHANGUP_TIMEOUT);
- } else { /* keypress received, get more digits for a full extension */
- int waittime = 0;
- if (digit)
- waittime = ast_channel_pbx(c)->dtimeoutms;
- else if (!autofallthrough)
- waittime = ast_channel_pbx(c)->rtimeoutms;
- if (!waittime) {
- const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
- if (!status)
- status = "UNKNOWN";
- ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", ast_channel_name(c), status);
- if (!strcasecmp(status, "CONGESTION"))
- res = pbx_builtin_congestion(c, "10");
- else if (!strcasecmp(status, "CHANUNAVAIL"))
- res = pbx_builtin_congestion(c, "10");
- else if (!strcasecmp(status, "BUSY"))
- res = pbx_builtin_busy(c, "10");
- error = 1; /* XXX disable message */
- break; /* exit from the 'for' loop */
- }
-
- if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
- break;
- if (res == AST_PBX_INCOMPLETE && ast_strlen_zero(&dst_exten[pos]))
- timeout = 1;
- if (!timeout
- && ast_exists_extension(c, ast_channel_context(c), dst_exten, 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) { /* Prepare the next cycle */
- set_ext_pri(c, dst_exten, 1);
- } else {
- /* No such extension */
- if (!timeout && !ast_strlen_zero(dst_exten)) {
- /* An invalid extension */
- if (ast_exists_extension(c, ast_channel_context(c), "i", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, ast_channel_context(c), ast_channel_name(c));
- pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
- set_ext_pri(c, "i", 1);
- } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- raise_exception(c, "INVALID", 1);
- } else {
- ast_log(LOG_WARNING,
- "Invalid extension '%s', but no rule 'i' or 'e' in context '%s'\n",
- dst_exten, ast_channel_context(c));
- found = 1; /* XXX disable message */
- break;
- }
- } else {
- /* A simple timeout */
- if (ast_exists_extension(c, ast_channel_context(c), "t", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- ast_verb(3, "Timeout on %s\n", ast_channel_name(c));
- set_ext_pri(c, "t", 1);
- } else if (ast_exists_extension(c, ast_channel_context(c), "e", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- raise_exception(c, "RESPONSETIMEOUT", 1);
- } else {
- ast_log(LOG_WARNING,
- "Timeout, but no rule 't' or 'e' in context '%s'\n",
- ast_channel_context(c));
- found = 1; /* XXX disable message */
- break;
- }
- }
- }
- }
- }
-
- if (!found && !error) {
- ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", ast_channel_name(c));
- }
-
- if (!args || !args->no_hangup_chan) {
- ast_softhangup(c, AST_SOFTHANGUP_APPUNLOAD);
- if (!ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN)
- && ast_exists_extension(c, ast_channel_context(c), "h", 1,
- S_COR(ast_channel_caller(c)->id.number.valid,
- ast_channel_caller(c)->id.number.str, NULL))) {
- ast_pbx_h_exten_run(c, ast_channel_context(c));
- }
- ast_pbx_hangup_handler_run(c);
- }
-
- ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP);
- ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
- pbx_destroy(ast_channel_pbx(c));
- ast_channel_pbx_set(c, NULL);
-
- if (!args || !args->no_hangup_chan) {
- ast_hangup(c);
- }
-
- return AST_PBX_SUCCESS;
-}
-
-/*!
- * \brief Increase call count for channel
- * \retval 0 on success
- * \retval non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached
-*/
-static int increase_call_count(const struct ast_channel *c)
-{
- int failed = 0;
- double curloadavg;
-#if defined(HAVE_SYSINFO)
- long curfreemem;
- struct sysinfo sys_info;
-#endif
-
- ast_mutex_lock(&maxcalllock);
- if (ast_option_maxcalls) {
- if (countcalls >= ast_option_maxcalls) {
- ast_log(LOG_WARNING, "Maximum call limit of %d calls exceeded by '%s'!\n", ast_option_maxcalls, ast_channel_name(c));
- failed = -1;
- }
- }
- if (ast_option_maxload) {
- getloadavg(&curloadavg, 1);
- if (curloadavg >= ast_option_maxload) {
- ast_log(LOG_WARNING, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", ast_option_maxload, ast_channel_name(c), curloadavg);
- failed = -1;
- }
- }
-#if defined(HAVE_SYSINFO)
- if (option_minmemfree) {
- if (!sysinfo(&sys_info)) {
- /* make sure that the free system memory is above the configured low watermark
- * convert the amount of freeram from mem_units to MB */
- curfreemem = sys_info.freeram * sys_info.mem_unit;
- curfreemem /= 1024 * 1024;
- if (curfreemem < option_minmemfree) {
- ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
- failed = -1;
- }
- }
- }
-#endif
-
- if (!failed) {
- countcalls++;
- totalcalls++;
- }
- ast_mutex_unlock(&maxcalllock);
-
- return failed;
-}
-
-static void decrease_call_count(void)
-{
- ast_mutex_lock(&maxcalllock);
- if (countcalls > 0)
- countcalls--;
- ast_mutex_unlock(&maxcalllock);
-}
-
-static void destroy_exten(struct ast_exten *e)
-{
- if (e->priority == PRIORITY_HINT)
- ast_remove_hint(e);
-
- if (e->peer_table)
- ast_hashtab_destroy(e->peer_table,0);
- if (e->peer_label_table)
- ast_hashtab_destroy(e->peer_label_table, 0);
- if (e->datad)
- e->datad(e->data);
- ast_free(e);
-}
-
-static void *pbx_thread(void *data)
-{
- /* Oh joyeous kernel, we're a new thread, with nothing to do but
- answer this channel and get it going.
- */
- /* NOTE:
- The launcher of this function _MUST_ increment 'countcalls'
- before invoking the function; it will be decremented when the
- PBX has finished running on the channel
- */
- struct ast_channel *c = data;
-
- __ast_pbx_run(c, NULL);
- decrease_call_count();
-
- pthread_exit(NULL);
-
- return NULL;
-}
-
-enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
-{
- pthread_t t;
-
- if (!c) {
- ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
- return AST_PBX_FAILED;
- }
-
- if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
- ast_log(LOG_WARNING, "PBX requires Asterisk to be fully booted\n");
- return AST_PBX_FAILED;
- }
-
- if (increase_call_count(c))
- return AST_PBX_CALL_LIMIT;
-
- /* Start a new thread, and get something handling this channel. */
- if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
- ast_log(LOG_WARNING, "Failed to create new channel thread\n");
- decrease_call_count();
- return AST_PBX_FAILED;
- }
-
- return AST_PBX_SUCCESS;
-}
-
-enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
-{
- enum ast_pbx_result res = AST_PBX_SUCCESS;
-
- if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
- ast_log(LOG_WARNING, "PBX requires Asterisk to be fully booted\n");
- return AST_PBX_FAILED;
- }
-
- if (increase_call_count(c)) {
- return AST_PBX_CALL_LIMIT;
- }
-
- res = __ast_pbx_run(c, args);
-
- decrease_call_count();
-
- return res;
-}
-
-enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
-{
- return ast_pbx_run_args(c, NULL);
-}
-
-int ast_active_calls(void)
-{
- return countcalls;
-}
-
-int ast_processed_calls(void)
-{
- return totalcalls;
-}
-
-int pbx_set_autofallthrough(int newval)
-{
- int oldval = autofallthrough;
- autofallthrough = newval;
- return oldval;
-}
-
-int pbx_set_extenpatternmatchnew(int newval)
-{
- int oldval = extenpatternmatchnew;
- extenpatternmatchnew = newval;
- return oldval;
-}
-
-void pbx_set_overrideswitch(const char *newval)
-{
- if (overrideswitch) {
- ast_free(overrideswitch);
- }
- if (!ast_strlen_zero(newval)) {
- overrideswitch = ast_strdup(newval);
- } else {
- overrideswitch = NULL;
- }
-}
-
-/*!
- * \brief lookup for a context with a given name,
- * \retval found context or NULL if not found.
- */
-static struct ast_context *find_context(const char *context)
-{
- struct fake_context item;
-
- ast_copy_string(item.name, context, sizeof(item.name));
-
- return ast_hashtab_lookup(contexts_table, &item);
-}
-
-/*!
- * \brief lookup for a context with a given name,
- * \retval with conlock held if found.
- * \retval NULL if not found.
- */
-static struct ast_context *find_context_locked(const char *context)
-{
- struct ast_context *c;
- struct fake_context item;
-
- ast_copy_string(item.name, context, sizeof(item.name));
-
- ast_rdlock_contexts();
- c = ast_hashtab_lookup(contexts_table, &item);
- if (!c) {
- ast_unlock_contexts();
- }
-
- return c;
-}
-
-/*!
- * \brief Remove included contexts.
- * This function locks contexts list by &conlist, search for the right context
- * structure, leave context list locked and call ast_context_remove_include2
- * which removes include, unlock contexts list and return ...
- */
-int ast_context_remove_include(const char *context, const char *include, const char *registrar)
-{
- int ret = -1;
- struct ast_context *c;
-
- c = find_context_locked(context);
- if (c) {
- /* found, remove include from this context ... */
- ret = ast_context_remove_include2(c, include, registrar);
- ast_unlock_contexts();
- }
- return ret;
-}
-
-/*!
- * \brief Locks context, remove included contexts, unlocks context.
- * When we call this function, &conlock lock must be locked, because when
- * we giving *con argument, some process can remove/change this context
- * and after that there can be segfault.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
-{
- struct ast_include *i, *pi = NULL;
- int ret = -1;
-
- ast_wrlock_context(con);
-
- /* find our include */
- for (i = con->includes; i; pi = i, i = i->next) {
- if (!strcmp(i->name, include) &&
- (!registrar || !strcmp(i->registrar, registrar))) {
- /* remove from list */
- ast_verb(3, "Removing inclusion of context '%s' in context '%s; registrar=%s'\n", include, ast_get_context_name(con), registrar);
- if (pi)
- pi->next = i->next;
- else
- con->includes = i->next;
- /* free include and return */
- ast_destroy_timing(&(i->timing));
- ast_free(i);
- ret = 0;
- break;
- }
- }
-
- ast_unlock_context(con);
-
- return ret;
-}
-
-/*!
- * \note This function locks contexts list by &conlist, search for the rigt context
- * structure, leave context list locked and call ast_context_remove_switch2
- * which removes switch, unlock contexts list and return ...
- */
-int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
-{
- int ret = -1; /* default error return */
- struct ast_context *c;
-
- c = find_context_locked(context);
- if (c) {
- /* remove switch from this context ... */
- ret = ast_context_remove_switch2(c, sw, data, registrar);
- ast_unlock_contexts();
- }
- return ret;
-}
-
-/*!
- * \brief This function locks given context, removes switch, unlock context and
- * return.
- * \note When we call this function, &conlock lock must be locked, because when
- * we giving *con argument, some process can remove/change this context
- * and after that there can be segfault.
- *
- */
-int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
-{
- struct ast_sw *i;
- int ret = -1;
-
- ast_wrlock_context(con);
-
- /* walk switches */
- AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
- if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
- (!registrar || !strcmp(i->registrar, registrar))) {
- /* found, remove from list */
- ast_verb(3, "Removing switch '%s' from context '%s; registrar=%s'\n", sw, ast_get_context_name(con), registrar);
- AST_LIST_REMOVE_CURRENT(list);
- ast_free(i); /* free switch and return */
- ret = 0;
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
-
- ast_unlock_context(con);
-
- return ret;
-}
-
-/*! \note This function will lock conlock. */
-int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
-{
- return ast_context_remove_extension_callerid(context, extension, priority, NULL, AST_EXT_MATCHCID_ANY, registrar);
-}
-
-int ast_context_remove_extension_callerid(const char *context, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar)
-{
- int ret = -1; /* default error return */
- struct ast_context *c;
-
- c = find_context_locked(context);
- if (c) { /* ... remove extension ... */
- ret = ast_context_remove_extension_callerid2(c, extension, priority, callerid,
- matchcallerid, registrar, 0);
- ast_unlock_contexts();
- }
-
- return ret;
-}
-
-/*!
- * \brief This functionc locks given context, search for the right extension and
- * fires out all peer in this extensions with given priority. If priority
- * is set to 0, all peers are removed. After that, unlock context and
- * return.
- * \note When do you want to call this function, make sure that &conlock is locked,
- * because some process can handle with your *con context before you lock
- * it.
- *
- */
-int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar, int already_locked)
-{
- return ast_context_remove_extension_callerid2(con, extension, priority, NULL, AST_EXT_MATCHCID_ANY, registrar, already_locked);
-}
-
-int ast_context_remove_extension_callerid2(struct ast_context *con, const char *extension, int priority, const char *callerid, int matchcallerid, const char *registrar, int already_locked)
-{
- struct ast_exten *exten, *prev_exten = NULL;
- struct ast_exten *peer;
- struct ast_exten ex, *exten2, *exten3;
- char dummy_name[1024];
- struct ast_exten *previous_peer = NULL;
- struct ast_exten *next_peer = NULL;
- int found = 0;
-
- if (!already_locked)
- ast_wrlock_context(con);
-
-#ifdef NEED_DEBUG
- ast_verb(3,"Removing %s/%s/%d%s%s from trees, registrar=%s\n", con->name, extension, priority, matchcallerid ? "/" : "", matchcallerid ? callerid : "", registrar);
-#endif
-#ifdef CONTEXT_DEBUG
- check_contexts(__FILE__, __LINE__);
-#endif
- /* find this particular extension */
- ex.exten = dummy_name;
- ex.matchcid = matchcallerid;
- ex.cidmatch = callerid;
- ast_copy_string(dummy_name, extension, sizeof(dummy_name));
- exten = ast_hashtab_lookup(con->root_table, &ex);
- if (exten) {
- if (priority == 0) {
- exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
- if (!exten2)
- ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_table\n", extension, con->name);
- if (con->pattern_tree) {
- struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
-
- if (x->exten) { /* this test for safety purposes */
- x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
- x->exten = 0; /* get rid of what will become a bad pointer */
- } else {
- ast_log(LOG_WARNING,"Trying to delete an exten from a context, but the pattern tree node returned isn't a full extension\n");
- }
- }
- } else {
- ex.priority = priority;
- exten2 = ast_hashtab_lookup(exten->peer_table, &ex);
- if (exten2) {
- if (exten2->label) { /* if this exten has a label, remove that, too */
- exten3 = ast_hashtab_remove_this_object(exten->peer_label_table,exten2);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_table of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
- }
-
- exten3 = ast_hashtab_remove_this_object(exten->peer_table, exten2);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_table of context %s, extension %s!\n", priority, con->name, exten2->exten);
- if (exten2 == exten && exten2->peer) {
- exten2 = ast_hashtab_remove_this_object(con->root_table, exten);
- ast_hashtab_insert_immediate(con->root_table, exten2->peer);
- }
- if (ast_hashtab_size(exten->peer_table) == 0) {
- /* well, if the last priority of an exten is to be removed,
- then, the extension is removed, too! */
- exten3 = ast_hashtab_remove_this_object(con->root_table, exten);
- if (!exten3)
- ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_table (%s) (priority %d)\n", exten->exten, con->name, priority);
- if (con->pattern_tree) {
- struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
- if (x->exten) { /* this test for safety purposes */
- x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
- x->exten = 0; /* get rid of what will become a bad pointer */
- }
- }
- }
- } else {
- ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
- priority, exten->exten, con->name);
- }
- }
- } else {
- /* hmmm? this exten is not in this pattern tree? */
- ast_log(LOG_WARNING,"Cannot find extension %s in root_table in context %s\n",
- extension, con->name);
- }
-#ifdef NEED_DEBUG
- if (con->pattern_tree) {
- ast_log(LOG_NOTICE,"match char tree after exten removal:\n");
- log_match_char_tree(con->pattern_tree, " ");
- }
-#endif
-
- /* scan the extension list to find first matching extension-registrar */
- for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
- if (!strcmp(exten->exten, extension) &&
- (!registrar || !strcmp(exten->registrar, registrar)) &&
- (!matchcallerid || (!ast_strlen_zero(callerid) && !ast_strlen_zero(exten->cidmatch) && !strcmp(exten->cidmatch, callerid)) || (ast_strlen_zero(callerid) && ast_strlen_zero(exten->cidmatch))))
- break;
- }
- if (!exten) {
- /* we can't find right extension */
- if (!already_locked)
- ast_unlock_context(con);
- return -1;
- }
-
- /* scan the priority list to remove extension with exten->priority == priority */
- for (peer = exten, next_peer = exten->peer ? exten->peer : exten->next;
- peer && !strcmp(peer->exten, extension) &&
- (!callerid || (!matchcallerid && !peer->matchcid) || (matchcallerid && peer->matchcid && !strcmp(peer->cidmatch, callerid))) ;
- peer = next_peer, next_peer = next_peer ? (next_peer->peer ? next_peer->peer : next_peer->next) : NULL) {
-
- if ((priority == 0 || peer->priority == priority) &&
- (!registrar || !strcmp(peer->registrar, registrar) )) {
- found = 1;
-
- /* we are first priority extension? */
- if (!previous_peer) {
- /*
- * We are first in the priority chain, so must update the extension chain.
- * The next node is either the next priority or the next extension
- */
- struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
- if (peer->peer) {
- /* move the peer_table and peer_label_table down to the next peer, if
- it is there */
- peer->peer->peer_table = peer->peer_table;
- peer->peer->peer_label_table = peer->peer_label_table;
- peer->peer_table = NULL;
- peer->peer_label_table = NULL;
- }
- if (!prev_exten) { /* change the root... */
- con->root = next_node;
- } else {
- prev_exten->next = next_node; /* unlink */
- }
- if (peer->peer) { /* update the new head of the pri list */
- peer->peer->next = peer->next;
- }
- } else { /* easy, we are not first priority in extension */
- previous_peer->peer = peer->peer;
- }
-
-
- /* now, free whole priority extension */
- destroy_exten(peer);
- } else {
- previous_peer = peer;
- }
- }
- if (!already_locked)
- ast_unlock_context(con);
- return found ? 0 : -1;
-}
-
-
-/*!
- * \note This function locks contexts list by &conlist, searches for the right context
- * structure, and locks the macrolock mutex in that context.
- * macrolock is used to limit a macro to be executed by one call at a time.
- * \param context The context
- */
-int ast_context_lockmacro(const char *context)
-{
- struct ast_context *c;
- int ret = -1;
-
- c = find_context_locked(context);
- if (c) {
- ast_unlock_contexts();
-
- /* if we found context, lock macrolock */
- ret = ast_mutex_lock(&c->macrolock);
- }
-
- return ret;
-}
-
-/*!
- * \note This function locks contexts list by &conlist, searches for the right context
- * structure, and unlocks the macrolock mutex in that context.
- * macrolock is used to limit a macro to be executed by one call at a time.
- * \param context The context
- */
-int ast_context_unlockmacro(const char *context)
-{
- struct ast_context *c;
- int ret = -1;
-
- c = find_context_locked(context);
- if (c) {
- ast_unlock_contexts();
-
- /* if we found context, unlock macrolock */
- ret = ast_mutex_unlock(&c->macrolock);
- }
-
- return ret;
-}
-
-/*! \brief Dynamically register a new dial plan application */
-int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, const char *), const char *synopsis, const char *description, void *mod)
-{
- struct ast_app *tmp;
- struct ast_app *cur;
- int length;
-#ifdef AST_XML_DOCS
- char *tmpxml;
-#endif
-
- AST_RWLIST_WRLOCK(&apps);
- cur = pbx_findapp_nolock(app);
- if (cur) {
- ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
- AST_RWLIST_UNLOCK(&apps);
- return -1;
- }
-
- length = sizeof(*tmp) + strlen(app) + 1;
-
- if (!(tmp = ast_calloc(1, length))) {
- AST_RWLIST_UNLOCK(&apps);
- return -1;
- }
-
- if (ast_string_field_init(tmp, 128)) {
- AST_RWLIST_UNLOCK(&apps);
- ast_free(tmp);
- return -1;
- }
-
- strcpy(tmp->name, app);
- tmp->execute = execute;
- tmp->module = mod;
-
-#ifdef AST_XML_DOCS
- /* Try to lookup the docs in our XML documentation database */
- if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
- /* load synopsis */
- tmpxml = ast_xmldoc_build_synopsis("application", app, ast_module_name(tmp->module));
- ast_string_field_set(tmp, synopsis, tmpxml);
- ast_free(tmpxml);
-
- /* load description */
- tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
- ast_string_field_set(tmp, description, tmpxml);
- ast_free(tmpxml);
-
- /* load syntax */
- tmpxml = ast_xmldoc_build_syntax("application", app, ast_module_name(tmp->module));
- ast_string_field_set(tmp, syntax, tmpxml);
- ast_free(tmpxml);
-
- /* load arguments */
- tmpxml = ast_xmldoc_build_arguments("application", app, ast_module_name(tmp->module));
- ast_string_field_set(tmp, arguments, tmpxml);
- ast_free(tmpxml);
-
- /* load seealso */
- tmpxml = ast_xmldoc_build_seealso("application", app, ast_module_name(tmp->module));
- ast_string_field_set(tmp, seealso, tmpxml);
- ast_free(tmpxml);
- tmp->docsrc = AST_XML_DOC;
- } else {
-#endif
- ast_string_field_set(tmp, synopsis, synopsis);
- ast_string_field_set(tmp, description, description);
-#ifdef AST_XML_DOCS
- tmp->docsrc = AST_STATIC_DOC;
- }
-#endif
-
- /* Store in alphabetical order */
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
- if (strcasecmp(tmp->name, cur->name) < 0) {
- AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
- if (!cur)
- AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
-
- ast_verb(2, "Registered application '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, tmp->name));
-
- AST_RWLIST_UNLOCK(&apps);
-
- return 0;
-}
-
-/*
- * Append to the list. We don't have a tail pointer because we need
- * to scan the list anyways to check for duplicates during insertion.
- */
-int ast_register_switch(struct ast_switch *sw)
-{
- struct ast_switch *tmp;
-
- AST_RWLIST_WRLOCK(&switches);
- AST_RWLIST_TRAVERSE(&switches, tmp, list) {
- if (!strcasecmp(tmp->name, sw->name)) {
- AST_RWLIST_UNLOCK(&switches);
- ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
- return -1;
- }
- }
- AST_RWLIST_INSERT_TAIL(&switches, sw, list);
- AST_RWLIST_UNLOCK(&switches);
-
- return 0;
-}
-
-void ast_unregister_switch(struct ast_switch *sw)
-{
- AST_RWLIST_WRLOCK(&switches);
- AST_RWLIST_REMOVE(&switches, sw, list);
- AST_RWLIST_UNLOCK(&switches);
-}
-
-/*
- * Help for CLI commands ...
- */
-
-static void print_app_docs(struct ast_app *aa, int fd)
-{
-#ifdef AST_XML_DOCS
- char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
- if (aa->docsrc == AST_XML_DOC) {
- synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
- description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
- arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
- seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
- if (!synopsis || !description || !arguments || !seealso) {
- goto free_docs;
- }
- ast_cli(fd, "\n"
- "%s -= Info about application '%s' =- %s\n\n"
- COLORIZE_FMT "\n"
- "%s\n\n"
- COLORIZE_FMT "\n"
- "%s\n\n"
- COLORIZE_FMT "\n"
- "%s%s%s\n\n"
- COLORIZE_FMT "\n"
- "%s\n\n"
- COLORIZE_FMT "\n"
- "%s\n",
- ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
- COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
- COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
- COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
- ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
- COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
- COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
-free_docs:
- ast_free(synopsis);
- ast_free(description);
- ast_free(arguments);
- ast_free(seealso);
- } else
-#endif
- {
- ast_cli(fd, "\n"
- "%s -= Info about application '%s' =- %s\n\n"
- COLORIZE_FMT "\n"
- COLORIZE_FMT "\n\n"
- COLORIZE_FMT "\n"
- COLORIZE_FMT "\n\n"
- COLORIZE_FMT "\n"
- COLORIZE_FMT "\n\n"
- COLORIZE_FMT "\n"
- COLORIZE_FMT "\n\n"
- COLORIZE_FMT "\n"
- COLORIZE_FMT "\n",
- ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
- COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
- COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
- COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
- COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
- COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
- COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
- COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
- COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
- COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
- COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
- }
-}
-
-/*
- * \brief 'show application' CLI command implementation function...
- */
-static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- struct ast_app *aa;
- int app, no_registered_app = 1;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "core show application";
- e->usage =
- "Usage: core show application <application> [<application> [<application> [...]]]\n"
- " Describes a particular application.\n";
- &nbs