Mon Jun 27 16:50:54 2011

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

Generated on Mon Jun 27 16:50:54 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7