Mon Jun 18 2012 03:55:36

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