Fri Apr 29 2011 07:54:26

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