Mon Aug 31 12:30:09 2015

Asterisk developer's documentation


manager.c

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

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1