Sat Jul 12 2014 17:18:34

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