Sun Oct 16 2011 08:41:42

Asterisk developer's documentation


manager.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief The Asterisk Management Interface - AMI
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \extref OpenSSL http://www.openssl.org - for AMI/SSL
00026  *
00027  * At the moment this file contains a number of functions, namely:
00028  *
00029  * - data structures storing AMI state
00030  * - AMI-related API functions, used by internal asterisk components
00031  * - handlers for AMI-related CLI functions
00032  * - handlers for AMI functions (available through the AMI socket)
00033  * - the code for the main AMI listener thread and individual session threads
00034  * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
00035  *
00036  * \ref amiconf
00037  */
00038 
00039 /*! \addtogroup Group_AMI AMI functions
00040 */
00041 /*! @{
00042  Doxygen group */
00043 
00044 /*** MODULEINFO
00045    <support_level>core</support_level>
00046  ***/
00047 
00048 #include "asterisk.h"
00049 
00050 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 332817 $")
00051 
00052 #include "asterisk/_private.h"
00053 #include "asterisk/paths.h"   /* use various ast_config_AST_* */
00054 #include <ctype.h>
00055 #include <sys/time.h>
00056 #include <signal.h>
00057 #include <sys/mman.h>
00058 #include <sys/types.h>
00059 #include <regex.h>
00060 
00061 #include "asterisk/channel.h"
00062 #include "asterisk/file.h"
00063 #include "asterisk/manager.h"
00064 #include "asterisk/module.h"
00065 #include "asterisk/config.h"
00066 #include "asterisk/callerid.h"
00067 #include "asterisk/lock.h"
00068 #include "asterisk/cli.h"
00069 #include "asterisk/app.h"
00070 #include "asterisk/pbx.h"
00071 #include "asterisk/md5.h"
00072 #include "asterisk/acl.h"
00073 #include "asterisk/utils.h"
00074 #include "asterisk/tcptls.h"
00075 #include "asterisk/http.h"
00076 #include "asterisk/ast_version.h"
00077 #include "asterisk/threadstorage.h"
00078 #include "asterisk/linkedlists.h"
00079 #include "asterisk/term.h"
00080 #include "asterisk/astobj2.h"
00081 #include "asterisk/features.h"
00082 #include "asterisk/security_events.h"
00083 #include "asterisk/aoc.h"
00084 
00085 /*** DOCUMENTATION
00086    <manager name="Ping" language="en_US">
00087       <synopsis>
00088          Keepalive command.
00089       </synopsis>
00090       <syntax>
00091          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00092       </syntax>
00093       <description>
00094          <para>A 'Ping' action will ellicit a 'Pong' response. Used to keep the
00095          manager connection open.</para>
00096       </description>
00097    </manager>
00098    <manager name="Events" language="en_US">
00099       <synopsis>
00100          Control Event Flow.
00101       </synopsis>
00102       <syntax>
00103          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00104          <parameter name="EventMask" required="true">
00105             <enumlist>
00106                <enum name="on">
00107                   <para>If all events should be sent.</para>
00108                </enum>
00109                <enum name="off">
00110                   <para>If no events should be sent.</para>
00111                </enum>
00112                <enum name="system,call,log,...">
00113                   <para>To select which flags events should have to be sent.</para>
00114                </enum>
00115             </enumlist>
00116          </parameter>
00117       </syntax>
00118       <description>
00119          <para>Enable/Disable sending of events to this manager client.</para>
00120       </description>
00121    </manager>
00122    <manager name="Logoff" language="en_US">
00123       <synopsis>
00124          Logoff Manager.
00125       </synopsis>
00126       <syntax>
00127          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00128       </syntax>
00129       <description>
00130          <para>Logoff the current manager session.</para>
00131       </description>
00132    </manager>
00133    <manager name="Login" language="en_US">
00134       <synopsis>
00135          Login Manager.
00136       </synopsis>
00137       <syntax>
00138          <parameter name="ActionID">
00139             <para>ActionID for this transaction. Will be returned.</para>
00140          </parameter>
00141          <parameter name="Username" required="true">
00142             <para>Username to login with as specified in manager.conf.</para>
00143          </parameter>
00144          <parameter name="Secret">
00145             <para>Secret to login with as specified in manager.conf.</para>
00146          </parameter>
00147       </syntax>
00148       <description>
00149          <para>Login Manager.</para>
00150       </description>
00151    </manager>
00152    <manager name="Challenge" language="en_US">
00153       <synopsis>
00154          Generate Challenge for MD5 Auth.
00155       </synopsis>
00156       <syntax>
00157          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00158       </syntax>
00159       <description>
00160          <para>Generate a challenge for MD5 authentication.</para>
00161       </description>
00162    </manager>
00163    <manager name="Hangup" language="en_US">
00164       <synopsis>
00165          Hangup channel.
00166       </synopsis>
00167       <syntax>
00168          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00169          <parameter name="Channel" required="true">
00170             <para>The channel name to be hangup.</para>
00171          </parameter>
00172          <parameter name="Cause">
00173             <para>Numeric hangup cause.</para>
00174          </parameter>
00175       </syntax>
00176       <description>
00177          <para>Hangup a channel.</para>
00178       </description>
00179    </manager>
00180    <manager name="Status" language="en_US">
00181       <synopsis>
00182          List channel status.
00183       </synopsis>
00184       <syntax>
00185          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00186          <parameter name="Channel" required="true">
00187             <para>The name of the channel to query for status.</para>
00188          </parameter>
00189          <parameter name="Variables">
00190             <para>Comma <literal>,</literal> separated list of variable to include.</para>
00191          </parameter>
00192       </syntax>
00193       <description>
00194          <para>Will return the status information of each channel along with the
00195          value for the specified channel variables.</para>
00196       </description>
00197    </manager>
00198    <manager name="Setvar" language="en_US">
00199       <synopsis>
00200          Set a channel variable.
00201       </synopsis>
00202       <syntax>
00203          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00204          <parameter name="Channel">
00205             <para>Channel to set variable for.</para>
00206          </parameter>
00207          <parameter name="Variable" required="true">
00208             <para>Variable name.</para>
00209          </parameter>
00210          <parameter name="Value" required="true">
00211             <para>Variable value.</para>
00212          </parameter>
00213       </syntax>
00214       <description>
00215          <para>Set a global or local channel variable.</para>
00216       </description>
00217    </manager>
00218    <manager name="Getvar" language="en_US">
00219       <synopsis>
00220          Gets a channel variable.
00221       </synopsis>
00222       <syntax>
00223          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00224          <parameter name="Channel">
00225             <para>Channel to read variable from.</para>
00226          </parameter>
00227          <parameter name="Variable" required="true">
00228             <para>Variable name.</para>
00229          </parameter>
00230       </syntax>
00231       <description>
00232          <para>Get the value of a global or local channel variable.</para>
00233       </description>
00234    </manager>
00235    <manager name="GetConfig" language="en_US">
00236       <synopsis>
00237          Retrieve configuration.
00238       </synopsis>
00239       <syntax>
00240          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00241          <parameter name="Filename" required="true">
00242             <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
00243          </parameter>
00244          <parameter name="Category">
00245             <para>Category in configuration file.</para>
00246          </parameter>
00247       </syntax>
00248       <description>
00249          <para>This action will dump the contents of a configuration
00250          file by category and contents or optionally by specified category only.</para>
00251       </description>
00252    </manager>
00253    <manager name="GetConfigJSON" language="en_US">
00254       <synopsis>
00255          Retrieve configuration (JSON format).
00256       </synopsis>
00257       <syntax>
00258          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00259          <parameter name="Filename" required="true">
00260             <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
00261          </parameter>
00262       </syntax>
00263       <description>
00264          <para>This action will dump the contents of a configuration file by category
00265          and contents in JSON format. This only makes sense to be used using rawman over
00266          the HTTP interface.</para>
00267       </description>
00268    </manager>
00269    <manager name="UpdateConfig" language="en_US">
00270       <synopsis>
00271          Update basic configuration.
00272       </synopsis>
00273       <syntax>
00274          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00275          <parameter name="SrcFilename" required="true">
00276             <para>Configuration filename to read (e.g. <filename>foo.conf</filename>).</para>
00277          </parameter>
00278          <parameter name="DstFilename" required="true">
00279             <para>Configuration filename to write (e.g. <filename>foo.conf</filename>)</para>
00280          </parameter>
00281          <parameter name="Reload">
00282             <para>Whether or not a reload should take place (or name of specific module).</para>
00283          </parameter>
00284          <parameter name="Action-XXXXXX">
00285             <para>Action to take.</para>
00286             <para>X's represent 6 digit number beginning with 000000.</para>
00287             <enumlist>
00288                <enum name="NewCat" />
00289                <enum name="RenameCat" />
00290                <enum name="DelCat" />
00291                <enum name="EmptyCat" />
00292                <enum name="Update" />
00293                <enum name="Delete" />
00294                <enum name="Append" />
00295                <enum name="Insert" />
00296             </enumlist>
00297          </parameter>
00298          <parameter name="Cat-XXXXXX">
00299             <para>Category to operate on.</para>
00300             <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
00301          </parameter>
00302          <parameter name="Var-XXXXXX">
00303             <para>Variable to work on.</para>
00304             <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
00305          </parameter>
00306          <parameter name="Value-XXXXXX">
00307             <para>Value to work on.</para>
00308             <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
00309          </parameter>
00310          <parameter name="Match-XXXXXX">
00311             <para>Extra match required to match line.</para>
00312             <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
00313          </parameter>
00314          <parameter name="Line-XXXXXX">
00315             <para>Line in category to operate on (used with delete and insert actions).</para>
00316             <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
00317          </parameter>
00318       </syntax>
00319       <description>
00320          <para>This action will modify, create, or delete configuration elements
00321          in Asterisk configuration files.</para>
00322       </description>
00323    </manager>
00324    <manager name="CreateConfig" language="en_US">
00325       <synopsis>
00326          Creates an empty file in the configuration directory.
00327       </synopsis>
00328       <syntax>
00329          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00330          <parameter name="Filename" required="true">
00331             <para>The configuration filename to create (e.g. <filename>foo.conf</filename>).</para>
00332          </parameter>
00333       </syntax>
00334       <description>
00335          <para>This action will create an empty file in the configuration
00336          directory. This action is intended to be used before an UpdateConfig
00337          action.</para>
00338       </description>
00339    </manager>
00340    <manager name="ListCategories" language="en_US">
00341       <synopsis>
00342          List categories in configuration file.
00343       </synopsis>
00344       <syntax>
00345          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00346          <parameter name="Filename" required="true">
00347             <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
00348          </parameter>
00349       </syntax>
00350       <description>
00351          <para>This action will dump the categories in a given file.</para>
00352       </description>
00353    </manager>
00354    <manager name="Redirect" language="en_US">
00355       <synopsis>
00356          Redirect (transfer) a call.
00357       </synopsis>
00358       <syntax>
00359          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00360          <parameter name="Channel" required="true">
00361             <para>Channel to redirect.</para>
00362          </parameter>
00363          <parameter name="ExtraChannel">
00364             <para>Second call leg to transfer (optional).</para>
00365          </parameter>
00366          <parameter name="Exten" required="true">
00367             <para>Extension to transfer to.</para>
00368          </parameter>
00369          <parameter name="ExtraExten">
00370             <para>Extension to transfer extrachannel to (optional).</para>
00371          </parameter>
00372          <parameter name="Context" required="true">
00373             <para>Context to transfer to.</para>
00374          </parameter>
00375          <parameter name="ExtraContext">
00376             <para>Context to transfer extrachannel to (optional).</para>
00377          </parameter>
00378          <parameter name="Priority" required="true">
00379             <para>Priority to transfer to.</para>
00380          </parameter>
00381          <parameter name="ExtraPriority">
00382             <para>Priority to transfer extrachannel to (optional).</para>
00383          </parameter>
00384       </syntax>
00385       <description>
00386          <para>Redirect (transfer) a call.</para>
00387       </description>
00388    </manager>
00389    <manager name="Atxfer" language="en_US">
00390       <synopsis>
00391          Attended transfer.
00392       </synopsis>
00393       <syntax>
00394          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00395          <parameter name="Channel" required="true">
00396             <para>Transferer's channel.</para>
00397          </parameter>
00398          <parameter name="Exten" required="true">
00399             <para>Extension to transfer to.</para>
00400          </parameter>
00401          <parameter name="Context" required="true">
00402             <para>Context to transfer to.</para>
00403          </parameter>
00404          <parameter name="Priority" required="true">
00405             <para>Priority to transfer to.</para>
00406          </parameter>
00407       </syntax>
00408       <description>
00409          <para>Attended transfer.</para>
00410       </description>
00411    </manager>
00412    <manager name="Originate" language="en_US">
00413       <synopsis>
00414          Originate a call.
00415       </synopsis>
00416       <syntax>
00417          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00418          <parameter name="Channel" required="true">
00419             <para>Channel name to call.</para>
00420          </parameter>
00421          <parameter name="Exten">
00422             <para>Extension to use (requires <literal>Context</literal> and
00423             <literal>Priority</literal>)</para>
00424          </parameter>
00425          <parameter name="Context">
00426             <para>Context to use (requires <literal>Exten</literal> and
00427             <literal>Priority</literal>)</para>
00428          </parameter>
00429          <parameter name="Priority">
00430             <para>Priority to use (requires <literal>Exten</literal> and
00431             <literal>Context</literal>)</para>
00432          </parameter>
00433          <parameter name="Application">
00434             <para>Application to execute.</para>
00435          </parameter>
00436          <parameter name="Data">
00437             <para>Data to use (requires <literal>Application</literal>).</para>
00438          </parameter>
00439          <parameter name="Timeout" default="30000">
00440             <para>How long to wait for call to be answered (in ms.).</para>
00441          </parameter>
00442          <parameter name="CallerID">
00443             <para>Caller ID to be set on the outgoing channel.</para>
00444          </parameter>
00445          <parameter name="Variable">
00446             <para>Channel variable to set, multiple Variable: headers are allowed.</para>
00447          </parameter>
00448          <parameter name="Account">
00449             <para>Account code.</para>
00450          </parameter>
00451          <parameter name="Async">
00452             <para>Set to <literal>true</literal> for fast origination.</para>
00453          </parameter>
00454          <parameter name="Codecs">
00455             <para>Comma-separated list of codecs to use for this call.</para>
00456          </parameter>
00457       </syntax>
00458       <description>
00459          <para>Generates an outgoing call to a
00460          <replaceable>Extension</replaceable>/<replaceable>Context</replaceable>/<replaceable>Priority</replaceable>
00461          or <replaceable>Application</replaceable>/<replaceable>Data</replaceable></para>
00462       </description>
00463    </manager>
00464    <manager name="Command" language="en_US">
00465       <synopsis>
00466          Execute Asterisk CLI Command.
00467       </synopsis>
00468       <syntax>
00469          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00470          <parameter name="Command" required="true">
00471             <para>Asterisk CLI command to run.</para>
00472          </parameter>
00473       </syntax>
00474       <description>
00475          <para>Run a CLI command.</para>
00476       </description>
00477    </manager>
00478    <manager name="ExtensionState" language="en_US">
00479       <synopsis>
00480          Check Extension Status.
00481       </synopsis>
00482       <syntax>
00483          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00484          <parameter name="Exten" required="true">
00485             <para>Extension to check state on.</para>
00486          </parameter>
00487          <parameter name="Context" required="true">
00488             <para>Context for extension.</para>
00489          </parameter>
00490       </syntax>
00491       <description>
00492          <para>Report the extension state for given extension. If the extension has a hint,
00493          will use devicestate to check the status of the device connected to the extension.</para>
00494          <para>Will return an <literal>Extension Status</literal> message. The response will include
00495          the hint for the extension and the status.</para>
00496       </description>
00497    </manager>
00498    <manager name="AbsoluteTimeout" language="en_US">
00499       <synopsis>
00500          Set absolute timeout.
00501       </synopsis>
00502       <syntax>
00503          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00504          <parameter name="Channel" required="true">
00505             <para>Channel name to hangup.</para>
00506          </parameter>
00507          <parameter name="Timeout" required="true">
00508             <para>Maximum duration of the call (sec).</para>
00509          </parameter>
00510       </syntax>
00511       <description>
00512          <para>Hangup a channel after a certain time. Acknowledges set time with
00513          <literal>Timeout Set</literal> message.</para>
00514       </description>
00515    </manager>
00516    <manager name="MailboxStatus" language="en_US">
00517       <synopsis>
00518          Check mailbox.
00519       </synopsis>
00520       <syntax>
00521          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00522          <parameter name="Mailbox" required="true">
00523             <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
00524          </parameter>
00525       </syntax>
00526       <description>
00527          <para>Checks a voicemail account for status.</para>
00528          <para>Returns number of messages.</para>
00529          <para>Message: Mailbox Status.</para>
00530          <para>Mailbox: <replaceable>mailboxid</replaceable>.</para>
00531          <para>Waiting: <replaceable>count</replaceable>.</para>
00532       </description>
00533    </manager>
00534    <manager name="MailboxCount" language="en_US">
00535       <synopsis>
00536          Check Mailbox Message Count.
00537       </synopsis>
00538       <syntax>
00539          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00540          <parameter name="Mailbox" required="true">
00541             <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
00542          </parameter>
00543       </syntax>
00544       <description>
00545          <para>Checks a voicemail account for new messages.</para>
00546          <para>Returns number of urgent, new and old messages.</para>
00547          <para>Message: Mailbox Message Count</para>
00548          <para>Mailbox: <replaceable>mailboxid</replaceable></para>
00549          <para>UrgentMessages: <replaceable>count</replaceable></para>
00550          <para>NewMessages: <replaceable>count</replaceable></para>
00551          <para>OldMessages: <replaceable>count</replaceable></para>
00552       </description>
00553    </manager>
00554    <manager name="ListCommands" language="en_US">
00555       <synopsis>
00556          List available manager commands.
00557       </synopsis>
00558       <syntax>
00559          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00560       </syntax>
00561       <description>
00562          <para>Returns the action name and synopsis for every action that
00563          is available to the user.</para>
00564       </description>
00565    </manager>
00566    <manager name="SendText" language="en_US">
00567       <synopsis>
00568          Send text message to channel.
00569       </synopsis>
00570       <syntax>
00571          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00572          <parameter name="Channel" required="true">
00573             <para>Channel to send message to.</para>
00574          </parameter>
00575          <parameter name="Message" required="true">
00576             <para>Message to send.</para>
00577          </parameter>
00578       </syntax>
00579       <description>
00580          <para>Sends A Text Message to a channel while in a call.</para>
00581       </description>
00582    </manager>
00583    <manager name="UserEvent" language="en_US">
00584       <synopsis>
00585          Send an arbitrary event.
00586       </synopsis>
00587       <syntax>
00588          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00589          <parameter name="UserEvent" required="true">
00590             <para>Event string to send.</para>
00591          </parameter>
00592          <parameter name="Header1">
00593             <para>Content1.</para>
00594          </parameter>
00595          <parameter name="HeaderN">
00596             <para>ContentN.</para>
00597          </parameter>
00598       </syntax>
00599       <description>
00600          <para>Send an event to manager sessions.</para>
00601       </description>
00602    </manager>
00603    <manager name="WaitEvent" language="en_US">
00604       <synopsis>
00605          Wait for an event to occur.
00606       </synopsis>
00607       <syntax>
00608          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00609          <parameter name="Timeout" required="true">
00610             <para>Maximum time (in seconds) to wait for events, <literal>-1</literal> means forever.</para>
00611          </parameter>
00612       </syntax>
00613       <description>
00614          <para>This action will ellicit a <literal>Success</literal> response. Whenever
00615          a manager event is queued. Once WaitEvent has been called on an HTTP manager
00616          session, events will be generated and queued.</para>
00617       </description>
00618    </manager>
00619    <manager name="CoreSettings" language="en_US">
00620       <synopsis>
00621          Show PBX core settings (version etc).
00622       </synopsis>
00623       <syntax>
00624          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00625       </syntax>
00626       <description>
00627          <para>Query for Core PBX settings.</para>
00628       </description>
00629    </manager>
00630    <manager name="CoreStatus" language="en_US">
00631       <synopsis>
00632          Show PBX core status variables.
00633       </synopsis>
00634       <syntax>
00635          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00636       </syntax>
00637       <description>
00638          <para>Query for Core PBX status.</para>
00639       </description>
00640    </manager>
00641    <manager name="Reload" language="en_US">
00642       <synopsis>
00643          Send a reload event.
00644       </synopsis>
00645       <syntax>
00646          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00647          <parameter name="Module">
00648             <para>Name of the module to reload.</para>
00649          </parameter>
00650       </syntax>
00651       <description>
00652          <para>Send a reload event.</para>
00653       </description>
00654    </manager>
00655    <manager name="CoreShowChannels" language="en_US">
00656       <synopsis>
00657          List currently active channels.
00658       </synopsis>
00659       <syntax>
00660          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00661       </syntax>
00662       <description>
00663          <para>List currently defined channels and some information about them.</para>
00664       </description>
00665    </manager>
00666    <manager name="ModuleLoad" language="en_US">
00667       <synopsis>
00668          Module management.
00669       </synopsis>
00670       <syntax>
00671          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00672          <parameter name="Module">
00673             <para>Asterisk module name (including .so extension) or subsystem identifier:</para>
00674             <enumlist>
00675                <enum name="cdr" />
00676                <enum name="enum" />
00677                <enum name="dnsmgr" />
00678                <enum name="extconfig" />
00679                <enum name="manager" />
00680                <enum name="rtp" />
00681                <enum name="http" />
00682             </enumlist>
00683          </parameter>
00684          <parameter name="LoadType" required="true">
00685             <para>The operation to be done on module.</para>
00686             <enumlist>
00687                <enum name="load" />
00688                <enum name="unload" />
00689                <enum name="reload" />
00690             </enumlist>
00691             <para>If no module is specified for a <literal>reload</literal> loadtype,
00692             all modules are reloaded.</para>
00693          </parameter>
00694       </syntax>
00695       <description>
00696          <para>Loads, unloads or reloads an Asterisk module in a running system.</para>
00697       </description>
00698    </manager>
00699    <manager name="ModuleCheck" language="en_US">
00700       <synopsis>
00701          Check if module is loaded.
00702       </synopsis>
00703       <syntax>
00704          <parameter name="Module" required="true">
00705             <para>Asterisk module name (not including extension).</para>
00706          </parameter>
00707       </syntax>
00708       <description>
00709          <para>Checks if Asterisk module is loaded. Will return Success/Failure.
00710          For success returns, the module revision number is included.</para>
00711       </description>
00712    </manager>
00713    <manager name="AOCMessage" language="en_US">
00714       <synopsis>
00715          Generate an Advice of Charge message on a channel.
00716       </synopsis>
00717       <syntax>
00718          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00719          <parameter name="Channel" required="true">
00720             <para>Channel name to generate the AOC message on.</para>
00721          </parameter>
00722          <parameter name="ChannelPrefix">
00723             <para>Partial channel prefix.  By using this option one can match the beginning part
00724             of a channel name without having to put the entire name in.  For example
00725             if a channel name is SIP/snom-00000001 and this value is set to SIP/snom, then
00726             that channel matches and the message will be sent.  Note however that only
00727             the first matched channel has the message sent on it. </para>
00728          </parameter>
00729          <parameter name="MsgType" required="true">
00730             <para>Defines what type of AOC message to create, AOC-D or AOC-E</para>
00731             <enumlist>
00732                <enum name="D" />
00733                <enum name="E" />
00734             </enumlist>
00735          </parameter>
00736          <parameter name="ChargeType" required="true">
00737             <para>Defines what kind of charge this message represents.</para>
00738             <enumlist>
00739                <enum name="NA" />
00740                <enum name="FREE" />
00741                <enum name="Currency" />
00742                <enum name="Unit" />
00743             </enumlist>
00744          </parameter>
00745          <parameter name="UnitAmount(0)">
00746             <para>This represents the amount of units charged. The ETSI AOC standard specifies that
00747             this value along with the optional UnitType value are entries in a list.  To accommodate this
00748             these values take an index value starting at 0 which can be used to generate this list of
00749             unit entries.  For Example, If two unit entires were required this could be achieved by setting the
00750             paramter UnitAmount(0)=1234 and UnitAmount(1)=5678.  Note that UnitAmount at index 0 is
00751             required when ChargeType=Unit, all other entries in the list are optional.
00752             </para>
00753          </parameter>
00754          <parameter name="UnitType(0)">
00755             <para>Defines the type of unit.  ETSI AOC standard specifies this as an integer
00756             value between 1 and 16, but this value is left open to accept any positive
00757             integer.  Like the UnitAmount parameter, this value represents a list entry
00758             and has an index parameter that starts at 0.
00759             </para>
00760          </parameter>
00761          <parameter name="CurrencyName">
00762             <para>Specifies the currency's name.  Note that this value is truncated after 10 characters.</para>
00763          </parameter>
00764          <parameter name="CurrencyAmount">
00765             <para>Specifies the charge unit amount as a positive integer.  This value is required
00766             when ChargeType==Currency.</para>
00767          </parameter>
00768          <parameter name="CurrencyMultiplier">
00769             <para>Specifies the currency multiplier.  This value is required when ChargeType==Currency.</para>
00770             <enumlist>
00771                <enum name="OneThousandth" />
00772                <enum name="OneHundredth" />
00773                <enum name="OneTenth" />
00774                <enum name="One" />
00775                <enum name="Ten" />
00776                <enum name="Hundred" />
00777                <enum name="Thousand" />
00778             </enumlist>
00779          </parameter>
00780          <parameter name="TotalType" default="Total">
00781             <para>Defines what kind of AOC-D total is represented.</para>
00782             <enumlist>
00783                <enum name="Total" />
00784                <enum name="SubTotal" />
00785             </enumlist>
00786          </parameter>
00787          <parameter name="AOCBillingId">
00788             <para>Represents a billing ID associated with an AOC-D or AOC-E message. Note
00789             that only the first 3 items of the enum are valid AOC-D billing IDs</para>
00790             <enumlist>
00791                <enum name="Normal" />
00792                <enum name="ReverseCharge" />
00793                <enum name="CreditCard" />
00794                <enum name="CallFwdUnconditional" />
00795                <enum name="CallFwdBusy" />
00796                <enum name="CallFwdNoReply" />
00797                <enum name="CallDeflection" />
00798                <enum name="CallTransfer" />
00799             </enumlist>
00800          </parameter>
00801          <parameter name="ChargingAssociationId">
00802             <para>Charging association identifier.  This is optional for AOC-E and can be
00803             set to any value between -32768 and 32767</para>
00804          </parameter>
00805          <parameter name="ChargingAssociationNumber">
00806             <para>Represents the charging association party number.  This value is optional
00807             for AOC-E.</para>
00808          </parameter>
00809          <parameter name="ChargingAssociationPlan">
00810             <para>Integer representing the charging plan associated with the ChargingAssociationNumber.
00811             The value is bits 7 through 1 of the Q.931 octet containing the type-of-number and
00812             numbering-plan-identification fields.</para>
00813          </parameter>
00814       </syntax>
00815       <description>
00816          <para>Generates an AOC-D or AOC-E message on a channel.</para>
00817       </description>
00818    </manager>
00819  ***/
00820 
00821 enum error_type {
00822    UNKNOWN_ACTION = 1,
00823    UNKNOWN_CATEGORY,
00824    UNSPECIFIED_CATEGORY,
00825    UNSPECIFIED_ARGUMENT,
00826    FAILURE_ALLOCATION,
00827    FAILURE_NEWCAT,
00828    FAILURE_DELCAT,
00829    FAILURE_EMPTYCAT,
00830    FAILURE_UPDATE,
00831    FAILURE_DELETE,
00832    FAILURE_APPEND
00833 };
00834 
00835 
00836 /*!
00837  * Linked list of events.
00838  * Global events are appended to the list by append_event().
00839  * The usecount is the number of stored pointers to the element,
00840  * excluding the list pointers. So an element that is only in
00841  * the list has a usecount of 0, not 1.
00842  *
00843  * Clients have a pointer to the last event processed, and for each
00844  * of these clients we track the usecount of the elements.
00845  * If we have a pointer to an entry in the list, it is safe to navigate
00846  * it forward because elements will not be deleted, but only appended.
00847  * The worst that can happen is seeing the pointer still NULL.
00848  *
00849  * When the usecount of an element drops to 0, and the element is the
00850  * first in the list, we can remove it. Removal is done within the
00851  * main thread, which is woken up for the purpose.
00852  *
00853  * For simplicity of implementation, we make sure the list is never empty.
00854  */
00855 struct eventqent {
00856    int usecount;     /*!< # of clients who still need the event */
00857    int category;
00858    unsigned int seq; /*!< sequence number */
00859    struct timeval tv;  /*!< When event was allocated */
00860    AST_RWLIST_ENTRY(eventqent) eq_next;
00861    char eventdata[1];   /*!< really variable size, allocated by append_event() */
00862 };
00863 
00864 static AST_RWLIST_HEAD_STATIC(all_events, eventqent);
00865 
00866 static const int DEFAULT_ENABLED       = 0;  /*!< Default setting for manager to be enabled */
00867 static const int DEFAULT_WEBENABLED       = 0;  /*!< Default setting for the web interface to be enabled */
00868 static const int DEFAULT_BLOCKSOCKETS     = 0;  /*!< Default setting for block-sockets */
00869 static const int DEFAULT_DISPLAYCONNECTS  = 1;  /*!< Default setting for displaying manager connections */
00870 static const int DEFAULT_TIMESTAMPEVENTS  = 0;  /*!< Default setting for timestampevents */  
00871 static const int DEFAULT_HTTPTIMEOUT      = 60; /*!< Default manager http timeout */
00872 static const int DEFAULT_BROKENEVENTSACTION  = 0;  /*!< Default setting for brokeneventsaction */
00873 static const int DEFAULT_AUTHTIMEOUT      = 30; /*!< Default setting for authtimeout */
00874 static const int DEFAULT_AUTHLIMIT     = 50; /*!< Default setting for authlimit */
00875 
00876 static int displayconnects;
00877 static int allowmultiplelogin = 1;
00878 static int timestampevents;
00879 static int httptimeout;
00880 static int broken_events_action;
00881 static int manager_enabled = 0;
00882 static int webmanager_enabled = 0;
00883 static int authtimeout;
00884 static int authlimit;
00885 static char *manager_channelvars;
00886 
00887 #define DEFAULT_REALM      "asterisk"
00888 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
00889 
00890 static int block_sockets;
00891 static int unauth_sessions = 0;
00892 
00893 static int manager_debug;  /*!< enable some debugging code in the manager */
00894 
00895 /*! \brief
00896  * Descriptor for a manager session, either on the AMI socket or over HTTP.
00897  *
00898  * \note
00899  * AMI session have managerid == 0; the entry is created upon a connect,
00900  * and destroyed with the socket.
00901  * HTTP sessions have managerid != 0, the value is used as a search key
00902  * to lookup sessions (using the mansession_id cookie, or nonce key from
00903  * Digest Authentication http header).
00904  */
00905 #define MAX_BLACKLIST_CMD_LEN 2
00906 static const struct {
00907    const char *words[AST_MAX_CMD_LEN];
00908 } command_blacklist[] = {
00909    {{ "module", "load", NULL }},
00910    {{ "module", "unload", NULL }},
00911    {{ "restart", "gracefully", NULL }},
00912 };
00913 
00914 /* In order to understand what the heck is going on with the
00915  * mansession_session and mansession structs, we need to have a bit of a history
00916  * lesson.
00917  *
00918  * In the beginning, there was the mansession. The mansession contained data that was
00919  * intrinsic to a manager session, such as the time that it started, the name of the logged-in
00920  * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
00921  * sessions, these were used to represent the TCP socket over which the AMI session was taking
00922  * place. It makes perfect sense for these fields to be a part of the session-specific data since
00923  * the session actually defines this information.
00924  *
00925  * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
00926  * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
00927  * but rather to the action that is being executed. Because a single session may execute many commands
00928  * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
00929  * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
00930  * has had a chance to properly close its handles.
00931  *
00932  * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
00933  * from being run at the same time in a single session. Some manager actions may block for a long time, thus
00934  * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
00935  * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
00936  * part of the action instead.
00937  *
00938  * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
00939  * contain the action-specific information, such as which file to write to. In order to maintain expectations
00940  * of action handlers and not have to change the public API of the manager code, we would need to name this
00941  * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
00942  * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
00943  * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
00944  * data.
00945  */
00946 struct mansession_session {
00947             /* XXX need to document which fields it is protecting */
00948    struct sockaddr_in sin; /*!< address we are connecting from */
00949    FILE *f;    /*!< fdopen() on the underlying fd */
00950    int fd;        /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
00951    int inuse;     /*!< number of HTTP sessions using this entry */
00952    int needdestroy;  /*!< Whether an HTTP session should be destroyed */
00953    pthread_t waiting_thread;  /*!< Sleeping thread using this descriptor */
00954    uint32_t managerid;  /*!< Unique manager identifier, 0 for AMI sessions */
00955    time_t sessionstart;    /*!< Session start time */
00956    struct timeval sessionstart_tv; /*!< Session start time */
00957    time_t sessiontimeout;  /*!< Session timeout if HTTP */
00958    char username[80];   /*!< Logged in username */
00959    char challenge[10];  /*!< Authentication challenge */
00960    int authenticated;   /*!< Authentication status */
00961    int readperm;     /*!< Authorization for reading */
00962    int writeperm;    /*!< Authorization for writing */
00963    char inbuf[1025]; /*!< Buffer */
00964             /* we use the extra byte to add a '\0' and simplify parsing */
00965    int inlen;     /*!< number of buffered bytes */
00966    int send_events;  /*!<  XXX what ? */
00967    struct eventqent *last_ev; /*!< last event processed. */
00968    int writetimeout; /*!< Timeout for ast_carefulwrite() */
00969    time_t authstart;
00970    int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
00971    time_t noncetime; /*!< Timer for nonce value expiration */
00972    struct ao2_container *whitefilters;
00973    struct ao2_container *blackfilters;
00974    unsigned long oldnonce; /*!< Stale nonce value */
00975    unsigned long nc; /*!< incremental  nonce counter */
00976    AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
00977    AST_LIST_ENTRY(mansession_session) list;
00978 };
00979 
00980 /* In case you didn't read that giant block of text above the mansession_session struct, the
00981  * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
00982  * represents data that is different from Manager action to Manager action. The mansession_session pointer
00983  * contained within points to session-specific data.
00984  */
00985 struct mansession {
00986    struct mansession_session *session;
00987    struct ast_tcptls_session_instance *tcptls_session;
00988    FILE *f;
00989    int fd;
00990    int write_error:1;
00991    struct manager_custom_hook *hook;
00992    ast_mutex_t lock;
00993 };
00994 
00995 static struct ao2_container *sessions = NULL;
00996 
00997 struct manager_channel_variable {
00998    AST_LIST_ENTRY(manager_channel_variable) entry;
00999    unsigned int isfunc:1;
01000    char name[0]; /* allocate off the end the real size. */
01001 };
01002 
01003 static AST_RWLIST_HEAD_STATIC(channelvars, manager_channel_variable);
01004 
01005 /*! \brief user descriptor, as read from the config file.
01006  *
01007  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
01008  * lines which are not supported here, and readperm/writeperm/writetimeout
01009  * are not stored.
01010  */
01011 struct ast_manager_user {
01012    char username[80];
01013    char *secret;
01014    struct ast_ha *ha;      /*!< ACL setting */
01015    int readperm;        /*! Authorization for reading */
01016    int writeperm;       /*! Authorization for writing */
01017    int writetimeout;    /*! Per user Timeout for ast_carefulwrite() */
01018    int displayconnects;    /*!< XXX unused */
01019    int keep;         /*!< mark entries created on a reload */
01020    struct ao2_container *whitefilters;
01021    struct ao2_container *blackfilters;
01022    char *a1_hash;       /*!< precalculated A1 for Digest auth */
01023    AST_RWLIST_ENTRY(ast_manager_user) list;
01024 };
01025 
01026 /*! \brief list of users found in the config file */
01027 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
01028 
01029 /*! \brief list of actions registered */
01030 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
01031 
01032 /*! \brief list of hooks registered */
01033 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
01034 
01035 static void free_channelvars(void);
01036 
01037 /*! \brief Add a custom hook to be called when an event is fired */
01038 void ast_manager_register_hook(struct manager_custom_hook *hook)
01039 {
01040    AST_RWLIST_WRLOCK(&manager_hooks);
01041    AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
01042    AST_RWLIST_UNLOCK(&manager_hooks);
01043 }
01044 
01045 /*! \brief Delete a custom hook to be called when an event is fired */
01046 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
01047 {
01048    AST_RWLIST_WRLOCK(&manager_hooks);
01049    AST_RWLIST_REMOVE(&manager_hooks, hook, list);
01050    AST_RWLIST_UNLOCK(&manager_hooks);
01051 }
01052 
01053 int check_manager_enabled()
01054 {
01055    return manager_enabled;
01056 }
01057 
01058 int check_webmanager_enabled()
01059 {
01060    return (webmanager_enabled && manager_enabled);
01061 }
01062 
01063 /*!
01064  * Grab a reference to the last event, update usecount as needed.
01065  * Can handle a NULL pointer.
01066  */
01067 static struct eventqent *grab_last(void)
01068 {
01069    struct eventqent *ret;
01070 
01071    AST_RWLIST_WRLOCK(&all_events);
01072    ret = AST_RWLIST_LAST(&all_events);
01073    /* the list is never empty now, but may become so when
01074     * we optimize it in the future, so be prepared.
01075     */
01076    if (ret) {
01077       ast_atomic_fetchadd_int(&ret->usecount, 1);
01078    }
01079    AST_RWLIST_UNLOCK(&all_events);
01080    return ret;
01081 }
01082 
01083 /*!
01084  * Purge unused events. Remove elements from the head
01085  * as long as their usecount is 0 and there is a next element.
01086  */
01087 static void purge_events(void)
01088 {
01089    struct eventqent *ev;
01090    struct timeval now = ast_tvnow();
01091 
01092    AST_RWLIST_WRLOCK(&all_events);
01093    while ( (ev = AST_RWLIST_FIRST(&all_events)) &&
01094        ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) {
01095       AST_RWLIST_REMOVE_HEAD(&all_events, eq_next);
01096       ast_free(ev);
01097    }
01098 
01099    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&all_events, ev, eq_next) {
01100       /* Never release the last event */
01101       if (!AST_RWLIST_NEXT(ev, eq_next)) {
01102          break;
01103       }
01104 
01105       /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */
01106       if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) {
01107          AST_RWLIST_REMOVE_CURRENT(eq_next);
01108          ast_free(ev);
01109       }
01110    }
01111    AST_RWLIST_TRAVERSE_SAFE_END;
01112    AST_RWLIST_UNLOCK(&all_events);
01113 }
01114 
01115 /*!
01116  * helper functions to convert back and forth between
01117  * string and numeric representation of set of flags
01118  */
01119 static const struct permalias {
01120    int num;
01121    const char *label;
01122 } perms[] = {
01123    { EVENT_FLAG_SYSTEM, "system" },
01124    { EVENT_FLAG_CALL, "call" },
01125    { EVENT_FLAG_LOG, "log" },
01126    { EVENT_FLAG_VERBOSE, "verbose" },
01127    { EVENT_FLAG_COMMAND, "command" },
01128    { EVENT_FLAG_AGENT, "agent" },
01129    { EVENT_FLAG_USER, "user" },
01130    { EVENT_FLAG_CONFIG, "config" },
01131    { EVENT_FLAG_DTMF, "dtmf" },
01132    { EVENT_FLAG_REPORTING, "reporting" },
01133    { EVENT_FLAG_CDR, "cdr" },
01134    { EVENT_FLAG_DIALPLAN, "dialplan" },
01135    { EVENT_FLAG_ORIGINATE, "originate" },
01136    { EVENT_FLAG_AGI, "agi" },
01137    { EVENT_FLAG_CC, "cc" },
01138    { EVENT_FLAG_AOC, "aoc" },
01139    { EVENT_FLAG_TEST, "test" },
01140    { INT_MAX, "all" },
01141    { 0, "none" },
01142 };
01143 
01144 /*! \brief Convert authority code to a list of options */
01145 static const char *authority_to_str(int authority, struct ast_str **res)
01146 {
01147    int i;
01148    char *sep = "";
01149 
01150    ast_str_reset(*res);
01151    for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
01152       if (authority & perms[i].num) {
01153          ast_str_append(res, 0, "%s%s", sep, perms[i].label);
01154          sep = ",";
01155       }
01156    }
01157 
01158    if (ast_str_strlen(*res) == 0)   /* replace empty string with something sensible */
01159       ast_str_append(res, 0, "<none>");
01160 
01161    return ast_str_buffer(*res);
01162 }
01163 
01164 /*! Tells you if smallstr exists inside bigstr
01165    which is delim by delim and uses no buf or stringsep
01166    ast_instring("this|that|more","this",'|') == 1;
01167 
01168    feel free to move this to app.c -anthm */
01169 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
01170 {
01171    const char *val = bigstr, *next;
01172 
01173    do {
01174       if ((next = strchr(val, delim))) {
01175          if (!strncmp(val, smallstr, (next - val))) {
01176             return 1;
01177          } else {
01178             continue;
01179          }
01180       } else {
01181          return !strcmp(smallstr, val);
01182       }
01183    } while (*(val = (next + 1)));
01184 
01185    return 0;
01186 }
01187 
01188 static int get_perm(const char *instr)
01189 {
01190    int x = 0, ret = 0;
01191 
01192    if (!instr) {
01193       return 0;
01194    }
01195 
01196    for (x = 0; x < ARRAY_LEN(perms); x++) {
01197       if (ast_instring(instr, perms[x].label, ',')) {
01198          ret |= perms[x].num;
01199       }
01200    }
01201 
01202    return ret;
01203 }
01204 
01205 /*!
01206  * A number returns itself, false returns 0, true returns all flags,
01207  * other strings return the flags that are set.
01208  */
01209 static int strings_to_mask(const char *string)
01210 {
01211    const char *p;
01212 
01213    if (ast_strlen_zero(string)) {
01214       return -1;
01215    }
01216 
01217    for (p = string; *p; p++) {
01218       if (*p < '0' || *p > '9') {
01219          break;
01220       }
01221    }
01222    if (!*p) { /* all digits */
01223       return atoi(string);
01224    }
01225    if (ast_false(string)) {
01226       return 0;
01227    }
01228    if (ast_true(string)) { /* all permissions */
01229       int x, ret = 0;
01230       for (x = 0; x < ARRAY_LEN(perms); x++) {
01231          ret |= perms[x].num;
01232       }
01233       return ret;
01234    }
01235    return get_perm(string);
01236 }
01237 
01238 /*! \brief Unreference manager session object.
01239      If no more references, then go ahead and delete it */
01240 static struct mansession_session *unref_mansession(struct mansession_session *s)
01241 {
01242    int refcount = ao2_ref(s, -1);
01243         if (manager_debug) {
01244       ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1);
01245    }
01246    return s;
01247 }
01248 
01249 static void event_filter_destructor(void *obj)
01250 {
01251    regex_t *regex_filter = obj;
01252    regfree(regex_filter);
01253 }
01254 
01255 static void session_destructor(void *obj)
01256 {
01257    struct mansession_session *session = obj;
01258    struct eventqent *eqe = session->last_ev;
01259    struct ast_datastore *datastore;
01260 
01261    /* Get rid of each of the data stores on the session */
01262    while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
01263       /* Free the data store */
01264       ast_datastore_free(datastore);
01265    }
01266 
01267    if (session->f != NULL) {
01268       fclose(session->f);
01269    }
01270    if (eqe) {
01271       ast_atomic_fetchadd_int(&eqe->usecount, -1);
01272    }
01273 
01274    if (session->whitefilters) {
01275       ao2_t_callback(session->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
01276       ao2_t_ref(session->whitefilters, -1 , "decrement ref for white container, should be last one");
01277    }
01278 
01279    if (session->blackfilters) {
01280       ao2_t_callback(session->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
01281       ao2_t_ref(session->blackfilters, -1 , "decrement ref for black container, should be last one");
01282    }
01283 }
01284 
01285 /*! \brief Allocate manager session structure and add it to the list of sessions */
01286 static struct mansession_session *build_mansession(struct sockaddr_in sin)
01287 {
01288    struct mansession_session *newsession;
01289 
01290    if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) {
01291       return NULL;
01292    }
01293 
01294    if (!(newsession->whitefilters = ao2_container_alloc(1, NULL, NULL))) {
01295       ao2_ref(newsession, -1);
01296       return NULL;
01297    }
01298 
01299    if (!(newsession->blackfilters = ao2_container_alloc(1, NULL, NULL))) {
01300       ao2_ref(newsession, -1); /* session_destructor will cleanup the other filter */
01301       return NULL;
01302    }
01303 
01304    newsession->fd = -1;
01305    newsession->waiting_thread = AST_PTHREADT_NULL;
01306    newsession->writetimeout = 100;
01307    newsession->send_events = -1;
01308    newsession->sin = sin;
01309 
01310    ao2_link(sessions, newsession);
01311 
01312    return newsession;
01313 }
01314 
01315 static int mansession_cmp_fn(void *obj, void *arg, int flags)
01316 {
01317    struct mansession_session *s = obj;
01318    char *str = arg;
01319    return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
01320 }
01321 
01322 static void session_destroy(struct mansession_session *s)
01323 {
01324    unref_mansession(s);
01325    ao2_unlink(sessions, s);
01326 }
01327 
01328 
01329 static int check_manager_session_inuse(const char *name)
01330 {
01331    struct mansession_session *session = ao2_find(sessions, (char *) name, 0);
01332    int inuse = 0;
01333 
01334    if (session) {
01335       inuse = 1;
01336       unref_mansession(session);
01337    }
01338    return inuse;
01339 }
01340 
01341 
01342 /*!
01343  * lookup an entry in the list of registered users.
01344  * must be called with the list lock held.
01345  */
01346 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
01347 {
01348    struct ast_manager_user *user = NULL;
01349 
01350    AST_RWLIST_TRAVERSE(&users, user, list) {
01351       if (!strcasecmp(user->username, name)) {
01352          break;
01353       }
01354    }
01355 
01356    return user;
01357 }
01358 
01359 /*! \brief Get displayconnects config option.
01360  *  \param session manager session to get parameter from.
01361  *  \return displayconnects config option value.
01362  */
01363 static int manager_displayconnects (struct mansession_session *session)
01364 {
01365    struct ast_manager_user *user = NULL;
01366    int ret = 0;
01367 
01368    AST_RWLIST_RDLOCK(&users);
01369    if ((user = get_manager_by_name_locked (session->username))) {
01370       ret = user->displayconnects;
01371    }
01372    AST_RWLIST_UNLOCK(&users);
01373 
01374    return ret;
01375 }
01376 
01377 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01378 {
01379    struct manager_action *cur;
01380    struct ast_str *authority;
01381    int num, l, which;
01382    char *ret = NULL;
01383 #ifdef AST_XML_DOCS
01384    char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
01385 #endif
01386 
01387    switch (cmd) {
01388    case CLI_INIT:
01389       e->command = "manager show command";
01390       e->usage =
01391          "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
01392          "  Shows the detailed description for a specific Asterisk manager interface command.\n";
01393       return NULL;
01394    case CLI_GENERATE:
01395       l = strlen(a->word);
01396       which = 0;
01397       AST_RWLIST_RDLOCK(&actions);
01398       AST_RWLIST_TRAVERSE(&actions, cur, list) {
01399          if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
01400             ret = ast_strdup(cur->action);
01401             break;   /* make sure we exit even if ast_strdup() returns NULL */
01402          }
01403       }
01404       AST_RWLIST_UNLOCK(&actions);
01405       return ret;
01406    }
01407    authority = ast_str_alloca(80);
01408    if (a->argc < 4) {
01409       return CLI_SHOWUSAGE;
01410    }
01411 
01412 #ifdef AST_XML_DOCS
01413    /* setup the titles */
01414    term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
01415    term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
01416    term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
01417    term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
01418    term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
01419 #endif
01420 
01421    AST_RWLIST_RDLOCK(&actions);
01422    AST_RWLIST_TRAVERSE(&actions, cur, list) {
01423       for (num = 3; num < a->argc; num++) {
01424          if (!strcasecmp(cur->action, a->argv[num])) {
01425 #ifdef AST_XML_DOCS
01426             if (cur->docsrc == AST_XML_DOC) {
01427                ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n",
01428                   syntax_title,
01429                   ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1),
01430                   synopsis_title,
01431                   ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1),
01432                   description_title,
01433                   ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1),
01434                   arguments_title,
01435                   ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1),
01436                   seealso_title,
01437                   ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1));
01438             } else {
01439 #endif
01440                ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
01441                      cur->action, cur->synopsis,
01442                      authority_to_str(cur->authority, &authority),
01443                      S_OR(cur->description, ""));
01444 #ifdef AST_XML_DOCS
01445             }
01446 #endif
01447          }
01448       }
01449    }
01450    AST_RWLIST_UNLOCK(&actions);
01451 
01452    return CLI_SUCCESS;
01453 }
01454 
01455 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01456 {
01457    switch (cmd) {
01458    case CLI_INIT:
01459       e->command = "manager set debug [on|off]";
01460       e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
01461       return NULL;
01462    case CLI_GENERATE:
01463       return NULL;
01464    }
01465 
01466    if (a->argc == 3) {
01467       ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
01468    } else if (a->argc == 4) {
01469       if (!strcasecmp(a->argv[3], "on")) {
01470          manager_debug = 1;
01471       } else if (!strcasecmp(a->argv[3], "off")) {
01472          manager_debug = 0;
01473       } else {
01474          return CLI_SHOWUSAGE;
01475       }
01476    }
01477    return CLI_SUCCESS;
01478 }
01479 
01480 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01481 {
01482    struct ast_manager_user *user = NULL;
01483    int l, which;
01484    char *ret = NULL;
01485    struct ast_str *rauthority = ast_str_alloca(128);
01486    struct ast_str *wauthority = ast_str_alloca(128);
01487 
01488    switch (cmd) {
01489    case CLI_INIT:
01490       e->command = "manager show user";
01491       e->usage =
01492          " Usage: manager show user <user>\n"
01493          "        Display all information related to the manager user specified.\n";
01494       return NULL;
01495    case CLI_GENERATE:
01496       l = strlen(a->word);
01497       which = 0;
01498       if (a->pos != 3) {
01499          return NULL;
01500       }
01501       AST_RWLIST_RDLOCK(&users);
01502       AST_RWLIST_TRAVERSE(&users, user, list) {
01503          if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
01504             ret = ast_strdup(user->username);
01505             break;
01506          }
01507       }
01508       AST_RWLIST_UNLOCK(&users);
01509       return ret;
01510    }
01511 
01512    if (a->argc != 4) {
01513       return CLI_SHOWUSAGE;
01514    }
01515 
01516    AST_RWLIST_RDLOCK(&users);
01517 
01518    if (!(user = get_manager_by_name_locked(a->argv[3]))) {
01519       ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
01520       AST_RWLIST_UNLOCK(&users);
01521       return CLI_SUCCESS;
01522    }
01523 
01524    ast_cli(a->fd, "\n");
01525    ast_cli(a->fd,
01526       "       username: %s\n"
01527       "         secret: %s\n"
01528       "            acl: %s\n"
01529       "      read perm: %s\n"
01530       "     write perm: %s\n"
01531       "displayconnects: %s\n",
01532       (user->username ? user->username : "(N/A)"),
01533       (user->secret ? "<Set>" : "(N/A)"),
01534       (user->ha ? "yes" : "no"),
01535       authority_to_str(user->readperm, &rauthority),
01536       authority_to_str(user->writeperm, &wauthority),
01537       (user->displayconnects ? "yes" : "no"));
01538 
01539    AST_RWLIST_UNLOCK(&users);
01540 
01541    return CLI_SUCCESS;
01542 }
01543 
01544 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01545 {
01546    struct ast_manager_user *user = NULL;
01547    int count_amu = 0;
01548    switch (cmd) {
01549    case CLI_INIT:
01550       e->command = "manager show users";
01551       e->usage =
01552          "Usage: manager show users\n"
01553          "       Prints a listing of all managers that are currently configured on that\n"
01554          " system.\n";
01555       return NULL;
01556    case CLI_GENERATE:
01557       return NULL;
01558    }
01559    if (a->argc != 3) {
01560       return CLI_SHOWUSAGE;
01561    }
01562 
01563    AST_RWLIST_RDLOCK(&users);
01564 
01565    /* If there are no users, print out something along those lines */
01566    if (AST_RWLIST_EMPTY(&users)) {
01567       ast_cli(a->fd, "There are no manager users.\n");
01568       AST_RWLIST_UNLOCK(&users);
01569       return CLI_SUCCESS;
01570    }
01571 
01572    ast_cli(a->fd, "\nusername\n--------\n");
01573 
01574    AST_RWLIST_TRAVERSE(&users, user, list) {
01575       ast_cli(a->fd, "%s\n", user->username);
01576       count_amu++;
01577    }
01578 
01579    AST_RWLIST_UNLOCK(&users);
01580 
01581    ast_cli(a->fd,"-------------------\n"
01582             "%d manager users configured.\n", count_amu);
01583    return CLI_SUCCESS;
01584 }
01585 
01586 /*! \brief  CLI command  manager list commands */
01587 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01588 {
01589    struct manager_action *cur;
01590    struct ast_str *authority;
01591 #define HSMC_FORMAT "  %-15.15s  %-15.15s  %-55.55s\n"
01592    switch (cmd) {
01593    case CLI_INIT:
01594       e->command = "manager show commands";
01595       e->usage =
01596          "Usage: manager show commands\n"
01597          "  Prints a listing of all the available Asterisk manager interface commands.\n";
01598       return NULL;
01599    case CLI_GENERATE:
01600       return NULL;
01601    }
01602    authority = ast_str_alloca(80);
01603    ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
01604    ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
01605 
01606    AST_RWLIST_RDLOCK(&actions);
01607    AST_RWLIST_TRAVERSE(&actions, cur, list)
01608       ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
01609    AST_RWLIST_UNLOCK(&actions);
01610 
01611    return CLI_SUCCESS;
01612 }
01613 
01614 /*! \brief CLI command manager list connected */
01615 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01616 {
01617    struct mansession_session *session;
01618    time_t now = time(NULL);
01619 #define HSMCONN_FORMAT1 "  %-15.15s  %-15.15s  %-10.10s  %-10.10s  %-8.8s  %-8.8s  %-5.5s  %-5.5s\n"
01620 #define HSMCONN_FORMAT2 "  %-15.15s  %-15.15s  %-10d  %-10d  %-8d  %-8d  %-5.5d  %-5.5d\n"
01621    int count = 0;
01622    struct ao2_iterator i;
01623 
01624    switch (cmd) {
01625    case CLI_INIT:
01626       e->command = "manager show connected";
01627       e->usage =
01628          "Usage: manager show connected\n"
01629          "  Prints a listing of the users that are currently connected to the\n"
01630          "Asterisk manager interface.\n";
01631       return NULL;
01632    case CLI_GENERATE:
01633       return NULL;
01634    }
01635 
01636    ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
01637 
01638    i = ao2_iterator_init(sessions, 0);
01639    while ((session = ao2_iterator_next(&i))) {
01640       ao2_lock(session);
01641       ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm);
01642       count++;
01643       ao2_unlock(session);
01644       unref_mansession(session);
01645    }
01646    ao2_iterator_destroy(&i);
01647    ast_cli(a->fd, "%d users connected.\n", count);
01648 
01649    return CLI_SUCCESS;
01650 }
01651 
01652 /*! \brief CLI command manager list eventq */
01653 /* Should change to "manager show connected" */
01654 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01655 {
01656    struct eventqent *s;
01657    switch (cmd) {
01658    case CLI_INIT:
01659       e->command = "manager show eventq";
01660       e->usage =
01661          "Usage: manager show eventq\n"
01662          "  Prints a listing of all events pending in the Asterisk manger\n"
01663          "event queue.\n";
01664       return NULL;
01665    case CLI_GENERATE:
01666       return NULL;
01667    }
01668    AST_RWLIST_RDLOCK(&all_events);
01669    AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
01670       ast_cli(a->fd, "Usecount: %d\n", s->usecount);
01671       ast_cli(a->fd, "Category: %d\n", s->category);
01672       ast_cli(a->fd, "Event:\n%s", s->eventdata);
01673    }
01674    AST_RWLIST_UNLOCK(&all_events);
01675 
01676    return CLI_SUCCESS;
01677 }
01678 
01679 /*! \brief CLI command manager reload */
01680 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01681 {
01682    switch (cmd) {
01683    case CLI_INIT:
01684       e->command = "manager reload";
01685       e->usage =
01686          "Usage: manager reload\n"
01687          "       Reloads the manager configuration.\n";
01688       return NULL;
01689    case CLI_GENERATE:
01690       return NULL;
01691    }
01692    if (a->argc > 2) {
01693       return CLI_SHOWUSAGE;
01694    }
01695    reload_manager();
01696    return CLI_SUCCESS;
01697 }
01698 
01699 static struct eventqent *advance_event(struct eventqent *e)
01700 {
01701    struct eventqent *next;
01702 
01703    AST_RWLIST_RDLOCK(&all_events);
01704    if ((next = AST_RWLIST_NEXT(e, eq_next))) {
01705       ast_atomic_fetchadd_int(&next->usecount, 1);
01706       ast_atomic_fetchadd_int(&e->usecount, -1);
01707    }
01708    AST_RWLIST_UNLOCK(&all_events);
01709    return next;
01710 }
01711 
01712 /*
01713  * Generic function to return either the first or the last matching header
01714  * from a list of variables, possibly skipping empty strings.
01715  * At the moment there is only one use of this function in this file,
01716  * so we make it static.
01717  */
01718 #define  GET_HEADER_FIRST_MATCH  0
01719 #define  GET_HEADER_LAST_MATCH   1
01720 #define  GET_HEADER_SKIP_EMPTY   2
01721 static const char *__astman_get_header(const struct message *m, char *var, int mode)
01722 {
01723    int x, l = strlen(var);
01724    const char *result = "";
01725 
01726    for (x = 0; x < m->hdrcount; x++) {
01727       const char *h = m->headers[x];
01728       if (!strncasecmp(var, h, l) && h[l] == ':') {
01729          const char *value = h + l + 1;
01730          value = ast_skip_blanks(value); /* ignore leading spaces in the value */
01731          /* found a potential candidate */
01732          if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
01733             continue;   /* not interesting */
01734          if (mode & GET_HEADER_LAST_MATCH)
01735             result = value;   /* record the last match so far */
01736          else
01737             return value;
01738       }
01739    }
01740 
01741    return result;
01742 }
01743 
01744 /*
01745  * Return the first matching variable from an array.
01746  * This is the legacy function and is implemented in therms of
01747  * __astman_get_header().
01748  */
01749 const char *astman_get_header(const struct message *m, char *var)
01750 {
01751    return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
01752 }
01753 
01754 
01755 struct ast_variable *astman_get_variables(const struct message *m)
01756 {
01757    int varlen, x, y;
01758    struct ast_variable *head = NULL, *cur;
01759 
01760    AST_DECLARE_APP_ARGS(args,
01761       AST_APP_ARG(vars)[32];
01762    );
01763 
01764    varlen = strlen("Variable: ");
01765 
01766    for (x = 0; x < m->hdrcount; x++) {
01767       char *parse, *var, *val;
01768 
01769       if (strncasecmp("Variable: ", m->headers[x], varlen)) {
01770          continue;
01771       }
01772       parse = ast_strdupa(m->headers[x] + varlen);
01773 
01774       AST_STANDARD_APP_ARGS(args, parse);
01775       if (!args.argc) {
01776          continue;
01777       }
01778       for (y = 0; y < args.argc; y++) {
01779          if (!args.vars[y]) {
01780             continue;
01781          }
01782          var = val = ast_strdupa(args.vars[y]);
01783          strsep(&val, "=");
01784          if (!val || ast_strlen_zero(var)) {
01785             continue;
01786          }
01787          cur = ast_variable_new(var, val, "");
01788          cur->next = head;
01789          head = cur;
01790       }
01791    }
01792 
01793    return head;
01794 }
01795 
01796 /* access for hooks to send action messages to ami */
01797 
01798 int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
01799 {
01800    const char *action;
01801    int ret = 0;
01802    struct manager_action *tmp;
01803    struct mansession s = {.session = NULL, };
01804    struct message m = { 0 };
01805    char header_buf[1025] = { '\0' };
01806    const char *src = msg;
01807    int x = 0;
01808    int curlen;
01809 
01810    if (hook == NULL) {
01811       return -1;
01812    }
01813 
01814    /* convert msg string to message struct */
01815    curlen = strlen(msg);
01816    for (x = 0; x < curlen; x++) {
01817       int cr;  /* set if we have \r */
01818       if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
01819          cr = 2;  /* Found. Update length to include \r\n */
01820       else if (src[x] == '\n')
01821          cr = 1;  /* also accept \n only */
01822       else
01823          continue;
01824       /* don't copy empty lines */
01825       if (x) {
01826          memmove(header_buf, src, x);  /*... but trim \r\n */
01827          header_buf[x] = '\0';      /* terminate the string */
01828          m.headers[m.hdrcount++] = ast_strdupa(header_buf);
01829       }
01830       x += cr;
01831       curlen -= x;      /* remaining size */
01832       src += x;      /* update pointer */
01833       x = -1;        /* reset loop */
01834    }
01835 
01836    action = astman_get_header(&m,"Action");
01837    if (action && strcasecmp(action,"login")) {
01838 
01839       AST_RWLIST_RDLOCK(&actions);
01840       AST_RWLIST_TRAVERSE(&actions, tmp, list) {
01841          if (strcasecmp(action, tmp->action))
01842             continue;
01843          /*
01844          * we have to simulate a session for this action request
01845          * to be able to pass it down for processing
01846          * This is necessary to meet the previous design of manager.c
01847          */
01848          s.hook = hook;
01849          s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
01850          ret = tmp->func(&s, &m);
01851          break;
01852       }
01853       AST_RWLIST_UNLOCK(&actions);
01854    }
01855    return ret;
01856 }
01857 
01858 
01859 /*!
01860  * helper function to send a string to the socket.
01861  * Return -1 on error (e.g. buffer full).
01862  */
01863 static int send_string(struct mansession *s, char *string)
01864 {
01865    int res;
01866    FILE *f = s->f ? s->f : s->session->f;
01867    int fd = s->f ? s->fd : s->session->fd;
01868 
01869    /* It's a result from one of the hook's action invocation */
01870    if (s->hook) {
01871       /*
01872        * to send responses, we're using the same function
01873        * as for receiving events. We call the event "HookResponse"
01874        */
01875       s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
01876       return 0;
01877    }
01878        
01879    if ((res = ast_careful_fwrite(f, fd, string, strlen(string), s->session->writetimeout))) {
01880       s->write_error = 1;
01881    }
01882 
01883    return res;
01884 }
01885 
01886 /*!
01887  * \brief thread local buffer for astman_append
01888  *
01889  * \note This can not be defined within the astman_append() function
01890  *       because it declares a couple of functions that get used to
01891  *       initialize the thread local storage key.
01892  */
01893 AST_THREADSTORAGE(astman_append_buf);
01894 AST_THREADSTORAGE(userevent_buf);
01895 
01896 /*! \brief initial allocated size for the astman_append_buf */
01897 #define ASTMAN_APPEND_BUF_INITSIZE   256
01898 
01899 /*!
01900  * utility functions for creating AMI replies
01901  */
01902 void astman_append(struct mansession *s, const char *fmt, ...)
01903 {
01904    va_list ap;
01905    struct ast_str *buf;
01906 
01907    if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
01908       return;
01909    }
01910 
01911    va_start(ap, fmt);
01912    ast_str_set_va(&buf, 0, fmt, ap);
01913    va_end(ap);
01914 
01915    if (s->f != NULL || s->session->f != NULL) {
01916       send_string(s, ast_str_buffer(buf));
01917    } else {
01918       ast_verbose("fd == -1 in astman_append, should not happen\n");
01919    }
01920 }
01921 
01922 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
01923    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
01924    hold the session lock _or_ be running in an action callback (in which case s->session->busy will
01925    be non-zero). In either of these cases, there is no need to lock-protect the session's
01926    fd, since no other output will be sent (events will be queued), and no input will
01927    be read until either the current action finishes or get_input() obtains the session
01928    lock.
01929  */
01930 
01931 /*! \brief send a response with an optional message,
01932  * and terminate it with an empty line.
01933  * m is used only to grab the 'ActionID' field.
01934  *
01935  * Use the explicit constant MSG_MOREDATA to remove the empty line.
01936  * XXX MSG_MOREDATA should go to a header file.
01937  */
01938 #define MSG_MOREDATA ((char *)astman_send_response)
01939 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
01940 {
01941    const char *id = astman_get_header(m, "ActionID");
01942 
01943    astman_append(s, "Response: %s\r\n", resp);
01944    if (!ast_strlen_zero(id)) {
01945       astman_append(s, "ActionID: %s\r\n", id);
01946    }
01947    if (listflag) {
01948       astman_append(s, "EventList: %s\r\n", listflag);   /* Start, complete, cancelled */
01949    }
01950    if (msg == MSG_MOREDATA) {
01951       return;
01952    } else if (msg) {
01953       astman_append(s, "Message: %s\r\n\r\n", msg);
01954    } else {
01955       astman_append(s, "\r\n");
01956    }
01957 }
01958 
01959 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
01960 {
01961    astman_send_response_full(s, m, resp, msg, NULL);
01962 }
01963 
01964 void astman_send_error(struct mansession *s, const struct message *m, char *error)
01965 {
01966    astman_send_response_full(s, m, "Error", error, NULL);
01967 }
01968 
01969 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
01970 {
01971    astman_send_response_full(s, m, "Success", msg, NULL);
01972 }
01973 
01974 static void astman_start_ack(struct mansession *s, const struct message *m)
01975 {
01976    astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
01977 }
01978 
01979 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
01980 {
01981    astman_send_response_full(s, m, "Success", msg, listflag);
01982 }
01983 
01984 /*! \brief Lock the 'mansession' structure. */
01985 static void mansession_lock(struct mansession *s)
01986 {
01987    ast_mutex_lock(&s->lock);
01988 }
01989 
01990 /*! \brief Unlock the 'mansession' structure. */
01991 static void mansession_unlock(struct mansession *s)
01992 {
01993    ast_mutex_unlock(&s->lock);
01994 }
01995 
01996 /*! \brief
01997    Rather than braindead on,off this now can also accept a specific int mask value
01998    or a ',' delim list of mask strings (the same as manager.conf) -anthm
01999 */
02000 static int set_eventmask(struct mansession *s, const char *eventmask)
02001 {
02002    int maskint = strings_to_mask(eventmask);
02003 
02004    ao2_lock(s->session);
02005    if (maskint >= 0) {
02006       s->session->send_events = maskint;
02007    }
02008    ao2_unlock(s->session);
02009 
02010    return maskint;
02011 }
02012 
02013 static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
02014 {
02015    return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
02016          AST_SECURITY_EVENT_TRANSPORT_TCP;
02017 }
02018 
02019 static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
02020       struct sockaddr_in *sin_local)
02021 {
02022    ast_sockaddr_to_sin(&s->tcptls_session->parent->local_address,
02023              sin_local);
02024 
02025    return sin_local;
02026 }
02027 
02028 static void report_invalid_user(const struct mansession *s, const char *username)
02029 {
02030    struct sockaddr_in sin_local;
02031    char session_id[32];
02032    struct ast_security_event_inval_acct_id inval_acct_id = {
02033       .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
02034       .common.version    = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
02035       .common.service    = "AMI",
02036       .common.account_id = username,
02037       .common.session_tv = &s->session->sessionstart_tv,
02038       .common.local_addr = {
02039          .sin       = mansession_encode_sin_local(s, &sin_local),
02040          .transport = mansession_get_transport(s),
02041       },
02042       .common.remote_addr = {
02043          .sin       = &s->session->sin,
02044          .transport = mansession_get_transport(s),
02045       },
02046       .common.session_id = session_id,
02047    };
02048 
02049    snprintf(session_id, sizeof(session_id), "%p", s);
02050 
02051    ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
02052 }
02053 
02054 static void report_failed_acl(const struct mansession *s, const char *username)
02055 {
02056    struct sockaddr_in sin_local;
02057    char session_id[32];
02058    struct ast_security_event_failed_acl failed_acl_event = {
02059       .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
02060       .common.version    = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
02061       .common.service    = "AMI",
02062       .common.account_id = username,
02063       .common.session_tv = &s->session->sessionstart_tv,
02064       .common.local_addr = {
02065          .sin       = mansession_encode_sin_local(s, &sin_local),
02066          .transport = mansession_get_transport(s),
02067       },
02068       .common.remote_addr = {
02069          .sin       = &s->session->sin,
02070          .transport = mansession_get_transport(s),
02071       },
02072       .common.session_id = session_id,
02073    };
02074 
02075    snprintf(session_id, sizeof(session_id), "%p", s->session);
02076 
02077    ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
02078 }
02079 
02080 static void report_inval_password(const struct mansession *s, const char *username)
02081 {
02082    struct sockaddr_in sin_local;
02083    char session_id[32];
02084    struct ast_security_event_inval_password inval_password = {
02085       .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
02086       .common.version    = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
02087       .common.service    = "AMI",
02088       .common.account_id = username,
02089       .common.session_tv = &s->session->sessionstart_tv,
02090       .common.local_addr = {
02091          .sin       = mansession_encode_sin_local(s, &sin_local),
02092          .transport = mansession_get_transport(s),
02093       },
02094       .common.remote_addr = {
02095          .sin       = &s->session->sin,
02096          .transport = mansession_get_transport(s),
02097       },
02098       .common.session_id = session_id,
02099    };
02100 
02101    snprintf(session_id, sizeof(session_id), "%p", s->session);
02102 
02103    ast_security_event_report(AST_SEC_EVT(&inval_password));
02104 }
02105 
02106 static void report_auth_success(const struct mansession *s)
02107 {
02108    struct sockaddr_in sin_local;
02109    char session_id[32];
02110    struct ast_security_event_successful_auth successful_auth = {
02111       .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
02112       .common.version    = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
02113       .common.service    = "AMI",
02114       .common.account_id = s->session->username,
02115       .common.session_tv = &s->session->sessionstart_tv,
02116       .common.local_addr = {
02117          .sin       = mansession_encode_sin_local(s, &sin_local),
02118          .transport = mansession_get_transport(s),
02119       },
02120       .common.remote_addr = {
02121          .sin       = &s->session->sin,
02122          .transport = mansession_get_transport(s),
02123       },
02124       .common.session_id = session_id,
02125    };
02126 
02127    snprintf(session_id, sizeof(session_id), "%p", s->session);
02128 
02129    ast_security_event_report(AST_SEC_EVT(&successful_auth));
02130 }
02131 
02132 static void report_req_not_allowed(const struct mansession *s, const char *action)
02133 {
02134    struct sockaddr_in sin_local;
02135    char session_id[32];
02136    char request_type[64];
02137    struct ast_security_event_req_not_allowed req_not_allowed = {
02138       .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
02139       .common.version    = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
02140       .common.service    = "AMI",
02141       .common.account_id = s->session->username,
02142       .common.session_tv = &s->session->sessionstart_tv,
02143       .common.local_addr = {
02144          .sin       = mansession_encode_sin_local(s, &sin_local),
02145          .transport = mansession_get_transport(s),
02146       },
02147       .common.remote_addr = {
02148          .sin       = &s->session->sin,
02149          .transport = mansession_get_transport(s),
02150       },
02151       .common.session_id = session_id,
02152 
02153       .request_type      = request_type,
02154    };
02155 
02156    snprintf(session_id, sizeof(session_id), "%p", s->session);
02157    snprintf(request_type, sizeof(request_type), "Action: %s", action);
02158 
02159    ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
02160 }
02161 
02162 static void report_req_bad_format(const struct mansession *s, const char *action)
02163 {
02164    struct sockaddr_in sin_local;
02165    char session_id[32];
02166    char request_type[64];
02167    struct ast_security_event_req_bad_format req_bad_format = {
02168       .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
02169       .common.version    = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
02170       .common.service    = "AMI",
02171       .common.account_id = s->session->username,
02172       .common.session_tv = &s->session->sessionstart_tv,
02173       .common.local_addr = {
02174          .sin       = mansession_encode_sin_local(s, &sin_local),
02175          .transport = mansession_get_transport(s),
02176       },
02177       .common.remote_addr = {
02178          .sin       = &s->session->sin,
02179          .transport = mansession_get_transport(s),
02180       },
02181       .common.session_id = session_id,
02182 
02183       .request_type      = request_type,
02184    };
02185 
02186    snprintf(session_id, sizeof(session_id), "%p", s->session);
02187    snprintf(request_type, sizeof(request_type), "Action: %s", action);
02188 
02189    ast_security_event_report(AST_SEC_EVT(&req_bad_format));
02190 }
02191 
02192 static void report_failed_challenge_response(const struct mansession *s,
02193       const char *response, const char *expected_response)
02194 {
02195    struct sockaddr_in sin_local;
02196    char session_id[32];
02197    struct ast_security_event_chal_resp_failed chal_resp_failed = {
02198       .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
02199       .common.version    = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
02200       .common.service    = "AMI",
02201       .common.account_id = s->session->username,
02202       .common.session_tv = &s->session->sessionstart_tv,
02203       .common.local_addr = {
02204          .sin       = mansession_encode_sin_local(s, &sin_local),
02205          .transport = mansession_get_transport(s),
02206       },
02207       .common.remote_addr = {
02208          .sin       = &s->session->sin,
02209          .transport = mansession_get_transport(s),
02210       },
02211       .common.session_id = session_id,
02212 
02213       .challenge         = s->session->challenge,
02214       .response          = response,
02215       .expected_response = expected_response,
02216    };
02217 
02218    snprintf(session_id, sizeof(session_id), "%p", s->session);
02219 
02220    ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
02221 }
02222 
02223 static void report_session_limit(const struct mansession *s)
02224 {
02225    struct sockaddr_in sin_local;
02226    char session_id[32];
02227    struct ast_security_event_session_limit session_limit = {
02228       .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
02229       .common.version    = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
02230       .common.service    = "AMI",
02231       .common.account_id = s->session->username,
02232       .common.session_tv = &s->session->sessionstart_tv,
02233       .common.local_addr = {
02234          .sin       = mansession_encode_sin_local(s, &sin_local),
02235          .transport = mansession_get_transport(s),
02236       },
02237       .common.remote_addr = {
02238          .sin       = &s->session->sin,
02239          .transport = mansession_get_transport(s),
02240       },
02241       .common.session_id = session_id,
02242    };
02243 
02244    snprintf(session_id, sizeof(session_id), "%p", s->session);
02245 
02246    ast_security_event_report(AST_SEC_EVT(&session_limit));
02247 }
02248 
02249 /*
02250  * Here we start with action_ handlers for AMI actions,
02251  * and the internal functions used by them.
02252  * Generally, the handlers are called action_foo()
02253  */
02254 
02255 /* helper function for action_login() */
02256 static int authenticate(struct mansession *s, const struct message *m)
02257 {
02258    const char *username = astman_get_header(m, "Username");
02259    const char *password = astman_get_header(m, "Secret");
02260    int error = -1;
02261    struct ast_manager_user *user = NULL;
02262    regex_t *regex_filter;
02263    struct ao2_iterator filter_iter;
02264    struct ast_sockaddr addr;
02265 
02266    if (ast_strlen_zero(username)) { /* missing username */
02267       return -1;
02268    }
02269 
02270    /* locate user in locked state */
02271    AST_RWLIST_WRLOCK(&users);
02272 
02273    ast_sockaddr_from_sin(&addr, &s->session->sin);
02274 
02275    if (!(user = get_manager_by_name_locked(username))) {
02276       report_invalid_user(s, username);
02277       ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
02278    } else if (user->ha && !ast_apply_ha(user->ha, &addr)) {
02279       report_failed_acl(s, username);
02280       ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
02281    } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
02282       const char *key = astman_get_header(m, "Key");
02283       if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
02284          int x;
02285          int len = 0;
02286          char md5key[256] = "";
02287          struct MD5Context md5;
02288          unsigned char digest[16];
02289 
02290          MD5Init(&md5);
02291          MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
02292          MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
02293          MD5Final(digest, &md5);
02294          for (x = 0; x < 16; x++)
02295             len += sprintf(md5key + len, "%2.2x", digest[x]);
02296          if (!strcmp(md5key, key)) {
02297             error = 0;
02298          } else {
02299             report_failed_challenge_response(s, key, md5key);
02300          }
02301       } else {
02302          ast_debug(1, "MD5 authentication is not possible.  challenge: '%s'\n",
02303             S_OR(s->session->challenge, ""));
02304       }
02305    } else if (user->secret) {
02306       if (!strcmp(password, user->secret)) {
02307          error = 0;
02308       } else {
02309          report_inval_password(s, username);
02310       }
02311    }
02312 
02313    if (error) {
02314       ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
02315       AST_RWLIST_UNLOCK(&users);
02316       return -1;
02317    }
02318 
02319    /* auth complete */
02320 
02321    /* All of the user parameters are copied to the session so that in the event
02322      * of a reload and a configuration change, the session parameters are not
02323      * changed. */
02324    ast_copy_string(s->session->username, username, sizeof(s->session->username));
02325    s->session->readperm = user->readperm;
02326    s->session->writeperm = user->writeperm;
02327    s->session->writetimeout = user->writetimeout;
02328 
02329    filter_iter = ao2_iterator_init(user->whitefilters, 0);
02330    while ((regex_filter = ao2_iterator_next(&filter_iter))) {
02331       ao2_t_link(s->session->whitefilters, regex_filter, "add white user filter to session");
02332       ao2_t_ref(regex_filter, -1, "remove iterator ref");
02333    }
02334    ao2_iterator_destroy(&filter_iter);
02335 
02336    filter_iter = ao2_iterator_init(user->blackfilters, 0);
02337    while ((regex_filter = ao2_iterator_next(&filter_iter))) {
02338       ao2_t_link(s->session->blackfilters, regex_filter, "add black user filter to session");
02339       ao2_t_ref(regex_filter, -1, "remove iterator ref");
02340    }
02341    ao2_iterator_destroy(&filter_iter);
02342 
02343    s->session->sessionstart = time(NULL);
02344    s->session->sessionstart_tv = ast_tvnow();
02345    set_eventmask(s, astman_get_header(m, "Events"));
02346 
02347    report_auth_success(s);
02348 
02349    AST_RWLIST_UNLOCK(&users);
02350    return 0;
02351 }
02352 
02353 static int action_ping(struct mansession *s, const struct message *m)
02354 {
02355    const char *actionid = astman_get_header(m, "ActionID");
02356    struct timeval now = ast_tvnow();
02357 
02358    astman_append(s, "Response: Success\r\n");
02359    if (!ast_strlen_zero(actionid)){
02360       astman_append(s, "ActionID: %s\r\n", actionid);
02361    }
02362    astman_append(
02363       s,
02364       "Ping: Pong\r\n"
02365       "Timestamp: %ld.%06lu\r\n"
02366       "\r\n",
02367       (long) now.tv_sec, (unsigned long) now.tv_usec);
02368    return 0;
02369 }
02370 
02371 static int action_getconfig(struct mansession *s, const struct message *m)
02372 {
02373    struct ast_config *cfg;
02374    const char *fn = astman_get_header(m, "Filename");
02375    const char *category = astman_get_header(m, "Category");
02376    int catcount = 0;
02377    int lineno = 0;
02378    char *cur_category = NULL;
02379    struct ast_variable *v;
02380    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
02381 
02382    if (ast_strlen_zero(fn)) {
02383       astman_send_error(s, m, "Filename not specified");
02384       return 0;
02385    }
02386    cfg = ast_config_load2(fn, "manager", config_flags);
02387    if (cfg == CONFIG_STATUS_FILEMISSING) {
02388       astman_send_error(s, m, "Config file not found");
02389       return 0;
02390    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02391       astman_send_error(s, m, "Config file has invalid format");
02392       return 0;
02393    }
02394 
02395    astman_start_ack(s, m);
02396    while ((cur_category = ast_category_browse(cfg, cur_category))) {
02397       if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
02398          lineno = 0;
02399          astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
02400          for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
02401             astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
02402          }
02403          catcount++;
02404       }
02405    }
02406    if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
02407       astman_append(s, "No categories found\r\n");
02408    }
02409    ast_config_destroy(cfg);
02410    astman_append(s, "\r\n");
02411 
02412    return 0;
02413 }
02414 
02415 static int action_listcategories(struct mansession *s, const struct message *m)
02416 {
02417    struct ast_config *cfg;
02418    const char *fn = astman_get_header(m, "Filename");
02419    char *category = NULL;
02420    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
02421    int catcount = 0;
02422 
02423    if (ast_strlen_zero(fn)) {
02424       astman_send_error(s, m, "Filename not specified");
02425       return 0;
02426    }
02427    if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
02428       astman_send_error(s, m, "Config file not found");
02429       return 0;
02430    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02431       astman_send_error(s, m, "Config file has invalid format");
02432       return 0;
02433    }
02434    astman_start_ack(s, m);
02435    while ((category = ast_category_browse(cfg, category))) {
02436       astman_append(s, "Category-%06d: %s\r\n", catcount, category);
02437       catcount++;
02438    }
02439    if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
02440       astman_append(s, "Error: no categories found\r\n");
02441    }
02442    ast_config_destroy(cfg);
02443    astman_append(s, "\r\n");
02444 
02445    return 0;
02446 }
02447 
02448 
02449 
02450 
02451 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
02452 static void json_escape(char *out, const char *in)
02453 {
02454    for (; *in; in++) {
02455       if (*in == '\\' || *in == '\"') {
02456          *out++ = '\\';
02457       }
02458       *out++ = *in;
02459    }
02460    *out = '\0';
02461 }
02462 
02463 static int action_getconfigjson(struct mansession *s, const struct message *m)
02464 {
02465    struct ast_config *cfg;
02466    const char *fn = astman_get_header(m, "Filename");
02467    char *category = NULL;
02468    struct ast_variable *v;
02469    int comma1 = 0;
02470    char *buf = NULL;
02471    unsigned int buf_len = 0;
02472    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
02473 
02474    if (ast_strlen_zero(fn)) {
02475       astman_send_error(s, m, "Filename not specified");
02476       return 0;
02477    }
02478 
02479    if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
02480       astman_send_error(s, m, "Config file not found");
02481       return 0;
02482    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02483       astman_send_error(s, m, "Config file has invalid format");
02484       return 0;
02485    }
02486 
02487    buf_len = 512;
02488    buf = alloca(buf_len);
02489 
02490    astman_start_ack(s, m);
02491    astman_append(s, "JSON: {");
02492    while ((category = ast_category_browse(cfg, category))) {
02493       int comma2 = 0;
02494       if (buf_len < 2 * strlen(category) + 1) {
02495          buf_len *= 2;
02496          buf = alloca(buf_len);
02497       }
02498       json_escape(buf, category);
02499       astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
02500       if (!comma1) {
02501          comma1 = 1;
02502       }
02503       for (v = ast_variable_browse(cfg, category); v; v = v->next) {
02504          if (comma2) {
02505             astman_append(s, ",");
02506          }
02507          if (buf_len < 2 * strlen(v->name) + 1) {
02508             buf_len *= 2;
02509             buf = alloca(buf_len);
02510          }
02511          json_escape(buf, v->name);
02512          astman_append(s, "\"%s", buf);
02513          if (buf_len < 2 * strlen(v->value) + 1) {
02514             buf_len *= 2;
02515             buf = alloca(buf_len);
02516          }
02517          json_escape(buf, v->value);
02518          astman_append(s, "%s\"", buf);
02519          if (!comma2) {
02520             comma2 = 1;
02521          }
02522       }
02523       astman_append(s, "]");
02524    }
02525    astman_append(s, "}\r\n\r\n");
02526 
02527    ast_config_destroy(cfg);
02528 
02529    return 0;
02530 }
02531 
02532 /* helper function for action_updateconfig */
02533 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
02534 {
02535    int x;
02536    char hdr[40];
02537    const char *action, *cat, *var, *value, *match, *line;
02538    struct ast_category *category;
02539    struct ast_variable *v;
02540    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
02541    enum error_type result = 0;
02542 
02543    for (x = 0; x < 100000; x++) {   /* 100000 = the max number of allowed updates + 1 */
02544       unsigned int object = 0;
02545 
02546       snprintf(hdr, sizeof(hdr), "Action-%06d", x);
02547       action = astman_get_header(m, hdr);
02548       if (ast_strlen_zero(action))     /* breaks the for loop if no action header */
02549          break;                        /* this could cause problems if actions come in misnumbered */
02550 
02551       snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
02552       cat = astman_get_header(m, hdr);
02553       if (ast_strlen_zero(cat)) {      /* every action needs a category */
02554          result =  UNSPECIFIED_CATEGORY;
02555          break;
02556       }
02557 
02558       snprintf(hdr, sizeof(hdr), "Var-%06d", x);
02559       var = astman_get_header(m, hdr);
02560 
02561       snprintf(hdr, sizeof(hdr), "Value-%06d", x);
02562       value = astman_get_header(m, hdr);
02563 
02564       if (!ast_strlen_zero(value) && *value == '>') {
02565          object = 1;
02566          value++;
02567       }
02568 
02569       snprintf(hdr, sizeof(hdr), "Match-%06d", x);
02570       match = astman_get_header(m, hdr);
02571 
02572       snprintf(hdr, sizeof(hdr), "Line-%06d", x);
02573       line = astman_get_header(m, hdr);
02574 
02575       if (!strcasecmp(action, "newcat")) {
02576          if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
02577             result = FAILURE_NEWCAT;   /* already exist */
02578             break;
02579          }
02580          if (!(category = ast_category_new(cat, dfn, -1))) {
02581             result = FAILURE_ALLOCATION;
02582             break;
02583          }
02584          if (ast_strlen_zero(match)) {
02585             ast_category_append(cfg, category);
02586          } else {
02587             ast_category_insert(cfg, category, match);
02588          }
02589       } else if (!strcasecmp(action, "renamecat")) {
02590          if (ast_strlen_zero(value)) {
02591             result = UNSPECIFIED_ARGUMENT;
02592             break;
02593          }
02594          if (!(category = ast_category_get(cfg, cat))) {
02595             result = UNKNOWN_CATEGORY;
02596             break;
02597          }
02598          ast_category_rename(category, value);
02599       } else if (!strcasecmp(action, "delcat")) {
02600          if (ast_category_delete(cfg, cat)) {
02601             result = FAILURE_DELCAT;
02602             break;
02603          }
02604       } else if (!strcasecmp(action, "emptycat")) {
02605          if (ast_category_empty(cfg, cat)) {
02606             result = FAILURE_EMPTYCAT;
02607             break;
02608          }
02609       } else if (!strcasecmp(action, "update")) {
02610          if (ast_strlen_zero(var)) {
02611             result = UNSPECIFIED_ARGUMENT;
02612             break;
02613          }
02614          if (!(category = ast_category_get(cfg,cat))) {
02615             result = UNKNOWN_CATEGORY;
02616             break;
02617          }
02618          if (ast_variable_update(category, var, value, match, object)) {
02619             result = FAILURE_UPDATE;
02620             break;
02621          }
02622       } else if (!strcasecmp(action, "delete")) {
02623          if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
02624             result = UNSPECIFIED_ARGUMENT;
02625             break;
02626          }
02627          if (!(category = ast_category_get(cfg, cat))) {
02628             result = UNKNOWN_CATEGORY;
02629             break;
02630          }
02631          if (ast_variable_delete(category, var, match, line)) {
02632             result = FAILURE_DELETE;
02633             break;
02634          }
02635       } else if (!strcasecmp(action, "append")) {
02636          if (ast_strlen_zero(var)) {
02637             result = UNSPECIFIED_ARGUMENT;
02638             break;
02639          }
02640          if (!(category = ast_category_get(cfg, cat))) {
02641             result = UNKNOWN_CATEGORY;
02642             break;
02643          }
02644          if (!(v = ast_variable_new(var, value, dfn))) {
02645             result = FAILURE_ALLOCATION;
02646             break;
02647          }
02648          if (object || (match && !strcasecmp(match, "object"))) {
02649             v->object = 1;
02650          }
02651          ast_variable_append(category, v);
02652       } else if (!strcasecmp(action, "insert")) {
02653          if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
02654             result = UNSPECIFIED_ARGUMENT;
02655             break;
02656          }
02657          if (!(category = ast_category_get(cfg, cat))) {
02658             result = UNKNOWN_CATEGORY;
02659             break;
02660          }
02661          if (!(v = ast_variable_new(var, value, dfn))) {
02662             result = FAILURE_ALLOCATION;
02663             break;
02664          }
02665          ast_variable_insert(category, v, line);
02666       }
02667       else {
02668          ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
02669          result = UNKNOWN_ACTION;
02670          break;
02671       }
02672    }
02673    ast_free(str1);
02674    ast_free(str2);
02675    return result;
02676 }
02677 
02678 static int action_updateconfig(struct mansession *s, const struct message *m)
02679 {
02680    struct ast_config *cfg;
02681    const char *sfn = astman_get_header(m, "SrcFilename");
02682    const char *dfn = astman_get_header(m, "DstFilename");
02683    int res;
02684    const char *rld = astman_get_header(m, "Reload");
02685    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
02686    enum error_type result;
02687 
02688    if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
02689       astman_send_error(s, m, "Filename not specified");
02690       return 0;
02691    }
02692    if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
02693       astman_send_error(s, m, "Config file not found");
02694       return 0;
02695    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
02696       astman_send_error(s, m, "Config file has invalid format");
02697       return 0;
02698    }
02699    result = handle_updates(s, m, cfg, dfn);
02700    if (!result) {
02701       ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
02702       res = ast_config_text_file_save(dfn, cfg, "Manager");
02703       ast_config_destroy(cfg);
02704       if (res) {
02705          astman_send_error(s, m, "Save of config failed");
02706          return 0;
02707       }
02708       astman_send_ack(s, m, NULL);
02709       if (!ast_strlen_zero(rld)) {
02710          if (ast_true(rld)) {
02711             rld = NULL;
02712          }
02713          ast_module_reload(rld);
02714       }
02715    } else {
02716       ast_config_destroy(cfg);
02717       switch(result) {
02718       case UNKNOWN_ACTION:
02719          astman_send_error(s, m, "Unknown action command");
02720          break;
02721       case UNKNOWN_CATEGORY:
02722          astman_send_error(s, m, "Given category does not exist");
02723          break;
02724       case UNSPECIFIED_CATEGORY:
02725          astman_send_error(s, m, "Category not specified");
02726          break;
02727       case UNSPECIFIED_ARGUMENT:
02728          astman_send_error(s, m, "Problem with category, value, or line (if required)");
02729          break;
02730       case FAILURE_ALLOCATION:
02731          astman_send_error(s, m, "Memory allocation failure, this should not happen");
02732          break;
02733       case FAILURE_NEWCAT:
02734          astman_send_error(s, m, "Create category did not complete successfully");
02735          break;
02736       case FAILURE_DELCAT:
02737          astman_send_error(s, m, "Delete category did not complete successfully");
02738          break;
02739       case FAILURE_EMPTYCAT:
02740          astman_send_error(s, m, "Empty category did not complete successfully");
02741          break;
02742       case FAILURE_UPDATE:
02743          astman_send_error(s, m, "Update did not complete successfully");
02744          break;
02745       case FAILURE_DELETE:
02746          astman_send_error(s, m, "Delete did not complete successfully");
02747          break;
02748       case FAILURE_APPEND:
02749          astman_send_error(s, m, "Append did not complete successfully");
02750          break;
02751       }
02752    }
02753    return 0;
02754 }
02755 
02756 static int action_createconfig(struct mansession *s, const struct message *m)
02757 {
02758    int fd;
02759    const char *fn = astman_get_header(m, "Filename");
02760    struct ast_str *filepath = ast_str_alloca(PATH_MAX);
02761    ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
02762    ast_str_append(&filepath, 0, "%s", fn);
02763 
02764    if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
02765       close(fd);
02766       astman_send_ack(s, m, "New configuration file created successfully");
02767    } else {
02768       astman_send_error(s, m, strerror(errno));
02769    }
02770 
02771    return 0;
02772 }
02773 
02774 static int action_waitevent(struct mansession *s, const struct message *m)
02775 {
02776    const char *timeouts = astman_get_header(m, "Timeout");
02777    int timeout = -1;
02778    int x;
02779    int needexit = 0;
02780    const char *id = astman_get_header(m, "ActionID");
02781    char idText[256];
02782 
02783    if (!ast_strlen_zero(id)) {
02784       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02785    } else {
02786       idText[0] = '\0';
02787    }
02788 
02789    if (!ast_strlen_zero(timeouts)) {
02790       sscanf(timeouts, "%30i", &timeout);
02791       if (timeout < -1) {
02792          timeout = -1;
02793       }
02794       /* XXX maybe put an upper bound, or prevent the use of 0 ? */
02795    }
02796 
02797    ao2_lock(s->session);
02798    if (s->session->waiting_thread != AST_PTHREADT_NULL) {
02799       pthread_kill(s->session->waiting_thread, SIGURG);
02800    }
02801 
02802    if (s->session->managerid) { /* AMI-over-HTTP session */
02803       /*
02804        * Make sure the timeout is within the expire time of the session,
02805        * as the client will likely abort the request if it does not see
02806        * data coming after some amount of time.
02807        */
02808       time_t now = time(NULL);
02809       int max = s->session->sessiontimeout - now - 10;
02810 
02811       if (max < 0) { /* We are already late. Strange but possible. */
02812          max = 0;
02813       }
02814       if (timeout < 0 || timeout > max) {
02815          timeout = max;
02816       }
02817       if (!s->session->send_events) {  /* make sure we record events */
02818          s->session->send_events = -1;
02819       }
02820    }
02821    ao2_unlock(s->session);
02822 
02823    /* XXX should this go inside the lock ? */
02824    s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
02825    ast_debug(1, "Starting waiting for an event!\n");
02826 
02827    for (x = 0; x < timeout || timeout < 0; x++) {
02828       ao2_lock(s->session);
02829       if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
02830          needexit = 1;
02831       }
02832       /* We can have multiple HTTP session point to the same mansession entry.
02833        * The way we deal with it is not very nice: newcomers kick out the previous
02834        * HTTP session. XXX this needs to be improved.
02835        */
02836       if (s->session->waiting_thread != pthread_self()) {
02837          needexit = 1;
02838       }
02839       if (s->session->needdestroy) {
02840          needexit = 1;
02841       }
02842       ao2_unlock(s->session);
02843       if (needexit) {
02844          break;
02845       }
02846       if (s->session->managerid == 0) {   /* AMI session */
02847          if (ast_wait_for_input(s->session->fd, 1000)) {
02848             break;
02849          }
02850       } else { /* HTTP session */
02851          sleep(1);
02852       }
02853    }
02854    ast_debug(1, "Finished waiting for an event!\n");
02855 
02856    ao2_lock(s->session);
02857    if (s->session->waiting_thread == pthread_self()) {
02858       struct eventqent *eqe = s->session->last_ev;
02859       astman_send_response(s, m, "Success", "Waiting for Event completed.");
02860       while ((eqe = advance_event(eqe))) {
02861          if (((s->session->readperm & eqe->category) == eqe->category) &&
02862              ((s->session->send_events & eqe->category) == eqe->category)) {
02863             astman_append(s, "%s", eqe->eventdata);
02864          }
02865          s->session->last_ev = eqe;
02866       }
02867       astman_append(s,
02868          "Event: WaitEventComplete\r\n"
02869          "%s"
02870          "\r\n", idText);
02871       s->session->waiting_thread = AST_PTHREADT_NULL;
02872    } else {
02873       ast_debug(1, "Abandoning event request!\n");
02874    }
02875    ao2_unlock(s->session);
02876 
02877    return 0;
02878 }
02879 
02880 /*! \note The actionlock is read-locked by the caller of this function */
02881 static int action_listcommands(struct mansession *s, const struct message *m)
02882 {
02883    struct manager_action *cur;
02884    struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
02885 
02886    astman_start_ack(s, m);
02887    AST_RWLIST_TRAVERSE(&actions, cur, list) {
02888       if (s->session->writeperm & cur->authority || cur->authority == 0) {
02889          astman_append(s, "%s: %s (Priv: %s)\r\n",
02890             cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
02891       }
02892    }
02893    astman_append(s, "\r\n");
02894 
02895    return 0;
02896 }
02897 
02898 static int action_events(struct mansession *s, const struct message *m)
02899 {
02900    const char *mask = astman_get_header(m, "EventMask");
02901    int res, x;
02902    const char *id = astman_get_header(m, "ActionID");
02903    char id_text[256];
02904 
02905    if (!ast_strlen_zero(id)) {
02906       snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
02907    } else {
02908       id_text[0] = '\0';
02909    }
02910 
02911    res = set_eventmask(s, mask);
02912    if (broken_events_action) {
02913       /* if this option is set we should not return a response on
02914        * error, or when all events are set */
02915 
02916       if (res > 0) {
02917          for (x = 0; x < ARRAY_LEN(perms); x++) {
02918             if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
02919                return 0;
02920             }
02921          }
02922          astman_append(s, "Response: Success\r\n%s"
02923                 "Events: On\r\n\r\n", id_text);
02924       } else if (res == 0)
02925          astman_append(s, "Response: Success\r\n%s"
02926                 "Events: Off\r\n\r\n", id_text);
02927       return 0;
02928    }
02929 
02930    if (res > 0)
02931       astman_append(s, "Response: Success\r\n%s"
02932              "Events: On\r\n\r\n", id_text);
02933    else if (res == 0)
02934       astman_append(s, "Response: Success\r\n%s"
02935              "Events: Off\r\n\r\n", id_text);
02936    else
02937       astman_send_error(s, m, "Invalid event mask");
02938 
02939    return 0;
02940 }
02941 
02942 static int action_logoff(struct mansession *s, const struct message *m)
02943 {
02944    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
02945    return -1;
02946 }
02947 
02948 static int action_login(struct mansession *s, const struct message *m)
02949 {
02950 
02951    /* still authenticated - don't process again */
02952    if (s->session->authenticated) {
02953       astman_send_ack(s, m, "Already authenticated");
02954       return 0;
02955    }
02956 
02957    if (authenticate(s, m)) {
02958       sleep(1);
02959       astman_send_error(s, m, "Authentication failed");
02960       return -1;
02961    }
02962    s->session->authenticated = 1;
02963    ast_atomic_fetchadd_int(&unauth_sessions, -1);
02964    if (manager_displayconnects(s->session)) {
02965       ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02966    }
02967    astman_send_ack(s, m, "Authentication accepted");
02968    if ((s->session->send_events & EVENT_FLAG_SYSTEM)
02969       && ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
02970       struct ast_str *auth = ast_str_alloca(80);
02971       const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
02972       astman_append(s, "Event: FullyBooted\r\n"
02973          "Privilege: %s\r\n"
02974          "Status: Fully Booted\r\n\r\n", cat_str);
02975    }
02976    return 0;
02977 }
02978 
02979 static int action_challenge(struct mansession *s, const struct message *m)
02980 {
02981    const char *authtype = astman_get_header(m, "AuthType");
02982 
02983    if (!strcasecmp(authtype, "MD5")) {
02984       if (ast_strlen_zero(s->session->challenge)) {
02985          snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
02986       }
02987       mansession_lock(s);
02988       astman_start_ack(s, m);
02989       astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
02990       mansession_unlock(s);
02991    } else {
02992       astman_send_error(s, m, "Must specify AuthType");
02993    }
02994    return 0;
02995 }
02996 
02997 static int action_hangup(struct mansession *s, const struct message *m)
02998 {
02999    struct ast_channel *c = NULL;
03000    int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
03001    const char *name = astman_get_header(m, "Channel");
03002    const char *cause = astman_get_header(m, "Cause");
03003 
03004    if (ast_strlen_zero(name)) {
03005       astman_send_error(s, m, "No channel specified");
03006       return 0;
03007    }
03008 
03009    if (!ast_strlen_zero(cause)) {
03010       char *endptr;
03011       causecode = strtol(cause, &endptr, 10);
03012       if (causecode < 0 || causecode > 127 || *endptr != '\0') {
03013          ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
03014          /* keep going, better to hangup without cause than to not hang up at all */
03015          causecode = 0; /* do not set channel's hangupcause */
03016       }
03017    }
03018 
03019    if (!(c = ast_channel_get_by_name(name))) {
03020       astman_send_error(s, m, "No such channel");
03021       return 0;
03022    }
03023 
03024    ast_channel_lock(c);
03025    if (causecode > 0) {
03026       ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
03027             c->name, causecode, c->hangupcause);
03028       c->hangupcause = causecode;
03029    }
03030    ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
03031    ast_channel_unlock(c);
03032 
03033    c = ast_channel_unref(c);
03034 
03035    astman_send_ack(s, m, "Channel Hungup");
03036 
03037    return 0;
03038 }
03039 
03040 static int action_setvar(struct mansession *s, const struct message *m)
03041 {
03042    struct ast_channel *c = NULL;
03043    const char *name = astman_get_header(m, "Channel");
03044    const char *varname = astman_get_header(m, "Variable");
03045    const char *varval = astman_get_header(m, "Value");
03046    int res = 0;
03047    
03048    if (ast_strlen_zero(varname)) {
03049       astman_send_error(s, m, "No variable specified");
03050       return 0;
03051    }
03052 
03053    if (!ast_strlen_zero(name)) {
03054       if (!(c = ast_channel_get_by_name(name))) {
03055          astman_send_error(s, m, "No such channel");
03056          return 0;
03057       }
03058    }
03059 
03060    res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
03061 
03062    if (c) {
03063       c = ast_channel_unref(c);
03064    }
03065    if (res == 0) {
03066       astman_send_ack(s, m, "Variable Set"); 
03067    } else {
03068       astman_send_error(s, m, "Variable not set");
03069    }
03070    return 0;
03071 }
03072 
03073 static int action_getvar(struct mansession *s, const struct message *m)
03074 {
03075    struct ast_channel *c = NULL;
03076    const char *name = astman_get_header(m, "Channel");
03077    const char *varname = astman_get_header(m, "Variable");
03078    char *varval;
03079    char workspace[1024] = "";
03080 
03081    if (ast_strlen_zero(varname)) {
03082       astman_send_error(s, m, "No variable specified");
03083       return 0;
03084    }
03085 
03086    if (!ast_strlen_zero(name)) {
03087       if (!(c = ast_channel_get_by_name(name))) {
03088          astman_send_error(s, m, "No such channel");
03089          return 0;
03090       }
03091    }
03092 
03093    if (varname[strlen(varname) - 1] == ')') {
03094       if (!c) {
03095          c = ast_dummy_channel_alloc();
03096          if (c) {
03097             ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
03098             c = ast_channel_release(c);
03099          } else
03100             ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
03101       } else {
03102          ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
03103       }
03104       varval = workspace;
03105    } else {
03106       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
03107    }
03108 
03109    if (c) {
03110       c = ast_channel_unref(c);
03111    }
03112 
03113    astman_start_ack(s, m);
03114    astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, S_OR(varval, ""));
03115 
03116    return 0;
03117 }
03118 
03119 /*! \brief Manager "status" command to show channels */
03120 /* Needs documentation... */
03121 static int action_status(struct mansession *s, const struct message *m)
03122 {
03123    const char *name = astman_get_header(m, "Channel");
03124    const char *cvariables = astman_get_header(m, "Variables");
03125    char *variables = ast_strdupa(S_OR(cvariables, ""));
03126    struct ast_channel *c;
03127    char bridge[256];
03128    struct timeval now = ast_tvnow();
03129    long elapsed_seconds = 0;
03130    int channels = 0;
03131    int all = ast_strlen_zero(name); /* set if we want all channels */
03132    const char *id = astman_get_header(m, "ActionID");
03133    char idText[256];
03134    AST_DECLARE_APP_ARGS(vars,
03135       AST_APP_ARG(name)[100];
03136    );
03137    struct ast_str *str = ast_str_create(1000);
03138    struct ast_channel_iterator *iter = NULL;
03139 
03140    if (!ast_strlen_zero(id)) {
03141       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
03142    } else {
03143       idText[0] = '\0';
03144    }
03145 
03146    if (all) {
03147       if (!(iter = ast_channel_iterator_all_new())) {
03148          ast_free(str);
03149          astman_send_error(s, m, "Memory Allocation Failure");
03150          return 1;
03151       }
03152       c = ast_channel_iterator_next(iter);
03153    } else {
03154       if (!(c = ast_channel_get_by_name(name))) {
03155          astman_send_error(s, m, "No such channel");
03156          ast_free(str);
03157          return 0;
03158       }
03159    }
03160 
03161    astman_send_ack(s, m, "Channel status will follow");
03162 
03163    if (!ast_strlen_zero(cvariables)) {
03164       AST_STANDARD_APP_ARGS(vars, variables);
03165    }
03166 
03167    /* if we look by name, we break after the first iteration */
03168    for (; c; c = ast_channel_iterator_next(iter)) {
03169       ast_channel_lock(c);
03170 
03171       if (!ast_strlen_zero(cvariables)) {
03172          int i;
03173          ast_str_reset(str);
03174          for (i = 0; i < vars.argc; i++) {
03175             char valbuf[512], *ret = NULL;
03176 
03177             if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
03178                if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
03179                   valbuf[0] = '\0';
03180                }
03181                ret = valbuf;
03182             } else {
03183                pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
03184             }
03185 
03186             ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
03187          }
03188       }
03189 
03190       channels++;
03191       if (c->_bridge) {
03192          snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
03193       } else {
03194          bridge[0] = '\0';
03195       }
03196       if (c->pbx) {
03197          if (c->cdr) {
03198             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
03199          }
03200          astman_append(s,
03201          "Event: Status\r\n"
03202          "Privilege: Call\r\n"
03203          "Channel: %s\r\n"
03204          "CallerIDNum: %s\r\n"
03205          "CallerIDName: %s\r\n"
03206          "ConnectedLineNum: %s\r\n"
03207          "ConnectedLineName: %s\r\n"
03208          "Accountcode: %s\r\n"
03209          "ChannelState: %d\r\n"
03210          "ChannelStateDesc: %s\r\n"
03211          "Context: %s\r\n"
03212          "Extension: %s\r\n"
03213          "Priority: %d\r\n"
03214          "Seconds: %ld\r\n"
03215          "%s"
03216          "Uniqueid: %s\r\n"
03217          "%s"
03218          "%s"
03219          "\r\n",
03220          c->name,
03221          S_COR(c->caller.id.number.valid, c->caller.id.number.str, "<unknown>"),
03222          S_COR(c->caller.id.name.valid, c->caller.id.name.str, "<unknown>"),
03223          S_COR(c->connected.id.number.valid, c->connected.id.number.str, "<unknown>"),
03224          S_COR(c->connected.id.name.valid, c->connected.id.name.str, "<unknown>"),
03225          c->accountcode,
03226          c->_state,
03227          ast_state2str(c->_state), c->context,
03228          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
03229       } else {
03230          astman_append(s,
03231             "Event: Status\r\n"
03232             "Privilege: Call\r\n"
03233             "Channel: %s\r\n"
03234             "CallerIDNum: %s\r\n"
03235             "CallerIDName: %s\r\n"
03236             "ConnectedLineNum: %s\r\n"
03237             "ConnectedLineName: %s\r\n"
03238             "Account: %s\r\n"
03239             "State: %s\r\n"
03240             "%s"
03241             "Uniqueid: %s\r\n"
03242             "%s"
03243             "%s"
03244             "\r\n",
03245             c->name,
03246             S_COR(c->caller.id.number.valid, c->caller.id.number.str, "<unknown>"),
03247             S_COR(c->caller.id.name.valid, c->caller.id.name.str, "<unknown>"),
03248             S_COR(c->connected.id.number.valid, c->connected.id.number.str, "<unknown>"),
03249             S_COR(c->connected.id.name.valid, c->connected.id.name.str, "<unknown>"),
03250             c->accountcode,
03251             ast_state2str(c->_state), bridge, c->uniqueid,
03252             ast_str_buffer(str), idText);
03253       }
03254 
03255       ast_channel_unlock(c);
03256       c = ast_channel_unref(c);
03257 
03258       if (!all) {
03259          break;
03260       }
03261    }
03262 
03263    if (iter) {
03264       ast_channel_iterator_destroy(iter);
03265    }
03266 
03267    astman_append(s,
03268       "Event: StatusComplete\r\n"
03269       "%s"
03270       "Items: %d\r\n"
03271       "\r\n", idText, channels);
03272 
03273    ast_free(str);
03274 
03275    return 0;
03276 }
03277 
03278 static int action_sendtext(struct mansession *s, const struct message *m)
03279 {
03280    struct ast_channel *c = NULL;
03281    const char *name = astman_get_header(m, "Channel");
03282    const char *textmsg = astman_get_header(m, "Message");
03283    int res = 0;
03284 
03285    if (ast_strlen_zero(name)) {
03286       astman_send_error(s, m, "No channel specified");
03287       return 0;
03288    }
03289 
03290    if (ast_strlen_zero(textmsg)) {
03291       astman_send_error(s, m, "No Message specified");
03292       return 0;
03293    }
03294 
03295    if (!(c = ast_channel_get_by_name(name))) {
03296       astman_send_error(s, m, "No such channel");
03297       return 0;
03298    }
03299 
03300    res = ast_sendtext(c, textmsg);
03301    c = ast_channel_unref(c);
03302 
03303    if (res >= 0) {
03304       astman_send_ack(s, m, "Success");
03305    } else {
03306       astman_send_error(s, m, "Failure");
03307    }
03308 
03309    return res;
03310 }
03311 
03312 /*! \brief  action_redirect: The redirect manager command */
03313 static int action_redirect(struct mansession *s, const struct message *m)
03314 {
03315    const char *name = astman_get_header(m, "Channel");
03316    const char *name2 = astman_get_header(m, "ExtraChannel");
03317    const char *exten = astman_get_header(m, "Exten");
03318    const char *exten2 = astman_get_header(m, "ExtraExten");
03319    const char *context = astman_get_header(m, "Context");
03320    const char *context2 = astman_get_header(m, "ExtraContext");
03321    const char *priority = astman_get_header(m, "Priority");
03322    const char *priority2 = astman_get_header(m, "ExtraPriority");
03323    struct ast_channel *chan, *chan2 = NULL;
03324    int pi, pi2 = 0;
03325    int res;
03326 
03327    if (ast_strlen_zero(name)) {
03328       astman_send_error(s, m, "Channel not specified");
03329       return 0;
03330    }
03331 
03332    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
03333       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
03334          astman_send_error(s, m, "Invalid priority");
03335          return 0;
03336       }
03337    }
03338 
03339    if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%30d", &pi2) != 1)) {
03340       if ((pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL)) < 1) {
03341          astman_send_error(s, m, "Invalid ExtraPriority");
03342          return 0;
03343       }
03344    }
03345 
03346    if (!(chan = ast_channel_get_by_name(name))) {
03347       char buf[256];
03348       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
03349       astman_send_error(s, m, buf);
03350       return 0;
03351    }
03352 
03353    if (ast_check_hangup_locked(chan)) {
03354       astman_send_error(s, m, "Redirect failed, channel not up.");
03355       chan = ast_channel_unref(chan);
03356       return 0;
03357    }
03358 
03359    if (!ast_strlen_zero(name2)) {
03360       chan2 = ast_channel_get_by_name(name2);
03361    }
03362 
03363    if (chan2 && ast_check_hangup_locked(chan2)) {
03364       astman_send_error(s, m, "Redirect failed, extra channel not up.");
03365       chan = ast_channel_unref(chan);
03366       chan2 = ast_channel_unref(chan2);
03367       return 0;
03368    }
03369 
03370    if (chan->pbx) {
03371       ast_channel_lock(chan);
03372       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
03373       ast_channel_unlock(chan);
03374    }
03375 
03376    res = ast_async_goto(chan, context, exten, pi);
03377    if (!res) {
03378       if (!ast_strlen_zero(name2)) {
03379          if (chan2) {
03380             if (chan2->pbx) {
03381                ast_channel_lock(chan2);
03382                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
03383                ast_channel_unlock(chan2);
03384             }
03385             if (context2) {
03386                res = ast_async_goto(chan2, context2, exten2, pi2);
03387             } else {
03388                res = ast_async_goto(chan2, context, exten, pi);
03389             }
03390          } else {
03391             res = -1;
03392          }
03393          if (!res) {
03394             astman_send_ack(s, m, "Dual Redirect successful");
03395          } else {
03396             astman_send_error(s, m, "Secondary redirect failed");
03397          }
03398       } else {
03399          astman_send_ack(s, m, "Redirect successful");
03400       }
03401    } else {
03402       astman_send_error(s, m, "Redirect failed");
03403    }
03404 
03405    if (chan) {
03406       chan = ast_channel_unref(chan);
03407    }
03408 
03409    if (chan2) {
03410       chan2 = ast_channel_unref(chan2);
03411    }
03412 
03413    return 0;
03414 }
03415 
03416 static int action_atxfer(struct mansession *s, const struct message *m)
03417 {
03418    const char *name = astman_get_header(m, "Channel");
03419    const char *exten = astman_get_header(m, "Exten");
03420    const char *context = astman_get_header(m, "Context");
03421    struct ast_channel *chan = NULL;
03422    struct ast_call_feature *atxfer_feature = NULL;
03423    char *feature_code = NULL;
03424 
03425    if (ast_strlen_zero(name)) {
03426       astman_send_error(s, m, "No channel specified");
03427       return 0;
03428    }
03429    if (ast_strlen_zero(exten)) {
03430       astman_send_error(s, m, "No extension specified");
03431       return 0;
03432    }
03433 
03434    if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
03435       astman_send_error(s, m, "No attended transfer feature found");
03436       return 0;
03437    }
03438 
03439    if (!(chan = ast_channel_get_by_name(name))) {
03440       astman_send_error(s, m, "Channel specified does not exist");
03441       return 0;
03442    }
03443 
03444    if (!ast_strlen_zero(context)) {
03445       pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
03446    }
03447 
03448    for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
03449       struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *feature_code };
03450       ast_queue_frame(chan, &f);
03451    }
03452 
03453    for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
03454       struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *feature_code };
03455       ast_queue_frame(chan, &f);
03456    }
03457 
03458    chan = ast_channel_unref(chan);
03459 
03460    astman_send_ack(s, m, "Atxfer successfully queued");
03461 
03462    return 0;
03463 }
03464 
03465 static int check_blacklist(const char *cmd)
03466 {
03467    char *cmd_copy, *cur_cmd;
03468    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
03469    int i;
03470 
03471    cmd_copy = ast_strdupa(cmd);
03472    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
03473       cur_cmd = ast_strip(cur_cmd);
03474       if (ast_strlen_zero(cur_cmd)) {
03475          i--;
03476          continue;
03477       }
03478 
03479       cmd_words[i] = cur_cmd;
03480    }
03481 
03482    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
03483       int j, match = 1;
03484 
03485       for (j = 0; command_blacklist[i].words[j]; j++) {
03486          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
03487             match = 0;
03488             break;
03489          }
03490       }
03491 
03492       if (match) {
03493          return 1;
03494       }
03495    }
03496 
03497    return 0;
03498 }
03499 
03500 /*! \brief  Manager command "command" - execute CLI command */
03501 static int action_command(struct mansession *s, const struct message *m)
03502 {
03503    const char *cmd = astman_get_header(m, "Command");
03504    const char *id = astman_get_header(m, "ActionID");
03505    char *buf, *final_buf;
03506    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
03507    int fd;
03508    off_t l;
03509 
03510    if (ast_strlen_zero(cmd)) {
03511       astman_send_error(s, m, "No command provided");
03512       return 0;
03513    }
03514 
03515    if (check_blacklist(cmd)) {
03516       astman_send_error(s, m, "Command blacklisted");
03517       return 0;
03518    }
03519 
03520    fd = mkstemp(template);
03521 
03522    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
03523    if (!ast_strlen_zero(id)) {
03524       astman_append(s, "ActionID: %s\r\n", id);
03525    }
03526    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
03527    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
03528    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
03529 
03530    /* This has a potential to overflow the stack.  Hence, use the heap. */
03531    buf = ast_calloc(1, l + 1);
03532    final_buf = ast_calloc(1, l + 1);
03533    if (buf) {
03534       lseek(fd, 0, SEEK_SET);
03535       if (read(fd, buf, l) < 0) {
03536          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
03537       }
03538       buf[l] = '\0';
03539       if (final_buf) {
03540          term_strip(final_buf, buf, l);
03541          final_buf[l] = '\0';
03542       }
03543       astman_append(s, "%s", S_OR(final_buf, buf));
03544       ast_free(buf);
03545    }
03546    close(fd);
03547    unlink(template);
03548    astman_append(s, "--END COMMAND--\r\n\r\n");
03549    if (final_buf) {
03550       ast_free(final_buf);
03551    }
03552    return 0;
03553 }
03554 
03555 /*! \brief helper function for originate */
03556 struct fast_originate_helper {
03557    char tech[AST_MAX_EXTENSION];
03558    /*! data can contain a channel name, extension number, username, password, etc. */
03559    char data[512];
03560    int timeout;
03561    format_t format;           /*!< Codecs used for a call */
03562    char app[AST_MAX_APP];
03563    char appdata[AST_MAX_EXTENSION];
03564    char cid_name[AST_MAX_EXTENSION];
03565    char cid_num[AST_MAX_EXTENSION];
03566    char context[AST_MAX_CONTEXT];
03567    char exten[AST_MAX_EXTENSION];
03568    char idtext[AST_MAX_EXTENSION];
03569    char account[AST_MAX_ACCOUNT_CODE];
03570    int priority;
03571    struct ast_variable *vars;
03572 };
03573 
03574 static void *fast_originate(void *data)
03575 {
03576    struct fast_originate_helper *in = data;
03577    int res;
03578    int reason = 0;
03579    struct ast_channel *chan = NULL, *chans[1];
03580    char requested_channel[AST_CHANNEL_NAME];
03581 
03582    if (!ast_strlen_zero(in->app)) {
03583       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
03584          S_OR(in->cid_num, NULL),
03585          S_OR(in->cid_name, NULL),
03586          in->vars, in->account, &chan);
03587    } else {
03588       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
03589          S_OR(in->cid_num, NULL),
03590          S_OR(in->cid_name, NULL),
03591          in->vars, in->account, &chan);
03592    }
03593 
03594    if (!chan) {
03595       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
03596    }
03597    /* Tell the manager what happened with the channel */
03598    chans[0] = chan;
03599    ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
03600       "%s%s"
03601       "Response: %s\r\n"
03602       "Channel: %s\r\n"
03603       "Context: %s\r\n"
03604       "Exten: %s\r\n"
03605       "Reason: %d\r\n"
03606       "Uniqueid: %s\r\n"
03607       "CallerIDNum: %s\r\n"
03608       "CallerIDName: %s\r\n",
03609       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success",
03610       chan ? chan->name : requested_channel, in->context, in->exten, reason,
03611       chan ? chan->uniqueid : "<null>",
03612       S_OR(in->cid_num, "<unknown>"),
03613       S_OR(in->cid_name, "<unknown>")
03614       );
03615 
03616    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
03617    if (chan) {
03618       ast_channel_unlock(chan);
03619    }
03620    ast_free(in);
03621    return NULL;
03622 }
03623 
03624 static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
03625 {
03626    const char *unitamount;
03627    const char *unittype;
03628    struct ast_str *str = ast_str_alloca(32);
03629 
03630    memset(entry, 0, sizeof(*entry));
03631 
03632    ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
03633    unitamount = astman_get_header(m, ast_str_buffer(str));
03634 
03635    ast_str_set(&str, 0, "UnitType(%u)", entry_num);
03636    unittype = astman_get_header(m, ast_str_buffer(str));
03637 
03638    if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
03639       entry->valid_amount = 1;
03640    }
03641 
03642    if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
03643       entry->valid_type = 1;
03644    }
03645 
03646    return 0;
03647 }
03648 
03649 static int action_aocmessage(struct mansession *s, const struct message *m)
03650 {
03651    const char *channel = astman_get_header(m, "Channel");
03652    const char *pchannel = astman_get_header(m, "ChannelPrefix");
03653    const char *msgtype = astman_get_header(m, "MsgType");
03654    const char *chargetype = astman_get_header(m, "ChargeType");
03655    const char *currencyname = astman_get_header(m, "CurrencyName");
03656    const char *currencyamount = astman_get_header(m, "CurrencyAmount");
03657    const char *mult = astman_get_header(m, "CurrencyMultiplier");
03658    const char *totaltype = astman_get_header(m, "TotalType");
03659    const char *aocbillingid = astman_get_header(m, "AOCBillingId");
03660    const char *association_id= astman_get_header(m, "ChargingAssociationId");
03661    const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
03662    const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
03663 
03664    enum ast_aoc_type _msgtype;
03665    enum ast_aoc_charge_type _chargetype;
03666    enum ast_aoc_currency_multiplier _mult = AST_AOC_MULT_ONE;
03667    enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
03668    enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
03669    unsigned int _currencyamount = 0;
03670    int _association_id = 0;
03671    unsigned int _association_plan = 0;
03672    struct ast_channel *chan = NULL;
03673 
03674    struct ast_aoc_decoded *decoded = NULL;
03675    struct ast_aoc_encoded *encoded = NULL;
03676    size_t encoded_size = 0;
03677 
03678    if (ast_strlen_zero(channel) && ast_strlen_zero(pchannel)) {
03679       astman_send_error(s, m, "Channel and PartialChannel are not specified. Specify at least one of these.");
03680       goto aocmessage_cleanup;
03681    }
03682 
03683    if (!(chan = ast_channel_get_by_name(channel)) && !ast_strlen_zero(pchannel)) {
03684       chan = ast_channel_get_by_name_prefix(pchannel, strlen(pchannel));
03685    }
03686 
03687    if (!chan) {
03688       astman_send_error(s, m, "No such channel");
03689       goto aocmessage_cleanup;
03690    }
03691 
03692    if (ast_strlen_zero(msgtype) || (strcasecmp(msgtype, "d") && strcasecmp(msgtype, "e"))) {
03693       astman_send_error(s, m, "Invalid MsgType");
03694       goto aocmessage_cleanup;
03695    }
03696 
03697    if (ast_strlen_zero(chargetype)) {
03698       astman_send_error(s, m, "ChargeType not specified");
03699       goto aocmessage_cleanup;
03700    }
03701 
03702    _msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
03703 
03704    if (!strcasecmp(chargetype, "NA")) {
03705       _chargetype = AST_AOC_CHARGE_NA;
03706    } else if (!strcasecmp(chargetype, "Free")) {
03707       _chargetype = AST_AOC_CHARGE_FREE;
03708    } else if (!strcasecmp(chargetype, "Currency")) {
03709       _chargetype = AST_AOC_CHARGE_CURRENCY;
03710    } else if (!strcasecmp(chargetype, "Unit")) {
03711       _chargetype = AST_AOC_CHARGE_UNIT;
03712    } else {
03713       astman_send_error(s, m, "Invalid ChargeType");
03714       goto aocmessage_cleanup;
03715    }
03716 
03717    if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
03718 
03719       if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
03720          astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
03721          goto aocmessage_cleanup;
03722       }
03723 
03724       if (ast_strlen_zero(mult)) {
03725          astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
03726          goto aocmessage_cleanup;
03727       } else if (!strcasecmp(mult, "onethousandth")) {
03728          _mult = AST_AOC_MULT_ONETHOUSANDTH;
03729       } else if (!strcasecmp(mult, "onehundredth")) {
03730          _mult = AST_AOC_MULT_ONEHUNDREDTH;
03731       } else if (!strcasecmp(mult, "onetenth")) {
03732          _mult = AST_AOC_MULT_ONETENTH;
03733       } else if (!strcasecmp(mult, "one")) {
03734          _mult = AST_AOC_MULT_ONE;
03735       } else if (!strcasecmp(mult, "ten")) {
03736          _mult = AST_AOC_MULT_TEN;
03737       } else if (!strcasecmp(mult, "hundred")) {
03738          _mult = AST_AOC_MULT_HUNDRED;
03739       } else if (!strcasecmp(mult, "thousand")) {
03740          _mult = AST_AOC_MULT_THOUSAND;
03741       } else {
03742          astman_send_error(s, m, "Invalid ChargeMultiplier");
03743          goto aocmessage_cleanup;
03744       }
03745    }
03746 
03747    /* create decoded object and start setting values */
03748    if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
03749          astman_send_error(s, m, "Message Creation Failed");
03750          goto aocmessage_cleanup;
03751    }
03752 
03753    if (_msgtype == AST_AOC_D) {
03754       if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
03755          _totaltype = AST_AOC_SUBTOTAL;
03756       }
03757 
03758       if (ast_strlen_zero(aocbillingid)) {
03759          /* ignore this is optional */
03760       } else if (!strcasecmp(aocbillingid, "Normal")) {
03761          _billingid = AST_AOC_BILLING_NORMAL;
03762       } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
03763          _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
03764       } else if (!strcasecmp(aocbillingid, "CreditCard")) {
03765          _billingid = AST_AOC_BILLING_CREDIT_CARD;
03766       } else {
03767          astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
03768          goto aocmessage_cleanup;
03769       }
03770    } else {
03771       if (ast_strlen_zero(aocbillingid)) {
03772          /* ignore this is optional */
03773       } else if (!strcasecmp(aocbillingid, "Normal")) {
03774          _billingid = AST_AOC_BILLING_NORMAL;
03775       } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
03776          _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
03777       } else if (!strcasecmp(aocbillingid, "CreditCard")) {
03778          _billingid = AST_AOC_BILLING_CREDIT_CARD;
03779       } else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
03780          _billingid = AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL;
03781       } else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
03782          _billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
03783       } else if (!strcasecmp(aocbillingid, "CallFwdNoReply")) {
03784          _billingid = AST_AOC_BILLING_CALL_FWD_NO_REPLY;
03785       } else if (!strcasecmp(aocbillingid, "CallDeflection")) {
03786          _billingid = AST_AOC_BILLING_CALL_DEFLECTION;
03787       } else if (!strcasecmp(aocbillingid, "CallTransfer")) {
03788          _billingid = AST_AOC_BILLING_CALL_TRANSFER;
03789       } else {
03790          astman_send_error(s, m, "Invalid AOC-E AOCBillingId");
03791          goto aocmessage_cleanup;
03792       }
03793 
03794       if (!ast_strlen_zero(association_id) && (sscanf(association_id, "%30d", &_association_id) != 1)) {
03795          astman_send_error(s, m, "Invalid ChargingAssociationId");
03796          goto aocmessage_cleanup;
03797       }
03798       if (!ast_strlen_zero(association_plan) && (sscanf(association_plan, "%30u", &_association_plan) != 1)) {
03799          astman_send_error(s, m, "Invalid ChargingAssociationPlan");
03800          goto aocmessage_cleanup;
03801       }
03802 
03803       if (_association_id) {
03804          ast_aoc_set_association_id(decoded, _association_id);
03805       } else if (!ast_strlen_zero(association_num)) {
03806          ast_aoc_set_association_number(decoded, association_num, _association_plan);
03807       }
03808    }
03809 
03810    if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
03811       ast_aoc_set_currency_info(decoded, _currencyamount, _mult, ast_strlen_zero(currencyname) ? NULL : currencyname);
03812    } else if (_chargetype == AST_AOC_CHARGE_UNIT) {
03813       struct ast_aoc_unit_entry entry;
03814       int i;
03815 
03816       /* multiple unit entries are possible, lets get them all */
03817       for (i = 0; i < 32; i++) {
03818          if (aocmessage_get_unit_entry(m, &entry, i)) {
03819             break; /* that's the end then */
03820          }
03821 
03822          ast_aoc_add_unit_entry(decoded, entry.valid_amount, entry.amount, entry.valid_type, entry.type);
03823       }
03824 
03825       /* at least one unit entry is required */
03826       if (!i) {
03827          astman_send_error(s, m, "Invalid UnitAmount(0), At least one valid unit entry is required when ChargeType is set to Unit");
03828          goto aocmessage_cleanup;
03829       }
03830 
03831    }
03832 
03833    ast_aoc_set_billing_id(decoded, _billingid);
03834    ast_aoc_set_total_type(decoded, _totaltype);
03835 
03836 
03837    if ((encoded = ast_aoc_encode(decoded, &encoded_size, NULL)) && !ast_indicate_data(chan, AST_CONTROL_AOC, encoded, encoded_size)) {
03838       astman_send_ack(s, m, "AOC Message successfully queued on channel");
03839    } else {
03840       astman_send_error(s, m, "Error encoding AOC message, could not queue onto channel");
03841    }
03842 
03843 aocmessage_cleanup:
03844 
03845    ast_aoc_destroy_decoded(decoded);
03846    ast_aoc_destroy_encoded(encoded);
03847 
03848    if (chan) {
03849       chan = ast_channel_unref(chan);
03850    }
03851    return 0;
03852 }
03853 
03854 static int action_originate(struct mansession *s, const struct message *m)
03855 {
03856    const char *name = astman_get_header(m, "Channel");
03857    const char *exten = astman_get_header(m, "Exten");
03858    const char *context = astman_get_header(m, "Context");
03859    const char *priority = astman_get_header(m, "Priority");
03860    const char *timeout = astman_get_header(m, "Timeout");
03861    const char *callerid = astman_get_header(m, "CallerID");
03862    const char *account = astman_get_header(m, "Account");
03863    const char *app = astman_get_header(m, "Application");
03864    const char *appdata = astman_get_header(m, "Data");
03865    const char *async = astman_get_header(m, "Async");
03866    const char *id = astman_get_header(m, "ActionID");
03867    const char *codecs = astman_get_header(m, "Codecs");
03868    struct ast_variable *vars;
03869    char *tech, *data;
03870    char *l = NULL, *n = NULL;
03871    int pi = 0;
03872    int res;
03873    int to = 30000;
03874    int reason = 0;
03875    char tmp[256];
03876    char tmp2[256];
03877    format_t format = AST_FORMAT_SLINEAR;
03878 
03879    pthread_t th;
03880    if (ast_strlen_zero(name)) {
03881       astman_send_error(s, m, "Channel not specified");
03882       return 0;
03883    }
03884    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
03885       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
03886          astman_send_error(s, m, "Invalid priority");
03887          return 0;
03888       }
03889    }
03890    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
03891       astman_send_error(s, m, "Invalid timeout");
03892       return 0;
03893    }
03894    ast_copy_string(tmp, name, sizeof(tmp));
03895    tech = tmp;
03896    data = strchr(tmp, '/');
03897    if (!data) {
03898       astman_send_error(s, m, "Invalid channel");
03899       return 0;
03900    }
03901    *data++ = '\0';
03902    ast_copy_string(tmp2, callerid, sizeof(tmp2));
03903    ast_callerid_parse(tmp2, &n, &l);
03904    if (n) {
03905       if (ast_strlen_zero(n)) {
03906          n = NULL;
03907       }
03908    }
03909    if (l) {
03910       ast_shrink_phone_number(l);
03911       if (ast_strlen_zero(l)) {
03912          l = NULL;
03913       }
03914    }
03915    if (!ast_strlen_zero(codecs)) {
03916       format = 0;
03917       ast_parse_allow_disallow(NULL, &format, codecs, 1);
03918    }
03919    if (!ast_strlen_zero(app)) {
03920       /* To run the System application (or anything else that goes to
03921        * shell), you must have the additional System privilege */
03922       if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
03923          && (
03924             strcasestr(app, "system") ||      /* System(rm -rf /)
03925                                                  TrySystem(rm -rf /)       */
03926             strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
03927                                                  TryExec(System(rm -rf /)) */
03928             strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
03929                                                  EAGI(/bin/rm,-rf /)       */
03930             strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
03931             strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
03932             )) {
03933          astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
03934          return 0;
03935       }
03936    }
03937    /* Allocate requested channel variables */
03938    vars = astman_get_variables(m);
03939 
03940    if (ast_true(async)) {
03941       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
03942       if (!fast) {
03943          res = -1;
03944       } else {
03945          if (!ast_strlen_zero(id))
03946             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
03947          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
03948          ast_copy_string(fast->data, data, sizeof(fast->data));
03949          ast_copy_string(fast->app, app, sizeof(fast->app));
03950          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
03951          if (l) {
03952             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
03953          }
03954          if (n) {
03955             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
03956          }
03957          fast->vars = vars;
03958          ast_copy_string(fast->context, context, sizeof(fast->context));
03959          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
03960          ast_copy_string(fast->account, account, sizeof(fast->account));
03961          fast->format = format;
03962          fast->timeout = to;
03963          fast->priority = pi;
03964          if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
03965             ast_free(fast);
03966             res = -1;
03967          } else {
03968             res = 0;
03969          }
03970       }
03971    } else if (!ast_strlen_zero(app)) {
03972       res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
03973    } else {
03974       if (exten && context && pi) {
03975          res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
03976       } else {
03977          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
03978          if (vars) {
03979             ast_variables_destroy(vars);
03980          }
03981          return 0;
03982       }
03983    }
03984    if (!res) {
03985       astman_send_ack(s, m, "Originate successfully queued");
03986    } else {
03987       astman_send_error(s, m, "Originate failed");
03988    }
03989    return 0;
03990 }
03991 
03992 static int action_mailboxstatus(struct mansession *s, const struct message *m)
03993 {
03994    const char *mailbox = astman_get_header(m, "Mailbox");
03995    int ret;
03996 
03997    if (ast_strlen_zero(mailbox)) {
03998       astman_send_error(s, m, "Mailbox not specified");
03999       return 0;
04000    }
04001    ret = ast_app_has_voicemail(mailbox, NULL);
04002    astman_start_ack(s, m);
04003    astman_append(s, "Message: Mailbox Status\r\n"
04004           "Mailbox: %s\r\n"
04005           "Waiting: %d\r\n\r\n", mailbox, ret);
04006    return 0;
04007 }
04008 
04009 static int action_mailboxcount(struct mansession *s, const struct message *m)
04010 {
04011    const char *mailbox = astman_get_header(m, "Mailbox");
04012    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
04013 
04014    if (ast_strlen_zero(mailbox)) {
04015       astman_send_error(s, m, "Mailbox not specified");
04016       return 0;
04017    }
04018    ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
04019    astman_start_ack(s, m);
04020    astman_append(s,   "Message: Mailbox Message Count\r\n"
04021             "Mailbox: %s\r\n"
04022             "UrgMessages: %d\r\n"
04023             "NewMessages: %d\r\n"
04024             "OldMessages: %d\r\n"
04025             "\r\n",
04026             mailbox, urgentmsgs, newmsgs, oldmsgs);
04027    return 0;
04028 }
04029 
04030 static int action_extensionstate(struct mansession *s, const struct message *m)
04031 {
04032    const char *exten = astman_get_header(m, "Exten");
04033    const char *context = astman_get_header(m, "Context");
04034    char hint[256] = "";
04035    int status;
04036    if (ast_strlen_zero(exten)) {
04037       astman_send_error(s, m, "Extension not specified");
04038       return 0;
04039    }
04040    if (ast_strlen_zero(context)) {
04041       context = "default";
04042    }
04043    status = ast_extension_state(NULL, context, exten);
04044    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
04045    astman_start_ack(s, m);
04046    astman_append(s,   "Message: Extension Status\r\n"
04047             "Exten: %s\r\n"
04048             "Context: %s\r\n"
04049             "Hint: %s\r\n"
04050             "Status: %d\r\n\r\n",
04051             exten, context, hint, status);
04052    return 0;
04053 }
04054 
04055 static int action_timeout(struct mansession *s, const struct message *m)
04056 {
04057    struct ast_channel *c;
04058    const char *name = astman_get_header(m, "Channel");
04059    double timeout = atof(astman_get_header(m, "Timeout"));
04060    struct timeval when = { timeout, 0 };
04061 
04062    if (ast_strlen_zero(name)) {
04063       astman_send_error(s, m, "No channel specified");
04064       return 0;
04065    }
04066 
04067    if (!timeout || timeout < 0) {
04068       astman_send_error(s, m, "No timeout specified");
04069       return 0;
04070    }
04071 
04072    if (!(c = ast_channel_get_by_name(name))) {
04073       astman_send_error(s, m, "No such channel");
04074       return 0;
04075    }
04076 
04077    when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
04078 
04079    ast_channel_lock(c);
04080    ast_channel_setwhentohangup_tv(c, when);
04081    ast_channel_unlock(c);
04082    c = ast_channel_unref(c);
04083 
04084    astman_send_ack(s, m, "Timeout Set");
04085 
04086    return 0;
04087 }
04088 
04089 static int whitefilter_cmp_fn(void *obj, void *arg, void *data, int flags)
04090 {
04091    regex_t *regex_filter = obj;
04092    const char *eventdata = arg;
04093    int *result = data;
04094 
04095    if (!regexec(regex_filter, eventdata, 0, NULL, 0)) {
04096       *result = 1;
04097       return (CMP_MATCH | CMP_STOP);
04098    }
04099 
04100    return 0;
04101 }
04102 
04103 static int blackfilter_cmp_fn(void *obj, void *arg, void *data, int flags)
04104 {
04105    regex_t *regex_filter = obj;
04106    const char *eventdata = arg;
04107    int *result = data;
04108 
04109    if (!regexec(regex_filter, eventdata, 0, NULL, 0)) {
04110       *result = 0;
04111       return (CMP_MATCH | CMP_STOP);
04112    }
04113 
04114    *result = 1;
04115    return 0;
04116 }
04117 
04118 static int match_filter(struct mansession *s, char *eventdata)
04119 {
04120    int result = 0;
04121 
04122    ast_debug(3, "Examining event:\n%s\n", eventdata);
04123    if (!ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
04124       return 1; /* no filtering means match all */
04125    } else if (ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
04126       /* white filters only: implied black all filter processed first, then white filters */
04127       ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04128    } else if (!ao2_container_count(s->session->whitefilters) && ao2_container_count(s->session->blackfilters)) {
04129       /* black filters only: implied white all filter processed first, then black filters */
04130       ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04131    } else {
04132       /* white and black filters: implied black all filter processed first, then white filters, and lastly black filters */
04133       ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04134       if (result) {
04135          result = 0;
04136          ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04137       }
04138    }
04139 
04140    return result;
04141 }
04142 
04143 /*!
04144  * Send any applicable events to the client listening on this socket.
04145  * Wait only for a finite time on each event, and drop all events whether
04146  * they are successfully sent or not.
04147  */
04148 static int process_events(struct mansession *s)
04149 {
04150    int ret = 0;
04151 
04152    ao2_lock(s->session);
04153    if (s->session->f != NULL) {
04154       struct eventqent *eqe = s->session->last_ev;
04155 
04156       while ((eqe = advance_event(eqe))) {
04157          if (!ret && s->session->authenticated &&
04158              (s->session->readperm & eqe->category) == eqe->category &&
04159              (s->session->send_events & eqe->category) == eqe->category) {
04160                if (match_filter(s, eqe->eventdata)) {
04161                   if (send_string(s, eqe->eventdata) < 0)
04162                      ret = -1;   /* don't send more */
04163                }
04164          }
04165          s->session->last_ev = eqe;
04166       }
04167    }
04168    ao2_unlock(s->session);
04169    return ret;
04170 }
04171 
04172 static int action_userevent(struct mansession *s, const struct message *m)
04173 {
04174    const char *event = astman_get_header(m, "UserEvent");
04175    struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
04176    int x;
04177 
04178    ast_str_reset(body);
04179 
04180    for (x = 0; x < m->hdrcount; x++) {
04181       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
04182          ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
04183       }
04184    }
04185 
04186    astman_send_ack(s, m, "Event Sent");   
04187    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
04188    return 0;
04189 }
04190 
04191 /*! \brief Show PBX core settings information */
04192 static int action_coresettings(struct mansession *s, const struct message *m)
04193 {
04194    const char *actionid = astman_get_header(m, "ActionID");
04195    char idText[150];
04196 
04197    if (!ast_strlen_zero(actionid)) {
04198       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04199    } else {
04200       idText[0] = '\0';
04201    }
04202 
04203    astman_append(s, "Response: Success\r\n"
04204          "%s"
04205          "AMIversion: %s\r\n"
04206          "AsteriskVersion: %s\r\n"
04207          "SystemName: %s\r\n"
04208          "CoreMaxCalls: %d\r\n"
04209          "CoreMaxLoadAvg: %f\r\n"
04210          "CoreRunUser: %s\r\n"
04211          "CoreRunGroup: %s\r\n"
04212          "CoreMaxFilehandles: %d\r\n"
04213          "CoreRealTimeEnabled: %s\r\n"
04214          "CoreCDRenabled: %s\r\n"
04215          "CoreHTTPenabled: %s\r\n"
04216          "\r\n",
04217          idText,
04218          AMI_VERSION,
04219          ast_get_version(),
04220          ast_config_AST_SYSTEM_NAME,
04221          option_maxcalls,
04222          option_maxload,
04223          ast_config_AST_RUN_USER,
04224          ast_config_AST_RUN_GROUP,
04225          option_maxfiles,
04226          AST_CLI_YESNO(ast_realtime_enabled()),
04227          AST_CLI_YESNO(check_cdr_enabled()),
04228          AST_CLI_YESNO(check_webmanager_enabled())
04229          );
04230    return 0;
04231 }
04232 
04233 /*! \brief Show PBX core status information */
04234 static int action_corestatus(struct mansession *s, const struct message *m)
04235 {
04236    const char *actionid = astman_get_header(m, "ActionID");
04237    char idText[150];
04238    char startuptime[150], startupdate[150];
04239    char reloadtime[150], reloaddate[150];
04240    struct ast_tm tm;
04241 
04242    if (!ast_strlen_zero(actionid)) {
04243       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04244    } else {
04245       idText[0] = '\0';
04246    }
04247 
04248    ast_localtime(&ast_startuptime, &tm, NULL);
04249    ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
04250    ast_strftime(startupdate, sizeof(startupdate), "%Y-%m-%d", &tm);
04251    ast_localtime(&ast_lastreloadtime, &tm, NULL);
04252    ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
04253    ast_strftime(reloaddate, sizeof(reloaddate), "%Y-%m-%d", &tm);
04254 
04255    astman_append(s, "Response: Success\r\n"
04256          "%s"
04257          "CoreStartupDate: %s\r\n"
04258          "CoreStartupTime: %s\r\n"
04259          "CoreReloadDate: %s\r\n"
04260          "CoreReloadTime: %s\r\n"
04261          "CoreCurrentCalls: %d\r\n"
04262          "\r\n",
04263          idText,
04264          startupdate,
04265          startuptime,
04266          reloaddate,
04267          reloadtime,
04268          ast_active_channels()
04269          );
04270    return 0;
04271 }
04272 
04273 /*! \brief Send a reload event */
04274 static int action_reload(struct mansession *s, const struct message *m)
04275 {
04276    const char *module = astman_get_header(m, "Module");
04277    int res = ast_module_reload(S_OR(module, NULL));
04278 
04279    if (res == 2) {
04280       astman_send_ack(s, m, "Module Reloaded");
04281    } else {
04282       astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
04283    }
04284    return 0;
04285 }
04286 
04287 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels
04288  *          and some information about them. */
04289 static int action_coreshowchannels(struct mansession *s, const struct message *m)
04290 {
04291    const char *actionid = astman_get_header(m, "ActionID");
04292    char idText[256];
04293    struct ast_channel *c = NULL;
04294    int numchans = 0;
04295    int duration, durh, durm, durs;
04296    struct ast_channel_iterator *iter;
04297 
04298    if (!ast_strlen_zero(actionid)) {
04299       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04300    } else {
04301       idText[0] = '\0';
04302    }
04303 
04304    if (!(iter = ast_channel_iterator_all_new())) {
04305       astman_send_error(s, m, "Memory Allocation Failure");
04306       return 1;
04307    }
04308 
04309    astman_send_listack(s, m, "Channels will follow", "start");
04310 
04311    for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
04312       struct ast_channel *bc;
04313       char durbuf[10] = "";
04314 
04315       ast_channel_lock(c);
04316 
04317       bc = ast_bridged_channel(c);
04318       if (c->cdr && !ast_tvzero(c->cdr->start)) {
04319          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
04320          durh = duration / 3600;
04321          durm = (duration % 3600) / 60;
04322          durs = duration % 60;
04323          snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
04324       }
04325 
04326       astman_append(s,
04327          "Event: CoreShowChannel\r\n"
04328          "%s"
04329          "Channel: %s\r\n"
04330          "UniqueID: %s\r\n"
04331          "Context: %s\r\n"
04332          "Extension: %s\r\n"
04333          "Priority: %d\r\n"
04334          "ChannelState: %d\r\n"
04335          "ChannelStateDesc: %s\r\n"
04336          "Application: %s\r\n"
04337          "ApplicationData: %s\r\n"
04338          "CallerIDnum: %s\r\n"
04339          "CallerIDname: %s\r\n"
04340          "ConnectedLineNum: %s\r\n"
04341          "ConnectedLineName: %s\r\n"
04342          "Duration: %s\r\n"
04343          "AccountCode: %s\r\n"
04344          "BridgedChannel: %s\r\n"
04345          "BridgedUniqueID: %s\r\n"
04346          "\r\n", idText, c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state,
04347          ast_state2str(c->_state), c->appl ? c->appl : "", c->data ? S_OR(c->data, "") : "",
04348          S_COR(c->caller.id.number.valid, c->caller.id.number.str, ""),
04349          S_COR(c->caller.id.name.valid, c->caller.id.name.str, ""),
04350          S_COR(c->connected.id.number.valid, c->connected.id.number.str, ""),
04351          S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
04352          durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
04353 
04354       ast_channel_unlock(c);
04355 
04356       numchans++;
04357    }
04358 
04359    astman_append(s,
04360       "Event: CoreShowChannelsComplete\r\n"
04361       "EventList: Complete\r\n"
04362       "ListItems: %d\r\n"
04363       "%s"
04364       "\r\n", numchans, idText);
04365 
04366    ast_channel_iterator_destroy(iter);
04367 
04368    return 0;
04369 }
04370 
04371 /* Manager function to check if module is loaded */
04372 static int manager_modulecheck(struct mansession *s, const struct message *m)
04373 {
04374    int res;
04375    const char *module = astman_get_header(m, "Module");
04376    const char *id = astman_get_header(m, "ActionID");
04377    char idText[256];
04378 #if !defined(LOW_MEMORY)
04379    const char *version;
04380 #endif
04381    char filename[PATH_MAX];
04382    char *cut;
04383 
04384    ast_copy_string(filename, module, sizeof(filename));
04385    if ((cut = strchr(filename, '.'))) {
04386       *cut = '\0';
04387    } else {
04388       cut = filename + strlen(filename);
04389    }
04390    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
04391    ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
04392    res = ast_module_check(filename);
04393    if (!res) {
04394       astman_send_error(s, m, "Module not loaded");
04395       return 0;
04396    }
04397    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
04398    ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
04399 #if !defined(LOW_MEMORY)
04400    version = ast_file_version_find(filename);
04401 #endif
04402 
04403    if (!ast_strlen_zero(id)) {
04404       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04405    } else {
04406       idText[0] = '\0';
04407    }
04408    astman_append(s, "Response: Success\r\n%s", idText);
04409 #if !defined(LOW_MEMORY)
04410    astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
04411 #endif
04412    return 0;
04413 }
04414 
04415 static int manager_moduleload(struct mansession *s, const struct message *m)
04416 {
04417    int res;
04418    const char *module = astman_get_header(m, "Module");
04419    const char *loadtype = astman_get_header(m, "LoadType");
04420 
04421    if (!loadtype || strlen(loadtype) == 0) {
04422       astman_send_error(s, m, "Incomplete ModuleLoad action.");
04423    }
04424    if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
04425       astman_send_error(s, m, "Need module name");
04426    }
04427 
04428    if (!strcasecmp(loadtype, "load")) {
04429       res = ast_load_resource(module);
04430       if (res) {
04431          astman_send_error(s, m, "Could not load module.");
04432       } else {
04433          astman_send_ack(s, m, "Module loaded.");
04434       }
04435    } else if (!strcasecmp(loadtype, "unload")) {
04436       res = ast_unload_resource(module, AST_FORCE_SOFT);
04437       if (res) {
04438          astman_send_error(s, m, "Could not unload module.");
04439       } else {
04440          astman_send_ack(s, m, "Module unloaded.");
04441       }
04442    } else if (!strcasecmp(loadtype, "reload")) {
04443       if (!ast_strlen_zero(module)) {
04444          res = ast_module_reload(module);
04445          if (res == 0) {
04446             astman_send_error(s, m, "No such module.");
04447          } else if (res == 1) {
04448             astman_send_error(s, m, "Module does not support reload action.");
04449          } else {
04450             astman_send_ack(s, m, "Module reloaded.");
04451          }
04452       } else {
04453          ast_module_reload(NULL);   /* Reload all modules */
04454          astman_send_ack(s, m, "All modules reloaded");
04455       }
04456    } else
04457       astman_send_error(s, m, "Incomplete ModuleLoad action.");
04458    return 0;
04459 }
04460 
04461 /*
04462  * Done with the action handlers here, we start with the code in charge
04463  * of accepting connections and serving them.
04464  * accept_thread() forks a new thread for each connection, session_do(),
04465  * which in turn calls get_input() repeatedly until a full message has
04466  * been accumulated, and then invokes process_message() to pass it to
04467  * the appropriate handler.
04468  */
04469 
04470 /*
04471  * Process an AMI message, performing desired action.
04472  * Return 0 on success, -1 on error that require the session to be destroyed.
04473  */
04474 static int process_message(struct mansession *s, const struct message *m)
04475 {
04476    char action[80] = "";
04477    int ret = 0;
04478    struct manager_action *tmp;
04479    const char *user = astman_get_header(m, "Username");
04480    int (*call_func)(struct mansession *s, const struct message *m) = NULL;
04481 
04482    ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
04483 
04484    if (ast_strlen_zero(action)) {
04485       report_req_bad_format(s, "NONE");
04486       mansession_lock(s);
04487       astman_send_error(s, m, "Missing action in request");
04488       mansession_unlock(s);
04489       return 0;
04490    }
04491 
04492    if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
04493       if (!s->session->authenticated) {
04494          report_req_not_allowed(s, action);
04495       }
04496       mansession_lock(s);
04497       astman_send_error(s, m, "Permission denied");
04498       mansession_unlock(s);
04499       return 0;
04500    }
04501 
04502    if (!allowmultiplelogin && !s->session->authenticated && user &&
04503       (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
04504       if (check_manager_session_inuse(user)) {
04505          report_session_limit(s);
04506          sleep(1);
04507          mansession_lock(s);
04508          astman_send_error(s, m, "Login Already In Use");
04509          mansession_unlock(s);
04510          return -1;
04511       }
04512    }
04513 
04514    AST_RWLIST_RDLOCK(&actions);
04515    AST_RWLIST_TRAVERSE(&actions, tmp, list) {
04516       if (strcasecmp(action, tmp->action)) {
04517          continue;
04518       }
04519       if (s->session->writeperm & tmp->authority || tmp->authority == 0) {
04520          call_func = tmp->func;
04521       }
04522       break;
04523    }
04524    AST_RWLIST_UNLOCK(&actions);
04525 
04526    if (tmp) {
04527       if (call_func) {
04528          /* Call our AMI function after we unlock our actions lists */
04529          ast_debug(1, "Running action '%s'\n", tmp->action);
04530          ret = call_func(s, m);
04531       } else {
04532          /* If we found our action but don't have a function pointer, access
04533           * was denied, so bail out.
04534           */
04535          report_req_not_allowed(s, action);
04536          mansession_lock(s);
04537          astman_send_error(s, m, "Permission denied");
04538          mansession_unlock(s);
04539       }
04540    } else {
04541       char buf[512];
04542       if (!tmp) {
04543          report_req_bad_format(s, action);
04544       }
04545       snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
04546       mansession_lock(s);
04547       astman_send_error(s, m, buf);
04548       mansession_unlock(s);
04549    }
04550    if (ret) {
04551       return ret;
04552    }
04553    /* Once done with our message, deliver any pending events unless the
04554       requester doesn't want them as part of this response.
04555    */
04556    if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
04557       return process_events(s);
04558    } else {
04559       return ret;
04560    }
04561 }
04562 
04563 /*!
04564  * Read one full line (including crlf) from the manager socket.
04565  * \note \verbatim
04566  * \r\n is the only valid terminator for the line.
04567  * (Note that, later, '\0' will be considered as the end-of-line marker,
04568  * so everything between the '\0' and the '\r\n' will not be used).
04569  * Also note that we assume output to have at least "maxlen" space.
04570  * \endverbatim
04571  */
04572 static int get_input(struct mansession *s, char *output)
04573 {
04574    int res, x;
04575    int maxlen = sizeof(s->session->inbuf) - 1;
04576    char *src = s->session->inbuf;
04577    int timeout = -1;
04578    time_t now;
04579 
04580    /*
04581     * Look for \r\n within the buffer. If found, copy to the output
04582     * buffer and return, trimming the \r\n (not used afterwards).
04583     */
04584    for (x = 0; x < s->session->inlen; x++) {
04585       int cr;  /* set if we have \r */
04586       if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
04587          cr = 2;  /* Found. Update length to include \r\n */
04588       } else if (src[x] == '\n') {
04589          cr = 1;  /* also accept \n only */
04590       } else {
04591          continue;
04592       }
04593       memmove(output, src, x);   /*... but trim \r\n */
04594       output[x] = '\0';    /* terminate the string */
04595       x += cr;       /* number of bytes used */
04596       s->session->inlen -= x;       /* remaining size */
04597       memmove(src, src + x, s->session->inlen); /* remove used bytes */
04598       return 1;
04599    }
04600    if (s->session->inlen >= maxlen) {
04601       /* no crlf found, and buffer full - sorry, too long for us */
04602       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
04603       s->session->inlen = 0;
04604    }
04605    res = 0;
04606    while (res == 0) {
04607       /* calculate a timeout if we are not authenticated */
04608       if (!s->session->authenticated) {
04609          if(time(&now) == -1) {
04610             ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
04611             return -1;
04612          }
04613 
04614          timeout = (authtimeout - (now - s->session->authstart)) * 1000;
04615          if (timeout < 0) {
04616             /* we have timed out */
04617             return 0;
04618          }
04619       }
04620 
04621       ao2_lock(s->session);
04622       if (s->session->pending_event) {
04623          s->session->pending_event = 0;
04624          ao2_unlock(s->session);
04625          return 0;
04626       }
04627       s->session->waiting_thread = pthread_self();
04628       ao2_unlock(s->session);
04629 
04630       res = ast_wait_for_input(s->session->fd, timeout);
04631 
04632       ao2_lock(s->session);
04633       s->session->waiting_thread = AST_PTHREADT_NULL;
04634       ao2_unlock(s->session);
04635    }
04636    if (res < 0) {
04637       /* If we get a signal from some other thread (typically because
04638        * there are new events queued), return 0 to notify the caller.
04639        */
04640       if (errno == EINTR || errno == EAGAIN) {
04641          return 0;
04642       }
04643       ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
04644       return -1;
04645    }
04646 
04647    ao2_lock(s->session);
04648    res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
04649    if (res < 1) {
04650       res = -1;   /* error return */
04651    } else {
04652       s->session->inlen += res;
04653       src[s->session->inlen] = '\0';
04654       res = 0;
04655    }
04656    ao2_unlock(s->session);
04657    return res;
04658 }
04659 
04660 static int do_message(struct mansession *s)
04661 {
04662    struct message m = { 0 };
04663    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
04664    int res;
04665    time_t now;
04666 
04667    for (;;) {
04668       /* Check if any events are pending and do them if needed */
04669       if (process_events(s)) {
04670          return -1;
04671       }
04672       res = get_input(s, header_buf);
04673       if (res == 0) {
04674          if (!s->session->authenticated) {
04675             if(time(&now) == -1) {
04676                ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
04677                return -1;
04678             }
04679 
04680             if (now - s->session->authstart > authtimeout) {
04681                if (displayconnects) {
04682                   ast_verb(2, "Client from %s, failed to authenticate in %d seconds\n", ast_inet_ntoa(s->session->sin.sin_addr), authtimeout);
04683                }
04684                return -1;
04685             }
04686          }
04687          continue;
04688       } else if (res > 0) {
04689          if (ast_strlen_zero(header_buf)) {
04690             return process_message(s, &m) ? -1 : 0;
04691          } else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) {
04692             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
04693          }
04694       } else {
04695          return res;
04696       }
04697    }
04698 }
04699 
04700 /*! \brief The body of the individual manager session.
04701  * Call get_input() to read one line at a time
04702  * (or be woken up on new events), collect the lines in a
04703  * message until found an empty line, and execute the request.
04704  * In any case, deliver events asynchronously through process_events()
04705  * (called from here if no line is available, or at the end of
04706  * process_message(). )
04707  */
04708 static void *session_do(void *data)
04709 {
04710    struct ast_tcptls_session_instance *ser = data;
04711    struct mansession_session *session;
04712    struct mansession s = {
04713       .tcptls_session = data,
04714    };
04715    int flags;
04716    int res;
04717    struct sockaddr_in ser_remote_address_tmp;
04718    struct protoent *p;
04719 
04720    if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
04721       fclose(ser->f);
04722       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04723       goto done;
04724    }
04725 
04726    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
04727    session = build_mansession(ser_remote_address_tmp);
04728 
04729    if (session == NULL) {
04730       fclose(ser->f);
04731       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04732       goto done;
04733    }
04734 
04735    /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
04736     * This is necessary to prevent delays (caused by buffering) as we
04737     * write to the socket in bits and peices. */
04738    p = getprotobyname("tcp");
04739    if (p) {
04740       int arg = 1;
04741       if( setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
04742          ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\nSome manager actions may be slow to respond.\n", strerror(errno));
04743       }
04744    } else {
04745       ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY, getprotobyname(\"tcp\") failed\nSome manager actions may be slow to respond.\n");
04746    }
04747 
04748    flags = fcntl(ser->fd, F_GETFL);
04749    if (!block_sockets) { /* make sure socket is non-blocking */
04750       flags |= O_NONBLOCK;
04751    } else {
04752       flags &= ~O_NONBLOCK;
04753    }
04754    fcntl(ser->fd, F_SETFL, flags);
04755 
04756    ao2_lock(session);
04757    /* Hook to the tail of the event queue */
04758    session->last_ev = grab_last();
04759 
04760    ast_mutex_init(&s.lock);
04761 
04762    /* these fields duplicate those in the 'ser' structure */
04763    session->fd = s.fd = ser->fd;
04764    session->f = s.f = ser->f;
04765    session->sin = ser_remote_address_tmp;
04766    s.session = session;
04767 
04768    AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
04769 
04770    if(time(&session->authstart) == -1) {
04771       ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
04772       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04773       ao2_unlock(session);
04774       session_destroy(session);
04775       goto done;
04776    }
04777    ao2_unlock(session);
04778 
04779    astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);   /* welcome prompt */
04780    for (;;) {
04781       if ((res = do_message(&s)) < 0 || s.write_error) {
04782          break;
04783       }
04784    }
04785    /* session is over, explain why and terminate */
04786    if (session->authenticated) {
04787       if (manager_displayconnects(session)) {
04788          ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
04789       }
04790    } else {
04791       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04792       if (displayconnects) {
04793          ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
04794       }
04795    }
04796 
04797    session_destroy(session);
04798 
04799    ast_mutex_destroy(&s.lock);
04800 done:
04801    ao2_ref(ser, -1);
04802    ser = NULL;
04803    return NULL;
04804 }
04805 
04806 /*! \brief remove at most n_max stale session from the list. */
04807 static void purge_sessions(int n_max)
04808 {
04809    struct mansession_session *session;
04810    time_t now = time(NULL);
04811    struct ao2_iterator i;
04812 
04813    i = ao2_iterator_init(sessions, 0);
04814    while ((session = ao2_iterator_next(&i)) && n_max > 0) {
04815       ao2_lock(session);
04816       if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
04817          if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
04818             ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
04819                session->username, ast_inet_ntoa(session->sin.sin_addr));
04820          }
04821          ao2_unlock(session);
04822          session_destroy(session);
04823          n_max--;
04824       } else {
04825          ao2_unlock(session);
04826          unref_mansession(session);
04827       }
04828    }
04829    ao2_iterator_destroy(&i);
04830 }
04831 
04832 /*
04833  * events are appended to a queue from where they
04834  * can be dispatched to clients.
04835  */
04836 static int append_event(const char *str, int category)
04837 {
04838    struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
04839    static int seq;   /* sequence number */
04840 
04841    if (!tmp) {
04842       return -1;
04843    }
04844 
04845    /* need to init all fields, because ast_malloc() does not */
04846    tmp->usecount = 0;
04847    tmp->category = category;
04848    tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
04849    tmp->tv = ast_tvnow();
04850    AST_RWLIST_NEXT(tmp, eq_next) = NULL;
04851    strcpy(tmp->eventdata, str);
04852 
04853    AST_RWLIST_WRLOCK(&all_events);
04854    AST_RWLIST_INSERT_TAIL(&all_events, tmp, eq_next);
04855    AST_RWLIST_UNLOCK(&all_events);
04856 
04857    return 0;
04858 }
04859 
04860 AST_THREADSTORAGE(manager_event_funcbuf);
04861 
04862 static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
04863 {
04864    struct manager_channel_variable *var;
04865    AST_RWLIST_RDLOCK(&channelvars);
04866    AST_LIST_TRAVERSE(&channelvars, var, entry) {
04867       const char *val = "";
04868       if (var->isfunc) {
04869          struct ast_str *res = ast_str_thread_get(&manager_event_funcbuf, 16);
04870          int ret;
04871          if (res && (ret = ast_func_read2(chan, var->name, &res, 0)) == 0) {
04872             val = ast_str_buffer(res);
04873          }
04874       } else {
04875          val = pbx_builtin_getvar_helper(chan, var->name);
04876       }
04877       ast_str_append(pbuf, 0, "ChanVariable(%s): %s=%s\r\n", chan->name, var->name, val ? val : "");
04878    }
04879    AST_RWLIST_UNLOCK(&channelvars);
04880 }
04881 
04882 /* XXX see if can be moved inside the function */
04883 AST_THREADSTORAGE(manager_event_buf);
04884 #define MANAGER_EVENT_BUF_INITSIZE   256
04885 
04886 int __ast_manager_event_multichan(int category, const char *event, int chancount, struct
04887    ast_channel **chans, const char *file, int line, const char *func, const char *fmt, ...)
04888 {
04889    struct mansession_session *session;
04890    struct manager_custom_hook *hook;
04891    struct ast_str *auth = ast_str_alloca(80);
04892    const char *cat_str;
04893    va_list ap;
04894    struct timeval now;
04895    struct ast_str *buf;
04896    int i;
04897 
04898    if (!(sessions && ao2_container_count(sessions)) && AST_RWLIST_EMPTY(&manager_hooks)) {
04899       return 0;
04900    }
04901    
04902    if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) {
04903       return -1;
04904    }
04905 
04906    cat_str = authority_to_str(category, &auth);
04907    ast_str_set(&buf, 0,
04908          "Event: %s\r\nPrivilege: %s\r\n",
04909           event, cat_str);
04910 
04911    if (timestampevents) {
04912       now = ast_tvnow();
04913       ast_str_append(&buf, 0,
04914             "Timestamp: %ld.%06lu\r\n",
04915              (long)now.tv_sec, (unsigned long) now.tv_usec);
04916    }
04917    if (manager_debug) {
04918       static int seq;
04919       ast_str_append(&buf, 0,
04920             "SequenceNumber: %d\r\n",
04921              ast_atomic_fetchadd_int(&seq, 1));
04922       ast_str_append(&buf, 0,
04923             "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
04924    }
04925 
04926    va_start(ap, fmt);
04927    ast_str_append_va(&buf, 0, fmt, ap);
04928    va_end(ap);
04929    for (i = 0; i < chancount; i++) {
04930       append_channel_vars(&buf, chans[i]);
04931    }
04932 
04933    ast_str_append(&buf, 0, "\r\n");
04934 
04935    append_event(ast_str_buffer(buf), category);
04936 
04937    /* Wake up any sleeping sessions */
04938    if (sessions) {
04939       struct ao2_iterator i;
04940       i = ao2_iterator_init(sessions, 0);
04941       while ((session = ao2_iterator_next(&i))) {
04942          ao2_lock(session);
04943          if (session->waiting_thread != AST_PTHREADT_NULL) {
04944             pthread_kill(session->waiting_thread, SIGURG);
04945          } else {
04946             /* We have an event to process, but the mansession is
04947              * not waiting for it. We still need to indicate that there
04948              * is an event waiting so that get_input processes the pending
04949              * event instead of polling.
04950              */
04951             session->pending_event = 1;
04952          }
04953          ao2_unlock(session);
04954          unref_mansession(session);
04955       }
04956       ao2_iterator_destroy(&i);
04957    }
04958 
04959    if (!AST_RWLIST_EMPTY(&manager_hooks)) {
04960       AST_RWLIST_RDLOCK(&manager_hooks);
04961       AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
04962          hook->helper(category, event, ast_str_buffer(buf));
04963       }
04964       AST_RWLIST_UNLOCK(&manager_hooks);
04965    }
04966 
04967    return 0;
04968 }
04969 
04970 /*
04971  * support functions to register/unregister AMI action handlers,
04972  */
04973 int ast_manager_unregister(char *action)
04974 {
04975    struct manager_action *cur;
04976    struct timespec tv = { 5, };
04977 
04978    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
04979       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
04980       return -1;
04981    }
04982    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
04983       if (!strcasecmp(action, cur->action)) {
04984          AST_RWLIST_REMOVE_CURRENT(list);
04985          ast_string_field_free_memory(cur);
04986          ast_free(cur);
04987          ast_verb(2, "Manager unregistered action %s\n", action);
04988          break;
04989       }
04990    }
04991    AST_RWLIST_TRAVERSE_SAFE_END;
04992    AST_RWLIST_UNLOCK(&actions);
04993 
04994    return 0;
04995 }
04996 
04997 static int manager_state_cb(char *context, char *exten, int state, void *data)
04998 {
04999    /* Notify managers of change */
05000    char hint[512];
05001    ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
05002 
05003    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
05004    return 0;
05005 }
05006 
05007 static int ast_manager_register_struct(struct manager_action *act)
05008 {
05009    struct manager_action *cur, *prev = NULL;
05010    struct timespec tv = { 5, };
05011 
05012    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
05013       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
05014       return -1;
05015    }
05016    AST_RWLIST_TRAVERSE(&actions, cur, list) {
05017       int ret = strcasecmp(cur->action, act->action);
05018       if (ret == 0) {
05019          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
05020          AST_RWLIST_UNLOCK(&actions);
05021          return -1;
05022       }
05023       if (ret > 0) { /* Insert these alphabetically */
05024          prev = cur;
05025          break;
05026       }
05027    }
05028 
05029    if (prev) {
05030       AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
05031    } else {
05032       AST_RWLIST_INSERT_HEAD(&actions, act, list);
05033    }
05034 
05035    ast_verb(2, "Manager registered action %s\n", act->action);
05036 
05037    AST_RWLIST_UNLOCK(&actions);
05038 
05039    return 0;
05040 }
05041 
05042 /*! \brief register a new command with manager, including online help. This is
05043    the preferred way to register a manager command */
05044 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
05045 {
05046    struct manager_action *cur = NULL;
05047 #ifdef AST_XML_DOCS
05048    char *tmpxml;
05049 #endif
05050 
05051    if (!(cur = ast_calloc_with_stringfields(1, struct manager_action, 128))) {
05052       return -1;
05053    }
05054 
05055    cur->action = action;
05056    cur->authority = auth;
05057    cur->func = func;
05058 #ifdef AST_XML_DOCS
05059    if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
05060       tmpxml = ast_xmldoc_build_synopsis("manager", action);
05061       ast_string_field_set(cur, synopsis, tmpxml);
05062       ast_free(tmpxml);
05063 
05064       tmpxml = ast_xmldoc_build_syntax("manager", action);
05065       ast_string_field_set(cur, syntax, tmpxml);
05066       ast_free(tmpxml);
05067 
05068       tmpxml = ast_xmldoc_build_description("manager", action);
05069       ast_string_field_set(cur, description, tmpxml);
05070       ast_free(tmpxml);
05071 
05072       tmpxml = ast_xmldoc_build_seealso("manager", action);
05073       ast_string_field_set(cur, seealso, tmpxml);
05074       ast_free(tmpxml);
05075 
05076       tmpxml = ast_xmldoc_build_arguments("manager", action);
05077       ast_string_field_set(cur, arguments, tmpxml);
05078       ast_free(tmpxml);
05079 
05080       cur->docsrc = AST_XML_DOC;
05081    } else {
05082 #endif
05083       ast_string_field_set(cur, synopsis, synopsis);
05084       ast_string_field_set(cur, description, description);
05085 #ifdef AST_XML_DOCS
05086       cur->docsrc = AST_STATIC_DOC;
05087    }
05088 #endif
05089    if (ast_manager_register_struct(cur)) {
05090       ast_free(cur);
05091       return -1;
05092    }
05093 
05094    return 0;
05095 }
05096 /*! @}
05097  END Doxygen group */
05098 
05099 /*
05100  * The following are support functions for AMI-over-http.
05101  * The common entry point is generic_http_callback(),
05102  * which extracts HTTP header and URI fields and reformats
05103  * them into AMI messages, locates a proper session
05104  * (using the mansession_id Cookie or GET variable),
05105  * and calls process_message() as for regular AMI clients.
05106  * When done, the output (which goes to a temporary file)
05107  * is read back into a buffer and reformatted as desired,
05108  * then fed back to the client over the original socket.
05109  */
05110 
05111 enum output_format {
05112    FORMAT_RAW,
05113    FORMAT_HTML,
05114    FORMAT_XML,
05115 };
05116 
05117 static const char * const contenttype[] = {
05118    [FORMAT_RAW] = "plain",
05119    [FORMAT_HTML] = "html",
05120    [FORMAT_XML] =  "xml",
05121 };
05122 
05123 /*!
05124  * locate an http session in the list. The search key (ident) is
05125  * the value of the mansession_id cookie (0 is not valid and means
05126  * a session on the AMI socket).
05127  */
05128 static struct mansession_session *find_session(uint32_t ident, int incinuse)
05129 {
05130    struct mansession_session *session;
05131    struct ao2_iterator i;
05132 
05133    if (ident == 0) {
05134       return NULL;
05135    }
05136 
05137    i = ao2_iterator_init(sessions, 0);
05138    while ((session = ao2_iterator_next(&i))) {
05139       ao2_lock(session);
05140       if (session->managerid == ident && !session->needdestroy) {
05141          ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
05142          break;
05143       }
05144       ao2_unlock(session);
05145       unref_mansession(session);
05146    }
05147    ao2_iterator_destroy(&i);
05148 
05149    return session;
05150 }
05151 
05152 /*!
05153  * locate an http session in the list.
05154  * The search keys (nonce) and (username) is value from received
05155  * "Authorization" http header.
05156  * As well as in find_session() function, the value of the nonce can't be zero.
05157  * (0 meansi, that the session used for AMI socket connection).
05158  * Flag (stale) is set, if client used valid, but old, nonce value.
05159  *
05160  */
05161 static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
05162 {
05163    struct mansession_session *session;
05164    struct ao2_iterator i;
05165 
05166    if (nonce == 0 || username == NULL || stale == NULL) {
05167       return NULL;
05168    }
05169 
05170    i = ao2_iterator_init(sessions, 0);
05171    while ((session = ao2_iterator_next(&i))) {
05172       ao2_lock(session);
05173       if (!strcasecmp(session->username, username) && session->managerid == nonce) {
05174          *stale = 0;
05175          break;
05176       } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
05177          *stale = 1;
05178          break;
05179       }
05180       ao2_unlock(session);
05181       unref_mansession(session);
05182    }
05183    ao2_iterator_destroy(&i);
05184    return session;
05185 }
05186 
05187 int astman_is_authed(uint32_t ident)
05188 {
05189    int authed;
05190    struct mansession_session *session;
05191 
05192    if (!(session = find_session(ident, 0)))
05193       return 0;
05194 
05195    authed = (session->authenticated != 0);
05196 
05197    ao2_unlock(session);
05198    unref_mansession(session);
05199 
05200    return authed;
05201 }
05202 
05203 int astman_verify_session_readpermissions(uint32_t ident, int perm)
05204 {
05205    int result = 0;
05206    struct mansession_session *session;
05207    struct ao2_iterator i;
05208 
05209    if (ident == 0) {
05210       return 0;
05211    }
05212 
05213    i = ao2_iterator_init(sessions, 0);
05214    while ((session = ao2_iterator_next(&i))) {
05215       ao2_lock(session);
05216       if ((session->managerid == ident) && (session->readperm & perm)) {
05217          result = 1;
05218          ao2_unlock(session);
05219          unref_mansession(session);
05220          break;
05221       }
05222       ao2_unlock(session);
05223       unref_mansession(session);
05224    }
05225    ao2_iterator_destroy(&i);
05226    return result;
05227 }
05228 
05229 int astman_verify_session_writepermissions(uint32_t ident, int perm)
05230 {
05231    int result = 0;
05232    struct mansession_session *session;
05233    struct ao2_iterator i;
05234 
05235    if (ident == 0) {
05236       return 0;
05237    }
05238 
05239    i = ao2_iterator_init(sessions, 0);
05240    while ((session = ao2_iterator_next(&i))) {
05241       ao2_lock(session);
05242       if ((session->managerid == ident) && (session->writeperm & perm)) {
05243          result = 1;
05244          ao2_unlock(session);
05245          unref_mansession(session);
05246          break;
05247       }
05248       ao2_unlock(session);
05249       unref_mansession(session);
05250    }
05251    ao2_iterator_destroy(&i);
05252    return result;
05253 }
05254 
05255 /*
05256  * convert to xml with various conversion:
05257  * mode & 1 -> lowercase;
05258  * mode & 2 -> replace non-alphanumeric chars with underscore
05259  */
05260 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
05261 {
05262    /* store in a local buffer to avoid calling ast_str_append too often */
05263    char buf[256];
05264    char *dst = buf;
05265    int space = sizeof(buf);
05266    /* repeat until done and nothing to flush */
05267    for ( ; *src || dst != buf ; src++) {
05268       if (*src == '\0' || space < 10) {   /* flush */
05269          *dst++ = '\0';
05270          ast_str_append(out, 0, "%s", buf);
05271          dst = buf;
05272          space = sizeof(buf);
05273          if (*src == '\0') {
05274             break;
05275          }
05276       }
05277 
05278       if ( (mode & 2) && !isalnum(*src)) {
05279          *dst++ = '_';
05280          space--;
05281          continue;
05282       }
05283       switch (*src) {
05284       case '<':
05285          strcpy(dst, "&lt;");
05286          dst += 4;
05287          space -= 4;
05288          break;
05289       case '>':
05290          strcpy(dst, "&gt;");
05291          dst += 4;
05292          space -= 4;
05293          break;
05294       case '\"':
05295          strcpy(dst, "&quot;");
05296          dst += 6;
05297          space -= 6;
05298          break;
05299       case '\'':
05300          strcpy(dst, "&apos;");
05301          dst += 6;
05302          space -= 6;
05303          break;
05304       case '&':
05305          strcpy(dst, "&amp;");
05306          dst += 5;
05307          space -= 5;
05308          break;
05309 
05310       default:
05311          *dst++ = mode ? tolower(*src) : *src;
05312          space--;
05313       }
05314    }
05315 }
05316 
05317 struct variable_count {
05318    char *varname;
05319    int count;
05320 };
05321 
05322 static int variable_count_hash_fn(const void *vvc, const int flags)
05323 {
05324    const struct variable_count *vc = vvc;
05325 
05326    return ast_str_hash(vc->varname);
05327 }
05328 
05329 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
05330 {
05331    /* Due to the simplicity of struct variable_count, it makes no difference
05332     * if you pass in objects or strings, the same operation applies. This is
05333     * due to the fact that the hash occurs on the first element, which means
05334     * the address of both the struct and the string are exactly the same. */
05335    struct variable_count *vc = obj;
05336    char *str = vstr;
05337    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
05338 }
05339 
05340 /*! \brief Convert the input into XML or HTML.
05341  * The input is supposed to be a sequence of lines of the form
05342  * Name: value
05343  * optionally followed by a blob of unformatted text.
05344  * A blank line is a section separator. Basically, this is a
05345  * mixture of the format of Manager Interface and CLI commands.
05346  * The unformatted text is considered as a single value of a field
05347  * named 'Opaque-data'.
05348  *
05349  * At the moment the output format is the following (but it may
05350  * change depending on future requirements so don't count too
05351  * much on it when writing applications):
05352  *
05353  * General: the unformatted text is used as a value of
05354  * XML output:  to be completed
05355  *
05356  * \verbatim
05357  *   Each section is within <response type="object" id="xxx">
05358  *   where xxx is taken from ajaxdest variable or defaults to unknown
05359  *   Each row is reported as an attribute Name="value" of an XML
05360  *   entity named from the variable ajaxobjtype, default to "generic"
05361  * \endverbatim
05362  *
05363  * HTML output:
05364  *   each Name-value pair is output as a single row of a two-column table.
05365  *   Sections (blank lines in the input) are separated by a <HR>
05366  *
05367  */
05368 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
05369 {
05370    struct ast_variable *v;
05371    const char *dest = NULL;
05372    char *var, *val;
05373    const char *objtype = NULL;
05374    int in_data = 0;  /* parsing data */
05375    int inobj = 0;
05376    int xml = (format == FORMAT_XML);
05377    struct variable_count *vc = NULL;
05378    struct ao2_container *vco = NULL;
05379 
05380    if (xml) {
05381       /* dest and objtype need only for XML format */
05382       for (v = get_vars; v; v = v->next) {
05383          if (!strcasecmp(v->name, "ajaxdest")) {
05384             dest = v->value;
05385          } else if (!strcasecmp(v->name, "ajaxobjtype")) {
05386             objtype = v->value;
05387          }
05388       }
05389       if (ast_strlen_zero(dest)) {
05390          dest = "unknown";
05391       }
05392       if (ast_strlen_zero(objtype)) {
05393          objtype = "generic";
05394       }
05395    }
05396 
05397    /* we want to stop when we find an empty line */
05398    while (in && *in) {
05399       val = strsep(&in, "\r\n"); /* mark start and end of line */
05400       if (in && *in == '\n') {   /* remove trailing \n if any */
05401          in++;
05402       }
05403       ast_trim_blanks(val);
05404       ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
05405       if (ast_strlen_zero(val)) {
05406          /* empty line */
05407          if (in_data) {
05408             /* close data in Opaque mode */
05409             ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
05410             in_data = 0;
05411          }
05412 
05413          if (inobj) {
05414             /* close block */
05415             ast_str_append(out, 0, xml ? " /></response>\n" :
05416                "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
05417             inobj = 0;
05418             ao2_ref(vco, -1);
05419             vco = NULL;
05420          }
05421          continue;
05422       }
05423 
05424       if (!inobj) {
05425          /* start new block */
05426          if (xml) {
05427             ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
05428          }
05429          vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
05430          inobj = 1;
05431       }
05432 
05433       if (in_data) {
05434          /* Process data field in Opaque mode */
05435          xml_copy_escape(out, val, 0);   /* data field */
05436          ast_str_append(out, 0, xml ? "\n" : "<br>\n");
05437          continue;
05438       }
05439 
05440       /* We expect "Name: value" line here */
05441       var = strsep(&val, ":");
05442       if (val) {
05443          /* found the field name */
05444          val = ast_skip_blanks(val);
05445          ast_trim_blanks(var);
05446       } else {
05447          /* field name not found, switch to opaque mode */
05448          val = var;
05449          var = "Opaque-data";
05450          in_data = 1;
05451       }
05452 
05453 
05454       ast_str_append(out, 0, xml ? " " : "<tr><td>");
05455       if ((vc = ao2_find(vco, var, 0))) {
05456          vc->count++;
05457       } else {
05458          /* Create a new entry for this one */
05459          vc = ao2_alloc(sizeof(*vc), NULL);
05460          vc->varname = var;
05461          vc->count = 1;
05462          ao2_link(vco, vc);
05463       }
05464 
05465       xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
05466       if (vc->count > 1) {
05467          ast_str_append(out, 0, "-%d", vc->count);
05468       }
05469       ao2_ref(vc, -1);
05470       ast_str_append(out, 0, xml ? "='" : "</td><td>");
05471       xml_copy_escape(out, val, 0); /* data field */
05472       ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
05473    }
05474 
05475    if (inobj) {
05476       ast_str_append(out, 0, xml ? " /></response>\n" :
05477          "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
05478       ao2_ref(vco, -1);
05479    }
05480 }
05481 
05482 static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
05483 {
05484    char *buf;
05485    size_t l;
05486 
05487    if (!s->f)
05488       return;
05489 
05490    /* Ensure buffer is NULL-terminated */
05491    fprintf(s->f, "%c", 0);
05492    fflush(s->f);
05493 
05494    if ((l = ftell(s->f))) {
05495       if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s->fd, 0))) {
05496          ast_log(LOG_WARNING, "mmap failed.  Manager output was not processed\n");
05497       } else {
05498          if (format == FORMAT_XML || format == FORMAT_HTML) {
05499             xml_translate(out, buf, params, format);
05500          } else {
05501             ast_str_append(out, 0, "%s", buf);
05502          }
05503          munmap(buf, l);
05504       }
05505    } else if (format == FORMAT_XML || format == FORMAT_HTML) {
05506       xml_translate(out, "", params, format);
05507    }
05508 
05509    fclose(s->f);
05510    s->f = NULL;
05511    close(s->fd);
05512    s->fd = -1;
05513 }
05514 
05515 static int generic_http_callback(struct ast_tcptls_session_instance *ser,
05516                     enum ast_http_method method,
05517                     enum output_format format,
05518                     struct sockaddr_in *remote_address, const char *uri,
05519                     struct ast_variable *get_params,
05520                     struct ast_variable *headers)
05521 {
05522    struct mansession s = { .session = NULL, .tcptls_session = ser };
05523    struct mansession_session *session = NULL;
05524    uint32_t ident = 0;
05525    int blastaway = 0;
05526    struct ast_variable *v, *cookies, *params = get_params;
05527    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
05528    struct ast_str *http_header = NULL, *out = NULL;
05529    struct message m = { 0 };
05530    unsigned int x;
05531    size_t hdrlen;
05532 
05533    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
05534       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
05535       return -1;
05536    }
05537 
05538    cookies = ast_http_get_cookies(headers);
05539    for (v = cookies; v; v = v->next) {
05540       if (!strcasecmp(v->name, "mansession_id")) {
05541          sscanf(v->value, "%30x", &ident);
05542          break;
05543       }
05544    }
05545    if (cookies) {
05546       ast_variables_destroy(cookies);
05547    }
05548 
05549    if (!(session = find_session(ident, 1))) {
05550 
05551       /**/
05552       /* Create new session.
05553        * While it is not in the list we don't need any locking
05554        */
05555       if (!(session = build_mansession(*remote_address))) {
05556          ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
05557          return -1;
05558       }
05559       ao2_lock(session);
05560       session->sin = *remote_address;
05561       session->fd = -1;
05562       session->waiting_thread = AST_PTHREADT_NULL;
05563       session->send_events = 0;
05564       session->inuse = 1;
05565       /*!\note There is approximately a 1 in 1.8E19 chance that the following
05566        * calculation will produce 0, which is an invalid ID, but due to the
05567        * properties of the rand() function (and the constantcy of s), that
05568        * won't happen twice in a row.
05569        */
05570       while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
05571       session->last_ev = grab_last();
05572       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
05573    }
05574    ao2_unlock(session);
05575 
05576    http_header = ast_str_create(128);
05577    out = ast_str_create(2048);
05578 
05579    ast_mutex_init(&s.lock);
05580 
05581    if (http_header == NULL || out == NULL) {
05582       ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
05583       goto generic_callback_out;
05584    }
05585 
05586    s.session = session;
05587    s.fd = mkstemp(template);  /* create a temporary file for command output */
05588    unlink(template);
05589    if (s.fd <= -1) {
05590       ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
05591       goto generic_callback_out;
05592    }
05593    s.f = fdopen(s.fd, "w+");
05594    if (!s.f) {
05595       ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
05596       ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
05597       close(s.fd);
05598       goto generic_callback_out;
05599    }
05600 
05601    if (method == AST_HTTP_POST) {
05602       params = ast_http_get_post_vars(ser, headers);
05603    }
05604 
05605    for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
05606       hdrlen = strlen(v->name) + strlen(v->value) + 3;
05607       m.headers[m.hdrcount] = alloca(hdrlen);
05608       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
05609       ast_debug(1, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
05610       m.hdrcount = x + 1;
05611    }
05612 
05613    if (process_message(&s, &m)) {
05614       if (session->authenticated) {
05615          if (manager_displayconnects(session)) {
05616             ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
05617          }
05618       } else {
05619          if (displayconnects) {
05620             ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
05621          }
05622       }
05623       session->needdestroy = 1;
05624    }
05625 
05626    ast_str_append(&http_header, 0,
05627       "Content-type: text/%s\r\n"
05628       "Cache-Control: no-cache;\r\n"
05629       "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
05630       "Pragma: SuppressEvents\r\n",
05631       contenttype[format],
05632       session->managerid, httptimeout);
05633 
05634    if (format == FORMAT_XML) {
05635       ast_str_append(&out, 0, "<ajax-response>\n");
05636    } else if (format == FORMAT_HTML) {
05637       /*
05638        * When handling AMI-over-HTTP in HTML format, we provide a simple form for
05639        * debugging purposes. This HTML code should not be here, we
05640        * should read from some config file...
05641        */
05642 
05643 #define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
05644 #define TEST_STRING \
05645    "<form action=\"manager\" method=\"post\">\n\
05646    Action: <select name=\"action\">\n\
05647       <option value=\"\">-----&gt;</option>\n\
05648       <option value=\"login\">login</option>\n\
05649       <option value=\"command\">Command</option>\n\
05650       <option value=\"waitevent\">waitevent</option>\n\
05651       <option value=\"listcommands\">listcommands</option>\n\
05652    </select>\n\
05653    or <input name=\"action\"><br/>\n\
05654    CLI Command <input name=\"command\"><br>\n\
05655    user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
05656    <input type=\"submit\">\n</form>\n"
05657 
05658       ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
05659       ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
05660       ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
05661       ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
05662    }
05663 
05664    process_output(&s, &out, params, format);
05665 
05666    if (format == FORMAT_XML) {
05667       ast_str_append(&out, 0, "</ajax-response>\n");
05668    } else if (format == FORMAT_HTML) {
05669       ast_str_append(&out, 0, "</table></body>\r\n");
05670    }
05671 
05672    ao2_lock(session);
05673    /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
05674    session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
05675 
05676    if (session->needdestroy) {
05677       if (session->inuse == 1) {
05678          ast_debug(1, "Need destroy, doing it now!\n");
05679          blastaway = 1;
05680       } else {
05681          ast_debug(1, "Need destroy, but can't do it yet!\n");
05682          if (session->waiting_thread != AST_PTHREADT_NULL) {
05683             pthread_kill(session->waiting_thread, SIGURG);
05684          }
05685          session->inuse--;
05686       }
05687    } else {
05688       session->inuse--;
05689    }
05690    ao2_unlock(session);
05691 
05692    ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
05693    http_header = out = NULL;
05694 
05695 generic_callback_out:
05696    ast_mutex_destroy(&s.lock);
05697 
05698    /* Clear resource */
05699 
05700    if (method == AST_HTTP_POST && params) {
05701       ast_variables_destroy(params);
05702    }
05703    if (http_header) {
05704       ast_free(http_header);
05705    }
05706    if (out) {
05707       ast_free(out);
05708    }
05709 
05710    if (session && blastaway) {
05711       session_destroy(session);
05712    } else if (session && session->f) {
05713       fclose(session->f);
05714       session->f = NULL;
05715    }
05716 
05717    return 0;
05718 }
05719 
05720 static int auth_http_callback(struct ast_tcptls_session_instance *ser,
05721                     enum ast_http_method method,
05722                     enum output_format format,
05723                     struct sockaddr_in *remote_address, const char *uri,
05724                     struct ast_variable *get_params,
05725                     struct ast_variable *headers)
05726 {
05727    struct mansession_session *session = NULL;
05728    struct mansession s = { .session = NULL, .tcptls_session = ser };
05729    struct ast_variable *v, *params = get_params;
05730    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
05731    struct ast_str *http_header = NULL, *out = NULL;
05732    size_t result_size = 512;
05733    struct message m = { 0 };
05734    unsigned int x;
05735    size_t hdrlen;
05736 
05737    time_t time_now = time(NULL);
05738    unsigned long nonce = 0, nc;
05739    struct ast_http_digest d = { NULL, };
05740    struct ast_manager_user *user = NULL;
05741    int stale = 0;
05742    char resp_hash[256]="";
05743    /* Cache for user data */
05744    char u_username[80];
05745    int u_readperm;
05746    int u_writeperm;
05747    int u_writetimeout;
05748    int u_displayconnects;
05749    struct ast_sockaddr addr;
05750 
05751    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
05752       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
05753       return -1;
05754    }
05755 
05756    /* Find "Authorization: " header */
05757    for (v = headers; v; v = v->next) {
05758       if (!strcasecmp(v->name, "Authorization")) {
05759          break;
05760       }
05761    }
05762 
05763    if (!v || ast_strlen_zero(v->value)) {
05764       goto out_401; /* Authorization Header not present - send auth request */
05765    }
05766 
05767    /* Digest found - parse */
05768    if (ast_string_field_init(&d, 128)) {
05769       ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
05770       return -1;
05771    }
05772 
05773    if (ast_parse_digest(v->value, &d, 0, 1)) {
05774       /* Error in Digest - send new one */
05775       nonce = 0;
05776       goto out_401;
05777    }
05778    if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
05779       ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
05780       nonce = 0;
05781       goto out_401;
05782    }
05783 
05784    AST_RWLIST_WRLOCK(&users);
05785    user = get_manager_by_name_locked(d.username);
05786    if(!user) {
05787       AST_RWLIST_UNLOCK(&users);
05788       ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
05789       nonce = 0;
05790       goto out_401;
05791    }
05792 
05793    ast_sockaddr_from_sin(&addr, remote_address);
05794    /* --- We have User for this auth, now check ACL */
05795    if (user->ha && !ast_apply_ha(user->ha, &addr)) {
05796       AST_RWLIST_UNLOCK(&users);
05797       ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
05798       ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
05799       return -1;
05800    }
05801 
05802    /* --- We have auth, so check it */
05803 
05804    /* compute the expected response to compare with what we received */
05805    {
05806       char a2[256];
05807       char a2_hash[256];
05808       char resp[256];
05809 
05810       /* XXX Now request method are hardcoded in A2 */
05811       snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri);
05812       ast_md5_hash(a2_hash, a2);
05813 
05814       if (d.qop) {
05815          /* RFC 2617 */
05816          snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
05817       }  else {
05818          /* RFC 2069 */
05819          snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
05820       }
05821       ast_md5_hash(resp_hash, resp);
05822    }
05823 
05824    if (!d.nonce  || strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
05825       /* Something was wrong, so give the client to try with a new challenge */
05826       AST_RWLIST_UNLOCK(&users);
05827       nonce = 0;
05828       goto out_401;
05829    }
05830 
05831    /*
05832     * User are pass Digest authentication.
05833     * Now, cache the user data and unlock user list.
05834     */
05835    ast_copy_string(u_username, user->username, sizeof(u_username));
05836    u_readperm = user->readperm;
05837    u_writeperm = user->writeperm;
05838    u_displayconnects = user->displayconnects;
05839    u_writetimeout = user->writetimeout;
05840    AST_RWLIST_UNLOCK(&users);
05841 
05842    if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
05843       /*
05844        * Create new session.
05845        * While it is not in the list we don't need any locking
05846        */
05847       if (!(session = build_mansession(*remote_address))) {
05848          ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
05849          return -1;
05850       }
05851       ao2_lock(session);
05852 
05853       ast_copy_string(session->username, u_username, sizeof(session->username));
05854       session->managerid = nonce;
05855       session->last_ev = grab_last();
05856       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
05857 
05858       session->readperm = u_readperm;
05859       session->writeperm = u_writeperm;
05860       session->writetimeout = u_writetimeout;
05861 
05862       if (u_displayconnects) {
05863          ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
05864       }
05865       session->noncetime = session->sessionstart = time_now;
05866       session->authenticated = 1;
05867    } else if (stale) {
05868       /*
05869        * Session found, but nonce is stale.
05870        *
05871        * This could be because an old request (w/old nonce) arrived.
05872        *
05873        * This may be as the result of http proxy usage (separate delay or
05874        * multipath) or in a situation where a page was refreshed too quickly
05875        * (seen in Firefox).
05876        *
05877        * In this situation, we repeat the 401 auth with the current nonce
05878        * value.
05879        */
05880       nonce = session->managerid;
05881       ao2_unlock(session);
05882       stale = 1;
05883       goto out_401;
05884    } else {
05885       sscanf(d.nc, "%30lx", &nc);
05886       if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
05887          /*
05888           * Nonce time expired (> 2 minutes) or something wrong with nonce
05889           * counter.
05890           *
05891           * Create new nonce key and resend Digest auth request. Old nonce
05892           * is saved for stale checking...
05893           */
05894          session->nc = 0; /* Reset nonce counter */
05895          session->oldnonce = session->managerid;
05896          nonce = session->managerid = ast_random();
05897          session->noncetime = time_now;
05898          ao2_unlock(session);
05899          stale = 1;
05900          goto out_401;
05901       } else {
05902          session->nc = nc; /* All OK, save nonce counter */
05903       }
05904    }
05905 
05906 
05907    /* Reset session timeout. */
05908    session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
05909    ao2_unlock(session);
05910 
05911    ast_mutex_init(&s.lock);
05912    s.session = session;
05913    s.fd = mkstemp(template);  /* create a temporary file for command output */
05914    unlink(template);
05915    if (s.fd <= -1) {
05916       ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
05917       goto auth_callback_out;
05918    }
05919    s.f = fdopen(s.fd, "w+");
05920    if (!s.f) {
05921       ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
05922       ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
05923       close(s.fd);
05924       goto auth_callback_out;
05925    }
05926 
05927    if (method == AST_HTTP_POST) {
05928       params = ast_http_get_post_vars(ser, headers);
05929    }
05930 
05931    for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
05932       hdrlen = strlen(v->name) + strlen(v->value) + 3;
05933       m.headers[m.hdrcount] = alloca(hdrlen);
05934       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
05935       ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
05936       m.hdrcount = x + 1;
05937    }
05938 
05939    if (process_message(&s, &m)) {
05940       if (u_displayconnects) {
05941          ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
05942       }
05943 
05944       session->needdestroy = 1;
05945    }
05946 
05947    if (s.f) {
05948       result_size = ftell(s.f); /* Calculate approx. size of result */
05949    }
05950 
05951    http_header = ast_str_create(80);
05952    out = ast_str_create(result_size * 2 + 512);
05953 
05954    if (http_header == NULL || out == NULL) {
05955       ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
05956       goto auth_callback_out;
05957    }
05958 
05959    ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);
05960 
05961    if (format == FORMAT_XML) {
05962       ast_str_append(&out, 0, "<ajax-response>\n");
05963    } else if (format == FORMAT_HTML) {
05964       ast_str_append(&out, 0,
05965       "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
05966       "<html><head>\r\n"
05967       "<title>Asterisk&trade; Manager Interface</title>\r\n"
05968       "</head><body style=\"background-color: #ffffff;\">\r\n"
05969       "<form method=\"POST\">\r\n"
05970       "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
05971       "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
05972       "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
05973       "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
05974    }
05975 
05976    process_output(&s, &out, params, format);
05977 
05978    if (format == FORMAT_XML) {
05979       ast_str_append(&out, 0, "</ajax-response>\n");
05980    } else if (format == FORMAT_HTML) {
05981       ast_str_append(&out, 0, "</table></form></body></html>\r\n");
05982    }
05983 
05984    ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
05985    http_header = out = NULL;
05986 
05987 auth_callback_out:
05988    ast_mutex_destroy(&s.lock);
05989 
05990    /* Clear resources and unlock manager session */
05991    if (method == AST_HTTP_POST && params) {
05992       ast_variables_destroy(params);
05993    }
05994 
05995    ast_free(http_header);
05996    ast_free(out);
05997 
05998    ao2_lock(session);
05999    if (session->f) {
06000       fclose(session->f);
06001    }
06002    session->f = NULL;
06003    session->fd = -1;
06004    ao2_unlock(session);
06005 
06006    if (session->needdestroy) {
06007       ast_debug(1, "Need destroy, doing it now!\n");
06008       session_destroy(session);
06009    }
06010    ast_string_field_free_memory(&d);
06011    return 0;
06012 
06013 out_401:
06014    if (!nonce) {
06015       nonce = ast_random();
06016    }
06017 
06018    ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
06019    ast_string_field_free_memory(&d);
06020    return 0;
06021 }
06022 
06023 static int manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params,  struct ast_variable *headers)
06024 {
06025    int retval;
06026    struct sockaddr_in ser_remote_address_tmp;
06027 
06028    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06029    retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
06030    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06031    return retval;
06032 }
06033 
06034 static int mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
06035 {
06036    int retval;
06037    struct sockaddr_in ser_remote_address_tmp;
06038 
06039    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06040    retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
06041    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06042    return retval;
06043 }
06044 
06045 static int rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
06046 {
06047    int retval;
06048    struct sockaddr_in ser_remote_address_tmp;
06049 
06050    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06051    retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
06052    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06053    return retval;
06054 }
06055 
06056 static struct ast_http_uri rawmanuri = {
06057    .description = "Raw HTTP Manager Event Interface",
06058    .uri = "rawman",
06059    .callback = rawman_http_callback,
06060    .data = NULL,
06061    .key = __FILE__,
06062 };
06063 
06064 static struct ast_http_uri manageruri = {
06065    .description = "HTML Manager Event Interface",
06066    .uri = "manager",
06067    .callback = manager_http_callback,
06068    .data = NULL,
06069    .key = __FILE__,
06070 };
06071 
06072 static struct ast_http_uri managerxmluri = {
06073    .description = "XML Manager Event Interface",
06074    .uri = "mxml",
06075    .callback = mxml_http_callback,
06076    .data = NULL,
06077    .key = __FILE__,
06078 };
06079 
06080 
06081 /* Callback with Digest authentication */
06082 static int auth_manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params,  struct ast_variable *headers)
06083 {
06084    int retval;
06085    struct sockaddr_in ser_remote_address_tmp;
06086 
06087    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06088    retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
06089    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06090    return retval;
06091 }
06092 
06093 static int auth_mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
06094 {
06095    int retval;
06096    struct sockaddr_in ser_remote_address_tmp;
06097 
06098    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06099    retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
06100    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06101    return retval;
06102 }
06103 
06104 static int auth_rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
06105 {
06106    int retval;
06107    struct sockaddr_in ser_remote_address_tmp;
06108 
06109    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06110    retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
06111    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06112    return retval;
06113 }
06114 
06115 static struct ast_http_uri arawmanuri = {
06116    .description = "Raw HTTP Manager Event Interface w/Digest authentication",
06117    .uri = "arawman",
06118    .has_subtree = 0,
06119    .callback = auth_rawman_http_callback,
06120    .data = NULL,
06121    .key = __FILE__,
06122 };
06123 
06124 static struct ast_http_uri amanageruri = {
06125    .description = "HTML Manager Event Interface w/Digest authentication",
06126    .uri = "amanager",
06127    .has_subtree = 0,
06128    .callback = auth_manager_http_callback,
06129    .data = NULL,
06130    .key = __FILE__,
06131 };
06132 
06133 static struct ast_http_uri amanagerxmluri = {
06134    .description = "XML Manager Event Interface w/Digest authentication",
06135    .uri = "amxml",
06136    .has_subtree = 0,
06137    .callback = auth_mxml_http_callback,
06138    .data = NULL,
06139    .key = __FILE__,
06140 };
06141 
06142 static int registered = 0;
06143 static int webregged = 0;
06144 
06145 /*! \brief cleanup code called at each iteration of server_root,
06146  * guaranteed to happen every 5 seconds at most
06147  */
06148 static void purge_old_stuff(void *data)
06149 {
06150    purge_sessions(1);
06151    purge_events();
06152 }
06153 
06154 static struct ast_tls_config ami_tls_cfg;
06155 static struct ast_tcptls_session_args ami_desc = {
06156    .accept_fd = -1,
06157    .master = AST_PTHREADT_NULL,
06158    .tls_cfg = NULL,
06159    .poll_timeout = 5000,   /* wake up every 5 seconds */
06160    .periodic_fn = purge_old_stuff,
06161    .name = "AMI server",
06162    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
06163    .worker_fn = session_do,   /* thread handling the session */
06164 };
06165 
06166 static struct ast_tcptls_session_args amis_desc = {
06167    .accept_fd = -1,
06168    .master = AST_PTHREADT_NULL,
06169    .tls_cfg = &ami_tls_cfg,
06170    .poll_timeout = -1,  /* the other does the periodic cleanup */
06171    .name = "AMI TLS server",
06172    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
06173    .worker_fn = session_do,   /* thread handling the session */
06174 };
06175 
06176 /*! \brief CLI command manager show settings */
06177 static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06178 {
06179    switch (cmd) {
06180    case CLI_INIT:
06181       e->command = "manager show settings";
06182       e->usage =
06183          "Usage: manager show settings\n"
06184          "       Provides detailed list of the configuration of the Manager.\n";
06185       return NULL;
06186    case CLI_GENERATE:
06187       return NULL;
06188    }
06189 #define FORMAT "  %-25.25s  %-15.15s\n"
06190 #define FORMAT2 "  %-25.25s  %-15d\n"
06191    if (a->argc != 3) {
06192       return CLI_SHOWUSAGE;
06193    }
06194    ast_cli(a->fd, "\nGlobal Settings:\n");
06195    ast_cli(a->fd, "----------------\n");
06196    ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
06197    ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
06198    ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
06199    ast_cli(a->fd, FORMAT2, "HTTP Timeout (minutes):", httptimeout);
06200    ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
06201    ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
06202    ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
06203    ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
06204    ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
06205    ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
06206    ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
06207    ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
06208    ast_cli(a->fd, FORMAT, "Channel vars:", S_OR(manager_channelvars, ""));
06209    ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
06210    ast_cli(a->fd, FORMAT, "Block sockets:", AST_CLI_YESNO(block_sockets));
06211 #undef FORMAT
06212 #undef FORMAT2
06213 
06214    return CLI_SUCCESS;
06215 }
06216 
06217 static struct ast_cli_entry cli_manager[] = {
06218    AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
06219    AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
06220    AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
06221    AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
06222    AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
06223    AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
06224    AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
06225    AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
06226    AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
06227 };
06228 
06229 static int __init_manager(int reload)
06230 {
06231    struct ast_config *ucfg = NULL, *cfg = NULL;
06232    const char *val;
06233    char *cat = NULL;
06234    int newhttptimeout = DEFAULT_HTTPTIMEOUT;
06235    struct ast_manager_user *user = NULL;
06236    struct ast_variable *var;
06237    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06238    char a1[256];
06239    char a1_hash[256];
06240    struct sockaddr_in ami_desc_local_address_tmp = { 0, };
06241    struct sockaddr_in amis_desc_local_address_tmp = { 0, };
06242 
06243    if (!registered) {
06244       /* Register default actions */
06245       ast_manager_register_xml("Ping", 0, action_ping);
06246       ast_manager_register_xml("Events", 0, action_events);
06247       ast_manager_register_xml("Logoff", 0, action_logoff);
06248       ast_manager_register_xml("Login", 0, action_login);
06249       ast_manager_register_xml("Challenge", 0, action_challenge);
06250       ast_manager_register_xml("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup);
06251       ast_manager_register_xml("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status);
06252       ast_manager_register_xml("Setvar", EVENT_FLAG_CALL, action_setvar);
06253       ast_manager_register_xml("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar);
06254       ast_manager_register_xml("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig);
06255       ast_manager_register_xml("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson);
06256       ast_manager_register_xml("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig);
06257       ast_manager_register_xml("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig);
06258       ast_manager_register_xml("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
06259       ast_manager_register_xml("Redirect", EVENT_FLAG_CALL, action_redirect);
06260       ast_manager_register_xml("Atxfer", EVENT_FLAG_CALL, action_atxfer);
06261       ast_manager_register_xml("Originate", EVENT_FLAG_ORIGINATE, action_originate);
06262       ast_manager_register_xml("Command", EVENT_FLAG_COMMAND, action_command);
06263       ast_manager_register_xml("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
06264       ast_manager_register_xml("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
06265       ast_manager_register_xml("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
06266       ast_manager_register_xml("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
06267       ast_manager_register_xml("ListCommands", 0, action_listcommands);
06268       ast_manager_register_xml("SendText", EVENT_FLAG_CALL, action_sendtext);
06269       ast_manager_register_xml("UserEvent", EVENT_FLAG_USER, action_userevent);
06270       ast_manager_register_xml("WaitEvent", 0, action_waitevent);
06271       ast_manager_register_xml("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings);
06272       ast_manager_register_xml("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus);
06273       ast_manager_register_xml("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload);
06274       ast_manager_register_xml("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
06275       ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
06276       ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
06277       ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
06278 
06279       ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
06280       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
06281       registered = 1;
06282       /* Append placeholder event so master_eventq never runs dry */
06283       append_event("Event: Placeholder\r\n\r\n", 0);
06284    }
06285    if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
06286       return 0;
06287    }
06288 
06289    manager_enabled = DEFAULT_ENABLED;
06290    webmanager_enabled = DEFAULT_WEBENABLED;
06291    displayconnects = DEFAULT_DISPLAYCONNECTS;
06292    broken_events_action = DEFAULT_BROKENEVENTSACTION;
06293    block_sockets = DEFAULT_BLOCKSOCKETS;
06294    timestampevents = DEFAULT_TIMESTAMPEVENTS;
06295    httptimeout = DEFAULT_HTTPTIMEOUT;
06296    authtimeout = DEFAULT_AUTHTIMEOUT;
06297    authlimit = DEFAULT_AUTHLIMIT;
06298 
06299    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
06300       ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
06301       return 0;
06302    }
06303 
06304    /* default values */
06305    ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
06306    memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
06307    memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
06308    amis_desc_local_address_tmp.sin_port = htons(5039);
06309    ami_desc_local_address_tmp.sin_port = htons(DEFAULT_MANAGER_PORT);
06310 
06311    ami_tls_cfg.enabled = 0;
06312    if (ami_tls_cfg.certfile) {
06313       ast_free(ami_tls_cfg.certfile);
06314    }
06315    ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
06316    if (ami_tls_cfg.pvtfile) {
06317       ast_free(ami_tls_cfg.pvtfile);
06318    }
06319    ami_tls_cfg.pvtfile = ast_strdup("");
06320    if (ami_tls_cfg.cipher) {
06321       ast_free(ami_tls_cfg.cipher);
06322    }
06323    ami_tls_cfg.cipher = ast_strdup("");
06324 
06325    free_channelvars();
06326 
06327    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
06328       val = var->value;
06329 
06330       if (!ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
06331          continue;
06332       }
06333 
06334       if (!strcasecmp(var->name, "enabled")) {
06335          manager_enabled = ast_true(val);
06336       } else if (!strcasecmp(var->name, "block-sockets")) {
06337          block_sockets = ast_true(val);
06338       } else if (!strcasecmp(var->name, "webenabled")) {
06339          webmanager_enabled = ast_true(val);
06340       } else if (!strcasecmp(var->name, "port")) {
06341          ami_desc_local_address_tmp.sin_port = htons(atoi(val));
06342       } else if (!strcasecmp(var->name, "bindaddr")) {
06343          if (!inet_aton(val, &ami_desc_local_address_tmp.sin_addr)) {
06344             ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
06345             memset(&ami_desc_local_address_tmp.sin_addr, 0,
06346                    sizeof(ami_desc_local_address_tmp.sin_addr));
06347          }
06348       } else if (!strcasecmp(var->name, "brokeneventsaction")) {
06349          broken_events_action = ast_true(val);
06350       } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
06351          allowmultiplelogin = ast_true(val);
06352       } else if (!strcasecmp(var->name, "displayconnects")) {
06353          displayconnects = ast_true(val);
06354       } else if (!strcasecmp(var->name, "timestampevents")) {
06355          timestampevents = ast_true(val);
06356       } else if (!strcasecmp(var->name, "debug")) {
06357          manager_debug = ast_true(val);
06358       } else if (!strcasecmp(var->name, "httptimeout")) {
06359          newhttptimeout = atoi(val);
06360       } else if (!strcasecmp(var->name, "authtimeout")) {
06361          int timeout = atoi(var->value);
06362 
06363          if (timeout < 1) {
06364             ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
06365          } else {
06366             authtimeout = timeout;
06367          }
06368       } else if (!strcasecmp(var->name, "authlimit")) {
06369          int limit = atoi(var->value);
06370 
06371          if (limit < 1) {
06372             ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
06373          } else {
06374             authlimit = limit;
06375          }
06376       } else if (!strcasecmp(var->name, "channelvars")) {
06377          struct manager_channel_variable *mcv;
06378          char *remaining = ast_strdupa(val), *next;
06379          ast_free(manager_channelvars);
06380          manager_channelvars = ast_strdup(val);
06381          AST_RWLIST_WRLOCK(&channelvars);
06382          while ((next = strsep(&remaining, ",|"))) {
06383             if (!(mcv = ast_calloc(1, sizeof(*mcv) + strlen(next) + 1))) {
06384                break;
06385             }
06386             strcpy(mcv->name, next); /* SAFE */
06387             if (strchr(next, '(')) {
06388                mcv->isfunc = 1;
06389             }
06390             AST_RWLIST_INSERT_TAIL(&channelvars, mcv, entry);
06391          }
06392          AST_RWLIST_UNLOCK(&channelvars);
06393       } else {
06394          ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
06395             var->name, val);
06396       }
06397    }
06398 
06399    ami_desc_local_address_tmp.sin_family = AF_INET;
06400    amis_desc_local_address_tmp.sin_family = AF_INET;
06401 
06402    /* if the amis address has not been set, default is the same as non secure ami */
06403    if (!amis_desc_local_address_tmp.sin_addr.s_addr) {
06404       amis_desc_local_address_tmp.sin_addr =
06405           ami_desc_local_address_tmp.sin_addr;
06406    }
06407 
06408    if (manager_enabled) {
06409       ast_sockaddr_from_sin(&ami_desc.local_address, &ami_desc_local_address_tmp);
06410       ast_sockaddr_from_sin(&amis_desc.local_address, &amis_desc_local_address_tmp);
06411    }
06412 
06413    AST_RWLIST_WRLOCK(&users);
06414 
06415    /* First, get users from users.conf */
06416    ucfg = ast_config_load2("users.conf", "manager", config_flags);
06417    if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
06418       const char *hasmanager;
06419       int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
06420 
06421       while ((cat = ast_category_browse(ucfg, cat))) {
06422          if (!strcasecmp(cat, "general")) {
06423             continue;
06424          }
06425 
06426          hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
06427          if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
06428             const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
06429             const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
06430             const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
06431             const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
06432             const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
06433 
06434             /* Look for an existing entry,
06435              * if none found - create one and add it to the list
06436              */
06437             if (!(user = get_manager_by_name_locked(cat))) {
06438                if (!(user = ast_calloc(1, sizeof(*user)))) {
06439                   break;
06440                }
06441 
06442                /* Copy name over */
06443                ast_copy_string(user->username, cat, sizeof(user->username));
06444                /* Insert into list */
06445                AST_LIST_INSERT_TAIL(&users, user, list);
06446                user->ha = NULL;
06447                user->keep = 1;
06448                user->readperm = -1;
06449                user->writeperm = -1;
06450                /* Default displayconnect from [general] */
06451                user->displayconnects = displayconnects;
06452                user->writetimeout = 100;
06453             }
06454 
06455             if (!user_secret) {
06456                user_secret = ast_variable_retrieve(ucfg, "general", "secret");
06457             }
06458             if (!user_read) {
06459                user_read = ast_variable_retrieve(ucfg, "general", "read");
06460             }
06461             if (!user_write) {
06462                user_write = ast_variable_retrieve(ucfg, "general", "write");
06463             }
06464             if (!user_displayconnects) {
06465                user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
06466             }
06467             if (!user_writetimeout) {
06468                user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
06469             }
06470 
06471             if (!ast_strlen_zero(user_secret)) {
06472                if (user->secret) {
06473                   ast_free(user->secret);
06474                }
06475                user->secret = ast_strdup(user_secret);
06476             }
06477 
06478             if (user_read) {
06479                user->readperm = get_perm(user_read);
06480             }
06481             if (user_write) {
06482                user->writeperm = get_perm(user_write);
06483             }
06484             if (user_displayconnects) {
06485                user->displayconnects = ast_true(user_displayconnects);
06486             }
06487             if (user_writetimeout) {
06488                int value = atoi(user_writetimeout);
06489                if (value < 100) {
06490                   ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
06491                } else {
06492                   user->writetimeout = value;
06493                }
06494             }
06495          }
06496       }
06497       ast_config_destroy(ucfg);
06498    }
06499 
06500    /* cat is NULL here in any case */
06501 
06502    while ((cat = ast_category_browse(cfg, cat))) {
06503       struct ast_ha *oldha;
06504 
06505       if (!strcasecmp(cat, "general")) {
06506          continue;
06507       }
06508 
06509       /* Look for an existing entry, if none found - create one and add it to the list */
06510       if (!(user = get_manager_by_name_locked(cat))) {
06511          if (!(user = ast_calloc(1, sizeof(*user)))) {
06512             break;
06513          }
06514          /* Copy name over */
06515          ast_copy_string(user->username, cat, sizeof(user->username));
06516 
06517          user->ha = NULL;
06518          user->readperm = 0;
06519          user->writeperm = 0;
06520          /* Default displayconnect from [general] */
06521          user->displayconnects = displayconnects;
06522          user->writetimeout = 100;
06523          user->whitefilters = ao2_container_alloc(1, NULL, NULL);
06524          user->blackfilters = ao2_container_alloc(1, NULL, NULL);
06525 
06526          /* Insert into list */
06527          AST_RWLIST_INSERT_TAIL(&users, user, list);
06528       } else {
06529          ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
06530          ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
06531       }
06532 
06533       /* Make sure we keep this user and don't destroy it during cleanup */
06534       user->keep = 1;
06535       oldha = user->ha;
06536       user->ha = NULL;
06537 
06538       var = ast_variable_browse(cfg, cat);
06539       for (; var; var = var->next) {
06540          if (!strcasecmp(var->name, "secret")) {
06541             if (user->secret) {
06542                ast_free(user->secret);
06543             }
06544             user->secret = ast_strdup(var->value);
06545          } else if (!strcasecmp(var->name, "deny") ||
06546                    !strcasecmp(var->name, "permit")) {
06547             user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
06548          }  else if (!strcasecmp(var->name, "read") ) {
06549             user->readperm = get_perm(var->value);
06550          }  else if (!strcasecmp(var->name, "write") ) {
06551             user->writeperm = get_perm(var->value);
06552          }  else if (!strcasecmp(var->name, "displayconnects") ) {
06553             user->displayconnects = ast_true(var->value);
06554          } else if (!strcasecmp(var->name, "writetimeout")) {
06555             int value = atoi(var->value);
06556             if (value < 100) {
06557                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
06558             } else {
06559                user->writetimeout = value;
06560             }
06561          } else if (!strcasecmp(var->name, "eventfilter")) {
06562             const char *value = var->value;
06563             regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
06564             if (new_filter) {
06565                int is_blackfilter;
06566                if (value[0] == '!') {
06567                   is_blackfilter = 1;
06568                   value++;
06569                } else {
06570                   is_blackfilter = 0;
06571                }
06572                if (regcomp(new_filter, value, 0)) {
06573                   ao2_t_ref(new_filter, -1, "failed to make regx");
06574                } else {
06575                   if (is_blackfilter) {
06576                      ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
06577                   } else {
06578                      ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
06579                   }
06580                }
06581             }
06582          } else {
06583             ast_debug(1, "%s is an unknown option.\n", var->name);
06584          }
06585       }
06586       ast_free_ha(oldha);
06587    }
06588    ast_config_destroy(cfg);
06589 
06590    /* Perform cleanup - essentially prune out old users that no longer exist */
06591    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
06592       if (user->keep) { /* valid record. clear flag for the next round */
06593          user->keep = 0;
06594 
06595          /* Calculate A1 for Digest auth */
06596          snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
06597          ast_md5_hash(a1_hash,a1);
06598          if (user->a1_hash) {
06599             ast_free(user->a1_hash);
06600          }
06601          user->a1_hash = ast_strdup(a1_hash);
06602          continue;
06603       }
06604       /* We do not need to keep this user so take them out of the list */
06605       AST_RWLIST_REMOVE_CURRENT(list);
06606       ast_debug(4, "Pruning user '%s'\n", user->username);
06607       /* Free their memory now */
06608       if (user->a1_hash) {
06609          ast_free(user->a1_hash);
06610       }
06611       if (user->secret) {
06612          ast_free(user->secret);
06613       }
06614       ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
06615       ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
06616       ao2_t_ref(user->whitefilters, -1, "decrement ref for white container, should be last one");
06617       ao2_t_ref(user->blackfilters, -1, "decrement ref for black container, should be last one");
06618       ast_free_ha(user->ha);
06619       ast_free(user);
06620    }
06621    AST_RWLIST_TRAVERSE_SAFE_END;
06622 
06623    AST_RWLIST_UNLOCK(&users);
06624 
06625    if (!reload) {
06626       /* If you have a NULL hash fn, you only need a single bucket */
06627       sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn);
06628    }
06629 
06630    if (webmanager_enabled && manager_enabled) {
06631       if (!webregged) {
06632 
06633          ast_http_uri_link(&rawmanuri);
06634          ast_http_uri_link(&manageruri);
06635          ast_http_uri_link(&managerxmluri);
06636 
06637          ast_http_uri_link(&arawmanuri);
06638          ast_http_uri_link(&amanageruri);
06639          ast_http_uri_link(&amanagerxmluri);
06640          webregged = 1;
06641       }
06642    } else {
06643       if (webregged) {
06644          ast_http_uri_unlink(&rawmanuri);
06645          ast_http_uri_unlink(&manageruri);
06646          ast_http_uri_unlink(&managerxmluri);
06647 
06648          ast_http_uri_unlink(&arawmanuri);
06649          ast_http_uri_unlink(&amanageruri);
06650          ast_http_uri_unlink(&amanagerxmluri);
06651          webregged = 0;
06652       }
06653    }
06654 
06655    if (newhttptimeout > 0) {
06656       httptimeout = newhttptimeout;
06657    }
06658 
06659    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
06660 
06661    ast_tcptls_server_start(&ami_desc);
06662    if (ast_ssl_setup(amis_desc.tls_cfg)) {
06663       ast_tcptls_server_start(&amis_desc);
06664    }
06665    return 0;
06666 }
06667 
06668 /* clear out every entry in the channelvar list */
06669 static void free_channelvars(void)
06670 {
06671    struct manager_channel_variable *var;
06672    AST_RWLIST_WRLOCK(&channelvars);
06673    while ((var = AST_RWLIST_REMOVE_HEAD(&channelvars, entry))) {
06674       ast_free(var);
06675    }
06676    AST_RWLIST_UNLOCK(&channelvars);
06677 }
06678 
06679 int init_manager(void)
06680 {
06681    return __init_manager(0);
06682 }
06683 
06684 int reload_manager(void)
06685 {
06686    return __init_manager(1);
06687 }
06688 
06689 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
06690 {
06691    AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
06692 
06693    return 0;
06694 }
06695 
06696 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
06697 {
06698    return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
06699 }
06700 
06701 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
06702 {
06703    struct ast_datastore *datastore = NULL;
06704 
06705    if (info == NULL)
06706       return NULL;
06707 
06708    AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
06709       if (datastore->info != info) {
06710          continue;
06711       }
06712 
06713       if (uid == NULL) {
06714          /* matched by type only */
06715          break;
06716       }
06717 
06718       if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
06719          /* Matched by type AND uid */
06720          break;
06721       }
06722    }
06723    AST_LIST_TRAVERSE_SAFE_END;
06724 
06725    return datastore;
06726 }