Wed Apr 6 11:29:45 2011

Asterisk developer's documentation


manager.c

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

Generated on Wed Apr 6 11:29:46 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7