Sat Apr 26 2014 22:01:38

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