Mon Oct 8 12:39:03 2012

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: 372845 $")
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 = NULL, *final_buf = NULL;
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    if ((fd = mkstemp(template)) < 0) {
03636       ast_log(AST_LOG_WARNING, "Failed to create temporary file for command: %s\n", strerror(errno));
03637       astman_send_error(s, m, "Command response construction error");
03638       return 0;
03639    }
03640 
03641    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
03642    if (!ast_strlen_zero(id)) {
03643       astman_append(s, "ActionID: %s\r\n", id);
03644    }
03645    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
03646    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
03647    /* Determine number of characters available */
03648    if ((l = lseek(fd, 0, SEEK_END)) < 0) {
03649       ast_log(LOG_WARNING, "Failed to determine number of characters for command: %s\n", strerror(errno));
03650       goto action_command_cleanup;
03651    }
03652 
03653    /* This has a potential to overflow the stack.  Hence, use the heap. */
03654    buf = ast_malloc(l + 1);
03655    final_buf = ast_malloc(l + 1);
03656 
03657    if (!buf || !final_buf) {
03658       ast_log(LOG_WARNING, "Failed to allocate memory for temporary buffer\n");
03659       goto action_command_cleanup;
03660    }
03661 
03662    if (lseek(fd, 0, SEEK_SET) < 0) {
03663       ast_log(LOG_WARNING, "Failed to set position on temporary file for command: %s\n", strerror(errno));
03664       goto action_command_cleanup;
03665    }
03666 
03667    if (read(fd, buf, l) < 0) {
03668       ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
03669       goto action_command_cleanup;
03670    }
03671 
03672    buf[l] = '\0';
03673    term_strip(final_buf, buf, l);
03674    final_buf[l] = '\0';
03675    astman_append(s, "%s", final_buf);
03676 
03677 action_command_cleanup:
03678 
03679    close(fd);
03680    unlink(template);
03681    astman_append(s, "--END COMMAND--\r\n\r\n");
03682 
03683    ast_free(buf);
03684    ast_free(final_buf);
03685 
03686    return 0;
03687 }
03688 
03689 /*! \brief helper function for originate */
03690 struct fast_originate_helper {
03691    int timeout;
03692    format_t format;           /*!< Codecs used for a call */
03693    AST_DECLARE_STRING_FIELDS (
03694       AST_STRING_FIELD(tech);
03695       /*! data can contain a channel name, extension number, username, password, etc. */
03696       AST_STRING_FIELD(data);
03697       AST_STRING_FIELD(app);
03698       AST_STRING_FIELD(appdata);
03699       AST_STRING_FIELD(cid_name);
03700       AST_STRING_FIELD(cid_num);
03701       AST_STRING_FIELD(context);
03702       AST_STRING_FIELD(exten);
03703       AST_STRING_FIELD(idtext);
03704       AST_STRING_FIELD(account);
03705    );
03706    int priority;
03707    struct ast_variable *vars;
03708 };
03709 
03710 /*!
03711  * \internal
03712  *
03713  * \param doomed Struct to destroy.
03714  *
03715  * \return Nothing
03716  */
03717 static void destroy_fast_originate_helper(struct fast_originate_helper *doomed)
03718 {
03719    ast_variables_destroy(doomed->vars);
03720    ast_string_field_free_memory(doomed);
03721    ast_free(doomed);
03722 }
03723 
03724 static void *fast_originate(void *data)
03725 {
03726    struct fast_originate_helper *in = data;
03727    int res;
03728    int reason = 0;
03729    struct ast_channel *chan = NULL, *chans[1];
03730    char requested_channel[AST_CHANNEL_NAME];
03731 
03732    if (!ast_strlen_zero(in->app)) {
03733       res = ast_pbx_outgoing_app(in->tech, in->format, (char *) in->data,
03734          in->timeout, in->app, in->appdata, &reason, 1,
03735          S_OR(in->cid_num, NULL),
03736          S_OR(in->cid_name, NULL),
03737          in->vars, in->account, &chan);
03738    } else {
03739       res = ast_pbx_outgoing_exten(in->tech, in->format, (char *) in->data,
03740          in->timeout, in->context, in->exten, in->priority, &reason, 1,
03741          S_OR(in->cid_num, NULL),
03742          S_OR(in->cid_name, NULL),
03743          in->vars, in->account, &chan);
03744    }
03745    /* Any vars memory was passed to the ast_pbx_outgoing_xxx() calls. */
03746    in->vars = NULL;
03747 
03748    if (!chan) {
03749       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
03750    }
03751    /* Tell the manager what happened with the channel */
03752    chans[0] = chan;
03753    ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
03754       "%s"
03755       "Response: %s\r\n"
03756       "Channel: %s\r\n"
03757       "Context: %s\r\n"
03758       "Exten: %s\r\n"
03759       "Reason: %d\r\n"
03760       "Uniqueid: %s\r\n"
03761       "CallerIDNum: %s\r\n"
03762       "CallerIDName: %s\r\n",
03763       in->idtext, res ? "Failure" : "Success",
03764       chan ? chan->name : requested_channel, in->context, in->exten, reason,
03765       chan ? chan->uniqueid : "<null>",
03766       S_OR(in->cid_num, "<unknown>"),
03767       S_OR(in->cid_name, "<unknown>")
03768       );
03769 
03770    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
03771    if (chan) {
03772       ast_channel_unlock(chan);
03773    }
03774    destroy_fast_originate_helper(in);
03775    return NULL;
03776 }
03777 
03778 static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
03779 {
03780    const char *unitamount;
03781    const char *unittype;
03782    struct ast_str *str = ast_str_alloca(32);
03783 
03784    memset(entry, 0, sizeof(*entry));
03785 
03786    ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
03787    unitamount = astman_get_header(m, ast_str_buffer(str));
03788 
03789    ast_str_set(&str, 0, "UnitType(%u)", entry_num);
03790    unittype = astman_get_header(m, ast_str_buffer(str));
03791 
03792    if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
03793       entry->valid_amount = 1;
03794    }
03795 
03796    if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
03797       entry->valid_type = 1;
03798    }
03799 
03800    return 0;
03801 }
03802 
03803 static int action_aocmessage(struct mansession *s, const struct message *m)
03804 {
03805    const char *channel = astman_get_header(m, "Channel");
03806    const char *pchannel = astman_get_header(m, "ChannelPrefix");
03807    const char *msgtype = astman_get_header(m, "MsgType");
03808    const char *chargetype = astman_get_header(m, "ChargeType");
03809    const char *currencyname = astman_get_header(m, "CurrencyName");
03810    const char *currencyamount = astman_get_header(m, "CurrencyAmount");
03811    const char *mult = astman_get_header(m, "CurrencyMultiplier");
03812    const char *totaltype = astman_get_header(m, "TotalType");
03813    const char *aocbillingid = astman_get_header(m, "AOCBillingId");
03814    const char *association_id= astman_get_header(m, "ChargingAssociationId");
03815    const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
03816    const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
03817 
03818    enum ast_aoc_type _msgtype;
03819    enum ast_aoc_charge_type _chargetype;
03820    enum ast_aoc_currency_multiplier _mult = AST_AOC_MULT_ONE;
03821    enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
03822    enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
03823    unsigned int _currencyamount = 0;
03824    int _association_id = 0;
03825    unsigned int _association_plan = 0;
03826    struct ast_channel *chan = NULL;
03827 
03828    struct ast_aoc_decoded *decoded = NULL;
03829    struct ast_aoc_encoded *encoded = NULL;
03830    size_t encoded_size = 0;
03831 
03832    if (ast_strlen_zero(channel) && ast_strlen_zero(pchannel)) {
03833       astman_send_error(s, m, "Channel and PartialChannel are not specified. Specify at least one of these.");
03834       goto aocmessage_cleanup;
03835    }
03836 
03837    if (!(chan = ast_channel_get_by_name(channel)) && !ast_strlen_zero(pchannel)) {
03838       chan = ast_channel_get_by_name_prefix(pchannel, strlen(pchannel));
03839    }
03840 
03841    if (!chan) {
03842       astman_send_error(s, m, "No such channel");
03843       goto aocmessage_cleanup;
03844    }
03845 
03846    if (ast_strlen_zero(msgtype) || (strcasecmp(msgtype, "d") && strcasecmp(msgtype, "e"))) {
03847       astman_send_error(s, m, "Invalid MsgType");
03848       goto aocmessage_cleanup;
03849    }
03850 
03851    if (ast_strlen_zero(chargetype)) {
03852       astman_send_error(s, m, "ChargeType not specified");
03853       goto aocmessage_cleanup;
03854    }
03855 
03856    _msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
03857 
03858    if (!strcasecmp(chargetype, "NA")) {
03859       _chargetype = AST_AOC_CHARGE_NA;
03860    } else if (!strcasecmp(chargetype, "Free")) {
03861       _chargetype = AST_AOC_CHARGE_FREE;
03862    } else if (!strcasecmp(chargetype, "Currency")) {
03863       _chargetype = AST_AOC_CHARGE_CURRENCY;
03864    } else if (!strcasecmp(chargetype, "Unit")) {
03865       _chargetype = AST_AOC_CHARGE_UNIT;
03866    } else {
03867       astman_send_error(s, m, "Invalid ChargeType");
03868       goto aocmessage_cleanup;
03869    }
03870 
03871    if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
03872 
03873       if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
03874          astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
03875          goto aocmessage_cleanup;
03876       }
03877 
03878       if (ast_strlen_zero(mult)) {
03879          astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
03880          goto aocmessage_cleanup;
03881       } else if (!strcasecmp(mult, "onethousandth")) {
03882          _mult = AST_AOC_MULT_ONETHOUSANDTH;
03883       } else if (!strcasecmp(mult, "onehundredth")) {
03884          _mult = AST_AOC_MULT_ONEHUNDREDTH;
03885       } else if (!strcasecmp(mult, "onetenth")) {
03886          _mult = AST_AOC_MULT_ONETENTH;
03887       } else if (!strcasecmp(mult, "one")) {
03888          _mult = AST_AOC_MULT_ONE;
03889       } else if (!strcasecmp(mult, "ten")) {
03890          _mult = AST_AOC_MULT_TEN;
03891       } else if (!strcasecmp(mult, "hundred")) {
03892          _mult = AST_AOC_MULT_HUNDRED;
03893       } else if (!strcasecmp(mult, "thousand")) {
03894          _mult = AST_AOC_MULT_THOUSAND;
03895       } else {
03896          astman_send_error(s, m, "Invalid ChargeMultiplier");
03897          goto aocmessage_cleanup;
03898       }
03899    }
03900 
03901    /* create decoded object and start setting values */
03902    if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
03903          astman_send_error(s, m, "Message Creation Failed");
03904          goto aocmessage_cleanup;
03905    }
03906 
03907    if (_msgtype == AST_AOC_D) {
03908       if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
03909          _totaltype = AST_AOC_SUBTOTAL;
03910       }
03911 
03912       if (ast_strlen_zero(aocbillingid)) {
03913          /* ignore this is optional */
03914       } else if (!strcasecmp(aocbillingid, "Normal")) {
03915          _billingid = AST_AOC_BILLING_NORMAL;
03916       } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
03917          _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
03918       } else if (!strcasecmp(aocbillingid, "CreditCard")) {
03919          _billingid = AST_AOC_BILLING_CREDIT_CARD;
03920       } else {
03921          astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
03922          goto aocmessage_cleanup;
03923       }
03924    } else {
03925       if (ast_strlen_zero(aocbillingid)) {
03926          /* ignore this is optional */
03927       } else if (!strcasecmp(aocbillingid, "Normal")) {
03928          _billingid = AST_AOC_BILLING_NORMAL;
03929       } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
03930          _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
03931       } else if (!strcasecmp(aocbillingid, "CreditCard")) {
03932          _billingid = AST_AOC_BILLING_CREDIT_CARD;
03933       } else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
03934          _billingid = AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL;
03935       } else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
03936          _billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
03937       } else if (!strcasecmp(aocbillingid, "CallFwdNoReply")) {
03938          _billingid = AST_AOC_BILLING_CALL_FWD_NO_REPLY;
03939       } else if (!strcasecmp(aocbillingid, "CallDeflection")) {
03940          _billingid = AST_AOC_BILLING_CALL_DEFLECTION;
03941       } else if (!strcasecmp(aocbillingid, "CallTransfer")) {
03942          _billingid = AST_AOC_BILLING_CALL_TRANSFER;
03943       } else {
03944          astman_send_error(s, m, "Invalid AOC-E AOCBillingId");
03945          goto aocmessage_cleanup;
03946       }
03947 
03948       if (!ast_strlen_zero(association_id) && (sscanf(association_id, "%30d", &_association_id) != 1)) {
03949          astman_send_error(s, m, "Invalid ChargingAssociationId");
03950          goto aocmessage_cleanup;
03951       }
03952       if (!ast_strlen_zero(association_plan) && (sscanf(association_plan, "%30u", &_association_plan) != 1)) {
03953          astman_send_error(s, m, "Invalid ChargingAssociationPlan");
03954          goto aocmessage_cleanup;
03955       }
03956 
03957       if (_association_id) {
03958          ast_aoc_set_association_id(decoded, _association_id);
03959       } else if (!ast_strlen_zero(association_num)) {
03960          ast_aoc_set_association_number(decoded, association_num, _association_plan);
03961       }
03962    }
03963 
03964    if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
03965       ast_aoc_set_currency_info(decoded, _currencyamount, _mult, ast_strlen_zero(currencyname) ? NULL : currencyname);
03966    } else if (_chargetype == AST_AOC_CHARGE_UNIT) {
03967       struct ast_aoc_unit_entry entry;
03968       int i;
03969 
03970       /* multiple unit entries are possible, lets get them all */
03971       for (i = 0; i < 32; i++) {
03972          if (aocmessage_get_unit_entry(m, &entry, i)) {
03973             break; /* that's the end then */
03974          }
03975 
03976          ast_aoc_add_unit_entry(decoded, entry.valid_amount, entry.amount, entry.valid_type, entry.type);
03977       }
03978 
03979       /* at least one unit entry is required */
03980       if (!i) {
03981          astman_send_error(s, m, "Invalid UnitAmount(0), At least one valid unit entry is required when ChargeType is set to Unit");
03982          goto aocmessage_cleanup;
03983       }
03984 
03985    }
03986 
03987    ast_aoc_set_billing_id(decoded, _billingid);
03988    ast_aoc_set_total_type(decoded, _totaltype);
03989 
03990 
03991    if ((encoded = ast_aoc_encode(decoded, &encoded_size, NULL)) && !ast_indicate_data(chan, AST_CONTROL_AOC, encoded, encoded_size)) {
03992       astman_send_ack(s, m, "AOC Message successfully queued on channel");
03993    } else {
03994       astman_send_error(s, m, "Error encoding AOC message, could not queue onto channel");
03995    }
03996 
03997 aocmessage_cleanup:
03998 
03999    ast_aoc_destroy_decoded(decoded);
04000    ast_aoc_destroy_encoded(encoded);
04001 
04002    if (chan) {
04003       chan = ast_channel_unref(chan);
04004    }
04005    return 0;
04006 }
04007 
04008 static int action_originate(struct mansession *s, const struct message *m)
04009 {
04010    const char *name = astman_get_header(m, "Channel");
04011    const char *exten = astman_get_header(m, "Exten");
04012    const char *context = astman_get_header(m, "Context");
04013    const char *priority = astman_get_header(m, "Priority");
04014    const char *timeout = astman_get_header(m, "Timeout");
04015    const char *callerid = astman_get_header(m, "CallerID");
04016    const char *account = astman_get_header(m, "Account");
04017    const char *app = astman_get_header(m, "Application");
04018    const char *appdata = astman_get_header(m, "Data");
04019    const char *async = astman_get_header(m, "Async");
04020    const char *id = astman_get_header(m, "ActionID");
04021    const char *codecs = astman_get_header(m, "Codecs");
04022    struct ast_variable *vars;
04023    char *tech, *data;
04024    char *l = NULL, *n = NULL;
04025    int pi = 0;
04026    int res;
04027    int to = 30000;
04028    int reason = 0;
04029    char tmp[256];
04030    char tmp2[256];
04031    format_t format = AST_FORMAT_SLINEAR;
04032 
04033    pthread_t th;
04034    if (ast_strlen_zero(name)) {
04035       astman_send_error(s, m, "Channel not specified");
04036       return 0;
04037    }
04038    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
04039       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
04040          astman_send_error(s, m, "Invalid priority");
04041          return 0;
04042       }
04043    }
04044    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
04045       astman_send_error(s, m, "Invalid timeout");
04046       return 0;
04047    }
04048    ast_copy_string(tmp, name, sizeof(tmp));
04049    tech = tmp;
04050    data = strchr(tmp, '/');
04051    if (!data) {
04052       astman_send_error(s, m, "Invalid channel");
04053       return 0;
04054    }
04055    *data++ = '\0';
04056    ast_copy_string(tmp2, callerid, sizeof(tmp2));
04057    ast_callerid_parse(tmp2, &n, &l);
04058    if (n) {
04059       if (ast_strlen_zero(n)) {
04060          n = NULL;
04061       }
04062    }
04063    if (l) {
04064       ast_shrink_phone_number(l);
04065       if (ast_strlen_zero(l)) {
04066          l = NULL;
04067       }
04068    }
04069    if (!ast_strlen_zero(codecs)) {
04070       format = 0;
04071       ast_parse_allow_disallow(NULL, &format, codecs, 1);
04072    }
04073    if (!ast_strlen_zero(app) && s->session) {
04074       int bad_appdata = 0;
04075       /* To run the System application (or anything else that goes to
04076        * shell), you must have the additional System privilege */
04077       if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
04078          && (
04079             strcasestr(app, "system") ||      /* System(rm -rf /)
04080                                                  TrySystem(rm -rf /)       */
04081             strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
04082                                                  TryExec(System(rm -rf /)) */
04083             strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
04084                                                  EAGI(/bin/rm,-rf /)       */
04085             strcasestr(app, "mixmonitor") ||  /* MixMonitor(blah,,rm -rf)  */
04086             strcasestr(app, "externalivr") || /* ExternalIVR(rm -rf)       */
04087             (strstr(appdata, "SHELL") && (bad_appdata = 1)) ||       /* NoOp(${SHELL(rm -rf /)})  */
04088             (strstr(appdata, "EVAL") && (bad_appdata = 1))           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
04089             )) {
04090          char error_buf[64];
04091          snprintf(error_buf, sizeof(error_buf), "Originate Access Forbidden: %s", bad_appdata ? "Data" : "Application");
04092          astman_send_error(s, m, error_buf);
04093          return 0;
04094       }
04095    }
04096    /* Allocate requested channel variables */
04097    vars = astman_get_variables(m);
04098 
04099    if (ast_true(async)) {
04100       struct fast_originate_helper *fast;
04101 
04102       fast = ast_calloc(1, sizeof(*fast));
04103       if (!fast || ast_string_field_init(fast, 252)) {
04104          ast_free(fast);
04105          ast_variables_destroy(vars);
04106          res = -1;
04107       } else {
04108          if (!ast_strlen_zero(id)) {
04109             ast_string_field_build(fast, idtext, "ActionID: %s\r\n", id);
04110          }
04111          ast_string_field_set(fast, tech, tech);
04112          ast_string_field_set(fast, data, data);
04113          ast_string_field_set(fast, app, app);
04114          ast_string_field_set(fast, appdata, appdata);
04115          ast_string_field_set(fast, cid_num, l);
04116          ast_string_field_set(fast, cid_name, n);
04117          ast_string_field_set(fast, context, context);
04118          ast_string_field_set(fast, exten, exten);
04119          ast_string_field_set(fast, account, account);
04120          fast->vars = vars;
04121          fast->format = format;
04122          fast->timeout = to;
04123          fast->priority = pi;
04124          if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
04125             destroy_fast_originate_helper(fast);
04126             res = -1;
04127          } else {
04128             res = 0;
04129          }
04130       }
04131    } else if (!ast_strlen_zero(app)) {
04132       res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
04133       /* Any vars memory was passed to ast_pbx_outgoing_app(). */
04134    } else {
04135       if (exten && context && pi) {
04136          res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
04137          /* Any vars memory was passed to ast_pbx_outgoing_exten(). */
04138       } else {
04139          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
04140          ast_variables_destroy(vars);
04141          return 0;
04142       }
04143    }
04144    if (!res) {
04145       astman_send_ack(s, m, "Originate successfully queued");
04146    } else {
04147       astman_send_error(s, m, "Originate failed");
04148    }
04149    return 0;
04150 }
04151 
04152 static int action_mailboxstatus(struct mansession *s, const struct message *m)
04153 {
04154    const char *mailbox = astman_get_header(m, "Mailbox");
04155    int ret;
04156 
04157    if (ast_strlen_zero(mailbox)) {
04158       astman_send_error(s, m, "Mailbox not specified");
04159       return 0;
04160    }
04161    ret = ast_app_has_voicemail(mailbox, NULL);
04162    astman_start_ack(s, m);
04163    astman_append(s, "Message: Mailbox Status\r\n"
04164           "Mailbox: %s\r\n"
04165           "Waiting: %d\r\n\r\n", mailbox, ret);
04166    return 0;
04167 }
04168 
04169 static int action_mailboxcount(struct mansession *s, const struct message *m)
04170 {
04171    const char *mailbox = astman_get_header(m, "Mailbox");
04172    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
04173 
04174    if (ast_strlen_zero(mailbox)) {
04175       astman_send_error(s, m, "Mailbox not specified");
04176       return 0;
04177    }
04178    ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
04179    astman_start_ack(s, m);
04180    astman_append(s,   "Message: Mailbox Message Count\r\n"
04181             "Mailbox: %s\r\n"
04182             "UrgMessages: %d\r\n"
04183             "NewMessages: %d\r\n"
04184             "OldMessages: %d\r\n"
04185             "\r\n",
04186             mailbox, urgentmsgs, newmsgs, oldmsgs);
04187    return 0;
04188 }
04189 
04190 static int action_extensionstate(struct mansession *s, const struct message *m)
04191 {
04192    const char *exten = astman_get_header(m, "Exten");
04193    const char *context = astman_get_header(m, "Context");
04194    char hint[256] = "";
04195    int status;
04196    if (ast_strlen_zero(exten)) {
04197       astman_send_error(s, m, "Extension not specified");
04198       return 0;
04199    }
04200    if (ast_strlen_zero(context)) {
04201       context = "default";
04202    }
04203    status = ast_extension_state(NULL, context, exten);
04204    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
04205    astman_start_ack(s, m);
04206    astman_append(s,   "Message: Extension Status\r\n"
04207             "Exten: %s\r\n"
04208             "Context: %s\r\n"
04209             "Hint: %s\r\n"
04210             "Status: %d\r\n\r\n",
04211             exten, context, hint, status);
04212    return 0;
04213 }
04214 
04215 static int action_timeout(struct mansession *s, const struct message *m)
04216 {
04217    struct ast_channel *c;
04218    const char *name = astman_get_header(m, "Channel");
04219    double timeout = atof(astman_get_header(m, "Timeout"));
04220    struct timeval when = { timeout, 0 };
04221 
04222    if (ast_strlen_zero(name)) {
04223       astman_send_error(s, m, "No channel specified");
04224       return 0;
04225    }
04226 
04227    if (!timeout || timeout < 0) {
04228       astman_send_error(s, m, "No timeout specified");
04229       return 0;
04230    }
04231 
04232    if (!(c = ast_channel_get_by_name(name))) {
04233       astman_send_error(s, m, "No such channel");
04234       return 0;
04235    }
04236 
04237    when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
04238 
04239    ast_channel_lock(c);
04240    ast_channel_setwhentohangup_tv(c, when);
04241    ast_channel_unlock(c);
04242    c = ast_channel_unref(c);
04243 
04244    astman_send_ack(s, m, "Timeout Set");
04245 
04246    return 0;
04247 }
04248 
04249 static int whitefilter_cmp_fn(void *obj, void *arg, void *data, int flags)
04250 {
04251    regex_t *regex_filter = obj;
04252    const char *eventdata = arg;
04253    int *result = data;
04254 
04255    if (!regexec(regex_filter, eventdata, 0, NULL, 0)) {
04256       *result = 1;
04257       return (CMP_MATCH | CMP_STOP);
04258    }
04259 
04260    return 0;
04261 }
04262 
04263 static int blackfilter_cmp_fn(void *obj, void *arg, void *data, int flags)
04264 {
04265    regex_t *regex_filter = obj;
04266    const char *eventdata = arg;
04267    int *result = data;
04268 
04269    if (!regexec(regex_filter, eventdata, 0, NULL, 0)) {
04270       *result = 0;
04271       return (CMP_MATCH | CMP_STOP);
04272    }
04273 
04274    *result = 1;
04275    return 0;
04276 }
04277 
04278 static int match_filter(struct mansession *s, char *eventdata)
04279 {
04280    int result = 0;
04281 
04282    ast_debug(3, "Examining event:\n%s\n", eventdata);
04283    if (!ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
04284       return 1; /* no filtering means match all */
04285    } else if (ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
04286       /* white filters only: implied black all filter processed first, then white filters */
04287       ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04288    } else if (!ao2_container_count(s->session->whitefilters) && ao2_container_count(s->session->blackfilters)) {
04289       /* black filters only: implied white all filter processed first, then black filters */
04290       ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04291    } else {
04292       /* white and black filters: implied black all filter processed first, then white filters, and lastly black filters */
04293       ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04294       if (result) {
04295          result = 0;
04296          ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container"); 
04297       }
04298    }
04299 
04300    return result;
04301 }
04302 
04303 /*!
04304  * Send any applicable events to the client listening on this socket.
04305  * Wait only for a finite time on each event, and drop all events whether
04306  * they are successfully sent or not.
04307  */
04308 static int process_events(struct mansession *s)
04309 {
04310    int ret = 0;
04311 
04312    ao2_lock(s->session);
04313    if (s->session->f != NULL) {
04314       struct eventqent *eqe = s->session->last_ev;
04315 
04316       while ((eqe = advance_event(eqe))) {
04317          if (!ret && s->session->authenticated &&
04318              (s->session->readperm & eqe->category) == eqe->category &&
04319              (s->session->send_events & eqe->category) == eqe->category) {
04320                if (match_filter(s, eqe->eventdata)) {
04321                   if (send_string(s, eqe->eventdata) < 0)
04322                      ret = -1;   /* don't send more */
04323                }
04324          }
04325          s->session->last_ev = eqe;
04326       }
04327    }
04328    ao2_unlock(s->session);
04329    return ret;
04330 }
04331 
04332 static int action_userevent(struct mansession *s, const struct message *m)
04333 {
04334    const char *event = astman_get_header(m, "UserEvent");
04335    struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
04336    int x;
04337 
04338    ast_str_reset(body);
04339 
04340    for (x = 0; x < m->hdrcount; x++) {
04341       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
04342          ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
04343       }
04344    }
04345 
04346    astman_send_ack(s, m, "Event Sent");   
04347    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
04348    return 0;
04349 }
04350 
04351 /*! \brief Show PBX core settings information */
04352 static int action_coresettings(struct mansession *s, const struct message *m)
04353 {
04354    const char *actionid = astman_get_header(m, "ActionID");
04355    char idText[150];
04356 
04357    if (!ast_strlen_zero(actionid)) {
04358       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04359    } else {
04360       idText[0] = '\0';
04361    }
04362 
04363    astman_append(s, "Response: Success\r\n"
04364          "%s"
04365          "AMIversion: %s\r\n"
04366          "AsteriskVersion: %s\r\n"
04367          "SystemName: %s\r\n"
04368          "CoreMaxCalls: %d\r\n"
04369          "CoreMaxLoadAvg: %f\r\n"
04370          "CoreRunUser: %s\r\n"
04371          "CoreRunGroup: %s\r\n"
04372          "CoreMaxFilehandles: %d\r\n"
04373          "CoreRealTimeEnabled: %s\r\n"
04374          "CoreCDRenabled: %s\r\n"
04375          "CoreHTTPenabled: %s\r\n"
04376          "\r\n",
04377          idText,
04378          AMI_VERSION,
04379          ast_get_version(),
04380          ast_config_AST_SYSTEM_NAME,
04381          option_maxcalls,
04382          option_maxload,
04383          ast_config_AST_RUN_USER,
04384          ast_config_AST_RUN_GROUP,
04385          option_maxfiles,
04386          AST_CLI_YESNO(ast_realtime_enabled()),
04387          AST_CLI_YESNO(check_cdr_enabled()),
04388          AST_CLI_YESNO(check_webmanager_enabled())
04389          );
04390    return 0;
04391 }
04392 
04393 /*! \brief Show PBX core status information */
04394 static int action_corestatus(struct mansession *s, const struct message *m)
04395 {
04396    const char *actionid = astman_get_header(m, "ActionID");
04397    char idText[150];
04398    char startuptime[150], startupdate[150];
04399    char reloadtime[150], reloaddate[150];
04400    struct ast_tm tm;
04401 
04402    if (!ast_strlen_zero(actionid)) {
04403       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04404    } else {
04405       idText[0] = '\0';
04406    }
04407 
04408    ast_localtime(&ast_startuptime, &tm, NULL);
04409    ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
04410    ast_strftime(startupdate, sizeof(startupdate), "%Y-%m-%d", &tm);
04411    ast_localtime(&ast_lastreloadtime, &tm, NULL);
04412    ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
04413    ast_strftime(reloaddate, sizeof(reloaddate), "%Y-%m-%d", &tm);
04414 
04415    astman_append(s, "Response: Success\r\n"
04416          "%s"
04417          "CoreStartupDate: %s\r\n"
04418          "CoreStartupTime: %s\r\n"
04419          "CoreReloadDate: %s\r\n"
04420          "CoreReloadTime: %s\r\n"
04421          "CoreCurrentCalls: %d\r\n"
04422          "\r\n",
04423          idText,
04424          startupdate,
04425          startuptime,
04426          reloaddate,
04427          reloadtime,
04428          ast_active_channels()
04429          );
04430    return 0;
04431 }
04432 
04433 /*! \brief Send a reload event */
04434 static int action_reload(struct mansession *s, const struct message *m)
04435 {
04436    const char *module = astman_get_header(m, "Module");
04437    int res = ast_module_reload(S_OR(module, NULL));
04438 
04439    switch (res) {
04440    case -1:
04441       astman_send_error(s, m, "A reload is in progress");
04442       break;
04443    case 0:
04444       astman_send_error(s, m, "No such module");
04445       break;
04446    case 1:
04447       astman_send_error(s, m, "Module does not support reload");
04448       break;
04449    case 2:
04450       astman_send_ack(s, m, "Module Reloaded");
04451       break;
04452    default:
04453       astman_send_error(s, m, "An unknown error occurred");
04454       break;
04455    }
04456    return 0;
04457 }
04458 
04459 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels
04460  *          and some information about them. */
04461 static int action_coreshowchannels(struct mansession *s, const struct message *m)
04462 {
04463    const char *actionid = astman_get_header(m, "ActionID");
04464    char idText[256];
04465    struct ast_channel *c = NULL;
04466    int numchans = 0;
04467    int duration, durh, durm, durs;
04468    struct ast_channel_iterator *iter;
04469 
04470    if (!ast_strlen_zero(actionid)) {
04471       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
04472    } else {
04473       idText[0] = '\0';
04474    }
04475 
04476    if (!(iter = ast_channel_iterator_all_new())) {
04477       astman_send_error(s, m, "Memory Allocation Failure");
04478       return 1;
04479    }
04480 
04481    astman_send_listack(s, m, "Channels will follow", "start");
04482 
04483    for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
04484       struct ast_channel *bc;
04485       char durbuf[10] = "";
04486 
04487       ast_channel_lock(c);
04488 
04489       bc = ast_bridged_channel(c);
04490       if (c->cdr && !ast_tvzero(c->cdr->start)) {
04491          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
04492          durh = duration / 3600;
04493          durm = (duration % 3600) / 60;
04494          durs = duration % 60;
04495          snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
04496       }
04497 
04498       astman_append(s,
04499          "Event: CoreShowChannel\r\n"
04500          "%s"
04501          "Channel: %s\r\n"
04502          "UniqueID: %s\r\n"
04503          "Context: %s\r\n"
04504          "Extension: %s\r\n"
04505          "Priority: %d\r\n"
04506          "ChannelState: %d\r\n"
04507          "ChannelStateDesc: %s\r\n"
04508          "Application: %s\r\n"
04509          "ApplicationData: %s\r\n"
04510          "CallerIDnum: %s\r\n"
04511          "CallerIDname: %s\r\n"
04512          "ConnectedLineNum: %s\r\n"
04513          "ConnectedLineName: %s\r\n"
04514          "Duration: %s\r\n"
04515          "AccountCode: %s\r\n"
04516          "BridgedChannel: %s\r\n"
04517          "BridgedUniqueID: %s\r\n"
04518          "\r\n", idText, c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state,
04519          ast_state2str(c->_state), c->appl ? c->appl : "", c->data ? S_OR(c->data, "") : "",
04520          S_COR(c->caller.id.number.valid, c->caller.id.number.str, ""),
04521          S_COR(c->caller.id.name.valid, c->caller.id.name.str, ""),
04522          S_COR(c->connected.id.number.valid, c->connected.id.number.str, ""),
04523          S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
04524          durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
04525 
04526       ast_channel_unlock(c);
04527 
04528       numchans++;
04529    }
04530 
04531    astman_append(s,
04532       "Event: CoreShowChannelsComplete\r\n"
04533       "EventList: Complete\r\n"
04534       "ListItems: %d\r\n"
04535       "%s"
04536       "\r\n", numchans, idText);
04537 
04538    ast_channel_iterator_destroy(iter);
04539 
04540    return 0;
04541 }
04542 
04543 /* Manager function to check if module is loaded */
04544 static int manager_modulecheck(struct mansession *s, const struct message *m)
04545 {
04546    int res;
04547    const char *module = astman_get_header(m, "Module");
04548    const char *id = astman_get_header(m, "ActionID");
04549    char idText[256];
04550 #if !defined(LOW_MEMORY)
04551    const char *version;
04552 #endif
04553    char filename[PATH_MAX];
04554    char *cut;
04555 
04556    ast_copy_string(filename, module, sizeof(filename));
04557    if ((cut = strchr(filename, '.'))) {
04558       *cut = '\0';
04559    } else {
04560       cut = filename + strlen(filename);
04561    }
04562    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
04563    ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
04564    res = ast_module_check(filename);
04565    if (!res) {
04566       astman_send_error(s, m, "Module not loaded");
04567       return 0;
04568    }
04569    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
04570    ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
04571 #if !defined(LOW_MEMORY)
04572    version = ast_file_version_find(filename);
04573 #endif
04574 
04575    if (!ast_strlen_zero(id)) {
04576       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04577    } else {
04578       idText[0] = '\0';
04579    }
04580    astman_append(s, "Response: Success\r\n%s", idText);
04581 #if !defined(LOW_MEMORY)
04582    astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
04583 #endif
04584    return 0;
04585 }
04586 
04587 static int manager_moduleload(struct mansession *s, const struct message *m)
04588 {
04589    int res;
04590    const char *module = astman_get_header(m, "Module");
04591    const char *loadtype = astman_get_header(m, "LoadType");
04592 
04593    if (!loadtype || strlen(loadtype) == 0) {
04594       astman_send_error(s, m, "Incomplete ModuleLoad action.");
04595    }
04596    if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
04597       astman_send_error(s, m, "Need module name");
04598    }
04599 
04600    if (!strcasecmp(loadtype, "load")) {
04601       res = ast_load_resource(module);
04602       if (res) {
04603          astman_send_error(s, m, "Could not load module.");
04604       } else {
04605          astman_send_ack(s, m, "Module loaded.");
04606       }
04607    } else if (!strcasecmp(loadtype, "unload")) {
04608       res = ast_unload_resource(module, AST_FORCE_SOFT);
04609       if (res) {
04610          astman_send_error(s, m, "Could not unload module.");
04611       } else {
04612          astman_send_ack(s, m, "Module unloaded.");
04613       }
04614    } else if (!strcasecmp(loadtype, "reload")) {
04615       if (!ast_strlen_zero(module)) {
04616          res = ast_module_reload(module);
04617          if (res == 0) {
04618             astman_send_error(s, m, "No such module.");
04619          } else if (res == 1) {
04620             astman_send_error(s, m, "Module does not support reload action.");
04621          } else {
04622             astman_send_ack(s, m, "Module reloaded.");
04623          }
04624       } else {
04625          ast_module_reload(NULL);   /* Reload all modules */
04626          astman_send_ack(s, m, "All modules reloaded");
04627       }
04628    } else
04629       astman_send_error(s, m, "Incomplete ModuleLoad action.");
04630    return 0;
04631 }
04632 
04633 /*
04634  * Done with the action handlers here, we start with the code in charge
04635  * of accepting connections and serving them.
04636  * accept_thread() forks a new thread for each connection, session_do(),
04637  * which in turn calls get_input() repeatedly until a full message has
04638  * been accumulated, and then invokes process_message() to pass it to
04639  * the appropriate handler.
04640  */
04641 
04642 /*
04643  * Process an AMI message, performing desired action.
04644  * Return 0 on success, -1 on error that require the session to be destroyed.
04645  */
04646 static int process_message(struct mansession *s, const struct message *m)
04647 {
04648    int ret = 0;
04649    struct manager_action *act_found;
04650    const char *user;
04651    const char *action;
04652 
04653    action = __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY);
04654    if (ast_strlen_zero(action)) {
04655       report_req_bad_format(s, "NONE");
04656       mansession_lock(s);
04657       astman_send_error(s, m, "Missing action in request");
04658       mansession_unlock(s);
04659       return 0;
04660    }
04661 
04662    if (!s->session->authenticated
04663       && strcasecmp(action, "Login")
04664       && strcasecmp(action, "Logoff")
04665       && strcasecmp(action, "Challenge")) {
04666       if (!s->session->authenticated) {
04667          report_req_not_allowed(s, action);
04668       }
04669       mansession_lock(s);
04670       astman_send_error(s, m, "Permission denied");
04671       mansession_unlock(s);
04672       return 0;
04673    }
04674 
04675    if (!allowmultiplelogin
04676       && !s->session->authenticated
04677       && (!strcasecmp(action, "Login")
04678          || !strcasecmp(action, "Challenge"))) {
04679       user = astman_get_header(m, "Username");
04680 
04681       if (check_manager_session_inuse(user)) {
04682          report_session_limit(s);
04683          sleep(1);
04684          mansession_lock(s);
04685          astman_send_error(s, m, "Login Already In Use");
04686          mansession_unlock(s);
04687          return -1;
04688       }
04689    }
04690 
04691    act_found = action_find(action);
04692    if (act_found) {
04693       /* Found the requested AMI action. */
04694       int acted = 0;
04695 
04696       if ((s->session->writeperm & act_found->authority)
04697          || act_found->authority == 0) {
04698          /* We have the authority to execute the action. */
04699          ao2_lock(act_found);
04700          if (act_found->registered && act_found->func) {
04701             ast_debug(1, "Running action '%s'\n", act_found->action);
04702             ++act_found->active_count;
04703             ao2_unlock(act_found);
04704             ret = act_found->func(s, m);
04705             acted = 1;
04706             ao2_lock(act_found);
04707             --act_found->active_count;
04708          }
04709          ao2_unlock(act_found);
04710       }
04711       if (!acted) {
04712          /*
04713           * We did not execute the action because access was denied, it
04714           * was no longer registered, or no action was really registered.
04715           * Complain about it and leave.
04716           */
04717          report_req_not_allowed(s, action);
04718          mansession_lock(s);
04719          astman_send_error(s, m, "Permission denied");
04720          mansession_unlock(s);
04721       }
04722       ao2_t_ref(act_found, -1, "done with found action object");
04723    } else {
04724       char buf[512];
04725 
04726       report_req_bad_format(s, action);
04727       snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
04728       mansession_lock(s);
04729       astman_send_error(s, m, buf);
04730       mansession_unlock(s);
04731    }
04732    if (ret) {
04733       return ret;
04734    }
04735    /* Once done with our message, deliver any pending events unless the
04736       requester doesn't want them as part of this response.
04737    */
04738    if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
04739       return process_events(s);
04740    } else {
04741       return ret;
04742    }
04743 }
04744 
04745 /*!
04746  * Read one full line (including crlf) from the manager socket.
04747  * \note \verbatim
04748  * \r\n is the only valid terminator for the line.
04749  * (Note that, later, '\0' will be considered as the end-of-line marker,
04750  * so everything between the '\0' and the '\r\n' will not be used).
04751  * Also note that we assume output to have at least "maxlen" space.
04752  * \endverbatim
04753  */
04754 static int get_input(struct mansession *s, char *output)
04755 {
04756    int res, x;
04757    int maxlen = sizeof(s->session->inbuf) - 1;
04758    char *src = s->session->inbuf;
04759    int timeout = -1;
04760    time_t now;
04761 
04762    /*
04763     * Look for \r\n within the buffer. If found, copy to the output
04764     * buffer and return, trimming the \r\n (not used afterwards).
04765     */
04766    for (x = 0; x < s->session->inlen; x++) {
04767       int cr;  /* set if we have \r */
04768       if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
04769          cr = 2;  /* Found. Update length to include \r\n */
04770       } else if (src[x] == '\n') {
04771          cr = 1;  /* also accept \n only */
04772       } else {
04773          continue;
04774       }
04775       memmove(output, src, x);   /*... but trim \r\n */
04776       output[x] = '\0';    /* terminate the string */
04777       x += cr;       /* number of bytes used */
04778       s->session->inlen -= x;       /* remaining size */
04779       memmove(src, src + x, s->session->inlen); /* remove used bytes */
04780       return 1;
04781    }
04782    if (s->session->inlen >= maxlen) {
04783       /* no crlf found, and buffer full - sorry, too long for us */
04784       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
04785       s->session->inlen = 0;
04786    }
04787    res = 0;
04788    while (res == 0) {
04789       /* calculate a timeout if we are not authenticated */
04790       if (!s->session->authenticated) {
04791          if(time(&now) == -1) {
04792             ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
04793             return -1;
04794          }
04795 
04796          timeout = (authtimeout - (now - s->session->authstart)) * 1000;
04797          if (timeout < 0) {
04798             /* we have timed out */
04799             return 0;
04800          }
04801       }
04802 
04803       ao2_lock(s->session);
04804       if (s->session->pending_event) {
04805          s->session->pending_event = 0;
04806          ao2_unlock(s->session);
04807          return 0;
04808       }
04809       s->session->waiting_thread = pthread_self();
04810       ao2_unlock(s->session);
04811 
04812       res = ast_wait_for_input(s->session->fd, timeout);
04813 
04814       ao2_lock(s->session);
04815       s->session->waiting_thread = AST_PTHREADT_NULL;
04816       ao2_unlock(s->session);
04817    }
04818    if (res < 0) {
04819       /* If we get a signal from some other thread (typically because
04820        * there are new events queued), return 0 to notify the caller.
04821        */
04822       if (errno == EINTR || errno == EAGAIN) {
04823          return 0;
04824       }
04825       ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
04826       return -1;
04827    }
04828 
04829    ao2_lock(s->session);
04830    res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
04831    if (res < 1) {
04832       res = -1;   /* error return */
04833    } else {
04834       s->session->inlen += res;
04835       src[s->session->inlen] = '\0';
04836       res = 0;
04837    }
04838    ao2_unlock(s->session);
04839    return res;
04840 }
04841 
04842 /*!
04843  * \internal
04844  * \brief Read and process an AMI action request.
04845  *
04846  * \param s AMI session to process action request.
04847  *
04848  * \retval 0 Retain AMI connection for next command.
04849  * \retval -1 Drop AMI connection due to logoff or connection error.
04850  */
04851 static int do_message(struct mansession *s)
04852 {
04853    struct message m = { 0 };
04854    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
04855    int res;
04856    int idx;
04857    int hdr_loss;
04858    time_t now;
04859 
04860    hdr_loss = 0;
04861    for (;;) {
04862       /* Check if any events are pending and do them if needed */
04863       if (process_events(s)) {
04864          res = -1;
04865          break;
04866       }
04867       res = get_input(s, header_buf);
04868       if (res == 0) {
04869          /* No input line received. */
04870          if (!s->session->authenticated) {
04871             if (time(&now) == -1) {
04872                ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
04873                res = -1;
04874                break;
04875             }
04876 
04877             if (now - s->session->authstart > authtimeout) {
04878                if (displayconnects) {
04879                   ast_verb(2, "Client from %s, failed to authenticate in %d seconds\n", ast_inet_ntoa(s->session->sin.sin_addr), authtimeout);
04880                }
04881                res = -1;
04882                break;
04883             }
04884          }
04885          continue;
04886       } else if (res > 0) {
04887          /* Input line received. */
04888          if (ast_strlen_zero(header_buf)) {
04889             if (hdr_loss) {
04890                mansession_lock(s);
04891                astman_send_error(s, &m, "Too many lines in message or allocation failure");
04892                mansession_unlock(s);
04893                res = 0;
04894             } else {
04895                res = process_message(s, &m) ? -1 : 0;
04896             }
04897             break;
04898          } else if (m.hdrcount < ARRAY_LEN(m.headers)) {
04899             m.headers[m.hdrcount] = ast_strdup(header_buf);
04900             if (!m.headers[m.hdrcount]) {
04901                /* Allocation failure. */
04902                hdr_loss = 1;
04903             } else {
04904                ++m.hdrcount;
04905             }
04906          } else {
04907             /* Too many lines in message. */
04908             hdr_loss = 1;
04909          }
04910       } else {
04911          /* Input error. */
04912          break;
04913       }
04914    }
04915 
04916    /* Free AMI request headers. */
04917    for (idx = 0; idx < m.hdrcount; ++idx) {
04918       ast_free((void *) m.headers[idx]);
04919    }
04920    return res;
04921 }
04922 
04923 /*! \brief The body of the individual manager session.
04924  * Call get_input() to read one line at a time
04925  * (or be woken up on new events), collect the lines in a
04926  * message until found an empty line, and execute the request.
04927  * In any case, deliver events asynchronously through process_events()
04928  * (called from here if no line is available, or at the end of
04929  * process_message(). )
04930  */
04931 static void *session_do(void *data)
04932 {
04933    struct ast_tcptls_session_instance *ser = data;
04934    struct mansession_session *session;
04935    struct mansession s = {
04936       .tcptls_session = data,
04937    };
04938    int flags;
04939    int res;
04940    struct sockaddr_in ser_remote_address_tmp;
04941    struct protoent *p;
04942 
04943    if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
04944       fclose(ser->f);
04945       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04946       goto done;
04947    }
04948 
04949    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
04950    session = build_mansession(ser_remote_address_tmp);
04951 
04952    if (session == NULL) {
04953       fclose(ser->f);
04954       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04955       goto done;
04956    }
04957 
04958    /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
04959     * This is necessary to prevent delays (caused by buffering) as we
04960     * write to the socket in bits and peices. */
04961    p = getprotobyname("tcp");
04962    if (p) {
04963       int arg = 1;
04964       if( setsockopt(ser->fd, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
04965          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));
04966       }
04967    } else {
04968       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");
04969    }
04970 
04971    flags = fcntl(ser->fd, F_GETFL);
04972    if (!block_sockets) { /* make sure socket is non-blocking */
04973       flags |= O_NONBLOCK;
04974    } else {
04975       flags &= ~O_NONBLOCK;
04976    }
04977    fcntl(ser->fd, F_SETFL, flags);
04978 
04979    ao2_lock(session);
04980    /* Hook to the tail of the event queue */
04981    session->last_ev = grab_last();
04982 
04983    ast_mutex_init(&s.lock);
04984 
04985    /* these fields duplicate those in the 'ser' structure */
04986    session->fd = s.fd = ser->fd;
04987    session->f = s.f = ser->f;
04988    session->sin = ser_remote_address_tmp;
04989    s.session = session;
04990 
04991    AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
04992 
04993    if(time(&session->authstart) == -1) {
04994       ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
04995       ast_atomic_fetchadd_int(&unauth_sessions, -1);
04996       ao2_unlock(session);
04997       session_destroy(session);
04998       goto done;
04999    }
05000    ao2_unlock(session);
05001 
05002    astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);   /* welcome prompt */
05003    for (;;) {
05004       if ((res = do_message(&s)) < 0 || s.write_error) {
05005          break;
05006       }
05007    }
05008    /* session is over, explain why and terminate */
05009    if (session->authenticated) {
05010       if (manager_displayconnects(session)) {
05011          ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
05012       }
05013    } else {
05014       ast_atomic_fetchadd_int(&unauth_sessions, -1);
05015       if (displayconnects) {
05016          ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
05017       }
05018    }
05019 
05020    session_destroy(session);
05021 
05022    ast_mutex_destroy(&s.lock);
05023 done:
05024    ao2_ref(ser, -1);
05025    ser = NULL;
05026    return NULL;
05027 }
05028 
05029 /*! \brief remove at most n_max stale session from the list. */
05030 static void purge_sessions(int n_max)
05031 {
05032    struct mansession_session *session;
05033    time_t now = time(NULL);
05034    struct ao2_iterator i;
05035 
05036    i = ao2_iterator_init(sessions, 0);
05037    while ((session = ao2_iterator_next(&i)) && n_max > 0) {
05038       ao2_lock(session);
05039       if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
05040          if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
05041             ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
05042                session->username, ast_inet_ntoa(session->sin.sin_addr));
05043          }
05044          ao2_unlock(session);
05045          session_destroy(session);
05046          n_max--;
05047       } else {
05048          ao2_unlock(session);
05049          unref_mansession(session);
05050       }
05051    }
05052    ao2_iterator_destroy(&i);
05053 }
05054 
05055 /*
05056  * events are appended to a queue from where they
05057  * can be dispatched to clients.
05058  */
05059 static int append_event(const char *str, int category)
05060 {
05061    struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
05062    static int seq;   /* sequence number */
05063 
05064    if (!tmp) {
05065       return -1;
05066    }
05067 
05068    /* need to init all fields, because ast_malloc() does not */
05069    tmp->usecount = 0;
05070    tmp->category = category;
05071    tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
05072    tmp->tv = ast_tvnow();
05073    AST_RWLIST_NEXT(tmp, eq_next) = NULL;
05074    strcpy(tmp->eventdata, str);
05075 
05076    AST_RWLIST_WRLOCK(&all_events);
05077    AST_RWLIST_INSERT_TAIL(&all_events, tmp, eq_next);
05078    AST_RWLIST_UNLOCK(&all_events);
05079 
05080    return 0;
05081 }
05082 
05083 AST_THREADSTORAGE(manager_event_funcbuf);
05084 
05085 static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
05086 {
05087    struct manager_channel_variable *var;
05088 
05089    AST_RWLIST_RDLOCK(&channelvars);
05090    AST_LIST_TRAVERSE(&channelvars, var, entry) {
05091       const char *val;
05092       struct ast_str *res;
05093 
05094       if (var->isfunc) {
05095          res = ast_str_thread_get(&manager_event_funcbuf, 16);
05096          if (res && ast_func_read2(chan, var->name, &res, 0) == 0) {
05097             val = ast_str_buffer(res);
05098          } else {
05099             val = NULL;
05100          }
05101       } else {
05102          val = pbx_builtin_getvar_helper(chan, var->name);
05103       }
05104       ast_str_append(pbuf, 0, "ChanVariable(%s): %s=%s\r\n", chan->name, var->name, val ? val : "");
05105    }
05106    AST_RWLIST_UNLOCK(&channelvars);
05107 }
05108 
05109 /* XXX see if can be moved inside the function */
05110 AST_THREADSTORAGE(manager_event_buf);
05111 #define MANAGER_EVENT_BUF_INITSIZE   256
05112 
05113 int __ast_manager_event_multichan(int category, const char *event, int chancount, struct
05114    ast_channel **chans, const char *file, int line, const char *func, const char *fmt, ...)
05115 {
05116    struct mansession_session *session;
05117    struct manager_custom_hook *hook;
05118    struct ast_str *auth = ast_str_alloca(80);
05119    const char *cat_str;
05120    va_list ap;
05121    struct timeval now;
05122    struct ast_str *buf;
05123    int i;
05124 
05125    if (!(sessions && ao2_container_count(sessions)) && AST_RWLIST_EMPTY(&manager_hooks)) {
05126       return 0;
05127    }
05128    
05129    if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) {
05130       return -1;
05131    }
05132 
05133    cat_str = authority_to_str(category, &auth);
05134    ast_str_set(&buf, 0,
05135          "Event: %s\r\nPrivilege: %s\r\n",
05136           event, cat_str);
05137 
05138    if (timestampevents) {
05139       now = ast_tvnow();
05140       ast_str_append(&buf, 0,
05141             "Timestamp: %ld.%06lu\r\n",
05142              (long)now.tv_sec, (unsigned long) now.tv_usec);
05143    }
05144    if (manager_debug) {
05145       static int seq;
05146       ast_str_append(&buf, 0,
05147             "SequenceNumber: %d\r\n",
05148              ast_atomic_fetchadd_int(&seq, 1));
05149       ast_str_append(&buf, 0,
05150             "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
05151    }
05152 
05153    va_start(ap, fmt);
05154    ast_str_append_va(&buf, 0, fmt, ap);
05155    va_end(ap);
05156    for (i = 0; i < chancount; i++) {
05157       append_channel_vars(&buf, chans[i]);
05158    }
05159 
05160    ast_str_append(&buf, 0, "\r\n");
05161 
05162    append_event(ast_str_buffer(buf), category);
05163 
05164    /* Wake up any sleeping sessions */
05165    if (sessions) {
05166       struct ao2_iterator i;
05167       i = ao2_iterator_init(sessions, 0);
05168       while ((session = ao2_iterator_next(&i))) {
05169          ao2_lock(session);
05170          if (session->waiting_thread != AST_PTHREADT_NULL) {
05171             pthread_kill(session->waiting_thread, SIGURG);
05172          } else {
05173             /* We have an event to process, but the mansession is
05174              * not waiting for it. We still need to indicate that there
05175              * is an event waiting so that get_input processes the pending
05176              * event instead of polling.
05177              */
05178             session->pending_event = 1;
05179          }
05180          ao2_unlock(session);
05181          unref_mansession(session);
05182       }
05183       ao2_iterator_destroy(&i);
05184    }
05185 
05186    if (!AST_RWLIST_EMPTY(&manager_hooks)) {
05187       AST_RWLIST_RDLOCK(&manager_hooks);
05188       AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
05189          hook->helper(category, event, ast_str_buffer(buf));
05190       }
05191       AST_RWLIST_UNLOCK(&manager_hooks);
05192    }
05193 
05194    return 0;
05195 }
05196 
05197 /*
05198  * support functions to register/unregister AMI action handlers,
05199  */
05200 int ast_manager_unregister(char *action)
05201 {
05202    struct manager_action *cur;
05203 
05204    AST_RWLIST_WRLOCK(&actions);
05205    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
05206       if (!strcasecmp(action, cur->action)) {
05207          AST_RWLIST_REMOVE_CURRENT(list);
05208          break;
05209       }
05210    }
05211    AST_RWLIST_TRAVERSE_SAFE_END;
05212    AST_RWLIST_UNLOCK(&actions);
05213 
05214    if (cur) {
05215       time_t now;
05216 
05217       /*
05218        * We have removed the action object from the container so we
05219        * are no longer in a hurry.
05220        */
05221       ao2_lock(cur);
05222       cur->registered = 0;
05223       ao2_unlock(cur);
05224 
05225       /*
05226        * Wait up to 5 seconds for any active invocations to complete
05227        * before returning.  We have to wait instead of blocking
05228        * because we may be waiting for ourself to complete.
05229        */
05230       now = time(NULL);
05231       while (cur->active_count) {
05232          if (5 <= time(NULL) - now) {
05233             ast_debug(1,
05234                "Unregister manager action %s timed out waiting for %d active instances to complete\n",
05235                action, cur->active_count);
05236             break;
05237          }
05238 
05239          sched_yield();
05240       }
05241 
05242       ao2_t_ref(cur, -1, "action object removed from list");
05243       ast_verb(2, "Manager unregistered action %s\n", action);
05244    }
05245 
05246    return 0;
05247 }
05248 
05249 static int manager_state_cb(char *context, char *exten, int state, void *data)
05250 {
05251    /* Notify managers of change */
05252    char hint[512];
05253    ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
05254 
05255    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
05256    return 0;
05257 }
05258 
05259 static int ast_manager_register_struct(struct manager_action *act)
05260 {
05261    struct manager_action *cur, *prev = NULL;
05262 
05263    AST_RWLIST_WRLOCK(&actions);
05264    AST_RWLIST_TRAVERSE(&actions, cur, list) {
05265       int ret;
05266 
05267       ret = strcasecmp(cur->action, act->action);
05268       if (ret == 0) {
05269          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
05270          AST_RWLIST_UNLOCK(&actions);
05271          return -1;
05272       }
05273       if (ret > 0) { /* Insert these alphabetically */
05274          prev = cur;
05275          break;
05276       }
05277    }
05278 
05279    ao2_t_ref(act, +1, "action object added to list");
05280    act->registered = 1;
05281    if (prev) {
05282       AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
05283    } else {
05284       AST_RWLIST_INSERT_HEAD(&actions, act, list);
05285    }
05286 
05287    ast_verb(2, "Manager registered action %s\n", act->action);
05288 
05289    AST_RWLIST_UNLOCK(&actions);
05290 
05291    return 0;
05292 }
05293 
05294 /*!
05295  * \internal
05296  * \brief Destroy the registered AMI action object.
05297  *
05298  * \param obj Object to destroy.
05299  *
05300  * \return Nothing
05301  */
05302 static void action_destroy(void *obj)
05303 {
05304    struct manager_action *doomed = obj;
05305 
05306    if (doomed->synopsis) {
05307       /* The string fields were initialized. */
05308       ast_string_field_free_memory(doomed);
05309    }
05310 }
05311 
05312 /*! \brief register a new command with manager, including online help. This is
05313    the preferred way to register a manager command */
05314 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
05315 {
05316    struct manager_action *cur;
05317 
05318    cur = ao2_alloc(sizeof(*cur), action_destroy);
05319    if (!cur) {
05320       return -1;
05321    }
05322    if (ast_string_field_init(cur, 128)) {
05323       ao2_t_ref(cur, -1, "action object creation failed");
05324       return -1;
05325    }
05326 
05327    cur->action = action;
05328    cur->authority = auth;
05329    cur->func = func;
05330 #ifdef AST_XML_DOCS
05331    if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
05332       char *tmpxml;
05333 
05334       tmpxml = ast_xmldoc_build_synopsis("manager", action, NULL);
05335       ast_string_field_set(cur, synopsis, tmpxml);
05336       ast_free(tmpxml);
05337 
05338       tmpxml = ast_xmldoc_build_syntax("manager", action, NULL);
05339       ast_string_field_set(cur, syntax, tmpxml);
05340       ast_free(tmpxml);
05341 
05342       tmpxml = ast_xmldoc_build_description("manager", action, NULL);
05343       ast_string_field_set(cur, description, tmpxml);
05344       ast_free(tmpxml);
05345 
05346       tmpxml = ast_xmldoc_build_seealso("manager", action, NULL);
05347       ast_string_field_set(cur, seealso, tmpxml);
05348       ast_free(tmpxml);
05349 
05350       tmpxml = ast_xmldoc_build_arguments("manager", action, NULL);
05351       ast_string_field_set(cur, arguments, tmpxml);
05352       ast_free(tmpxml);
05353 
05354       cur->docsrc = AST_XML_DOC;
05355    } else
05356 #endif
05357    {
05358       ast_string_field_set(cur, synopsis, synopsis);
05359       ast_string_field_set(cur, description, description);
05360 #ifdef AST_XML_DOCS
05361       cur->docsrc = AST_STATIC_DOC;
05362 #endif
05363    }
05364    if (ast_manager_register_struct(cur)) {
05365       ao2_t_ref(cur, -1, "action object registration failed");
05366       return -1;
05367    }
05368 
05369    ao2_t_ref(cur, -1, "action object registration successful");
05370    return 0;
05371 }
05372 /*! @}
05373  END Doxygen group */
05374 
05375 /*
05376  * The following are support functions for AMI-over-http.
05377  * The common entry point is generic_http_callback(),
05378  * which extracts HTTP header and URI fields and reformats
05379  * them into AMI messages, locates a proper session
05380  * (using the mansession_id Cookie or GET variable),
05381  * and calls process_message() as for regular AMI clients.
05382  * When done, the output (which goes to a temporary file)
05383  * is read back into a buffer and reformatted as desired,
05384  * then fed back to the client over the original socket.
05385  */
05386 
05387 enum output_format {
05388    FORMAT_RAW,
05389    FORMAT_HTML,
05390    FORMAT_XML,
05391 };
05392 
05393 static const char * const contenttype[] = {
05394    [FORMAT_RAW] = "plain",
05395    [FORMAT_HTML] = "html",
05396    [FORMAT_XML] =  "xml",
05397 };
05398 
05399 /*!
05400  * locate an http session in the list. The search key (ident) is
05401  * the value of the mansession_id cookie (0 is not valid and means
05402  * a session on the AMI socket).
05403  */
05404 static struct mansession_session *find_session(uint32_t ident, int incinuse)
05405 {
05406    struct mansession_session *session;
05407    struct ao2_iterator i;
05408 
05409    if (ident == 0) {
05410       return NULL;
05411    }
05412 
05413    i = ao2_iterator_init(sessions, 0);
05414    while ((session = ao2_iterator_next(&i))) {
05415       ao2_lock(session);
05416       if (session->managerid == ident && !session->needdestroy) {
05417          ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
05418          break;
05419       }
05420       ao2_unlock(session);
05421       unref_mansession(session);
05422    }
05423    ao2_iterator_destroy(&i);
05424 
05425    return session;
05426 }
05427 
05428 /*!
05429  * locate an http session in the list.
05430  * The search keys (nonce) and (username) is value from received
05431  * "Authorization" http header.
05432  * As well as in find_session() function, the value of the nonce can't be zero.
05433  * (0 meansi, that the session used for AMI socket connection).
05434  * Flag (stale) is set, if client used valid, but old, nonce value.
05435  *
05436  */
05437 static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
05438 {
05439    struct mansession_session *session;
05440    struct ao2_iterator i;
05441 
05442    if (nonce == 0 || username == NULL || stale == NULL) {
05443       return NULL;
05444    }
05445 
05446    i = ao2_iterator_init(sessions, 0);
05447    while ((session = ao2_iterator_next(&i))) {
05448       ao2_lock(session);
05449       if (!strcasecmp(session->username, username) && session->managerid == nonce) {
05450          *stale = 0;
05451          break;
05452       } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
05453          *stale = 1;
05454          break;
05455       }
05456       ao2_unlock(session);
05457       unref_mansession(session);
05458    }
05459    ao2_iterator_destroy(&i);
05460    return session;
05461 }
05462 
05463 int astman_is_authed(uint32_t ident)
05464 {
05465    int authed;
05466    struct mansession_session *session;
05467 
05468    if (!(session = find_session(ident, 0)))
05469       return 0;
05470 
05471    authed = (session->authenticated != 0);
05472 
05473    ao2_unlock(session);
05474    unref_mansession(session);
05475 
05476    return authed;
05477 }
05478 
05479 int astman_verify_session_readpermissions(uint32_t ident, int perm)
05480 {
05481    int result = 0;
05482    struct mansession_session *session;
05483    struct ao2_iterator i;
05484 
05485    if (ident == 0) {
05486       return 0;
05487    }
05488 
05489    i = ao2_iterator_init(sessions, 0);
05490    while ((session = ao2_iterator_next(&i))) {
05491       ao2_lock(session);
05492       if ((session->managerid == ident) && (session->readperm & perm)) {
05493          result = 1;
05494          ao2_unlock(session);
05495          unref_mansession(session);
05496          break;
05497       }
05498       ao2_unlock(session);
05499       unref_mansession(session);
05500    }
05501    ao2_iterator_destroy(&i);
05502    return result;
05503 }
05504 
05505 int astman_verify_session_writepermissions(uint32_t ident, int perm)
05506 {
05507    int result = 0;
05508    struct mansession_session *session;
05509    struct ao2_iterator i;
05510 
05511    if (ident == 0) {
05512       return 0;
05513    }
05514 
05515    i = ao2_iterator_init(sessions, 0);
05516    while ((session = ao2_iterator_next(&i))) {
05517       ao2_lock(session);
05518       if ((session->managerid == ident) && (session->writeperm & perm)) {
05519          result = 1;
05520          ao2_unlock(session);
05521          unref_mansession(session);
05522          break;
05523       }
05524       ao2_unlock(session);
05525       unref_mansession(session);
05526    }
05527    ao2_iterator_destroy(&i);
05528    return result;
05529 }
05530 
05531 /*
05532  * convert to xml with various conversion:
05533  * mode & 1 -> lowercase;
05534  * mode & 2 -> replace non-alphanumeric chars with underscore
05535  */
05536 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
05537 {
05538    /* store in a local buffer to avoid calling ast_str_append too often */
05539    char buf[256];
05540    char *dst = buf;
05541    int space = sizeof(buf);
05542    /* repeat until done and nothing to flush */
05543    for ( ; *src || dst != buf ; src++) {
05544       if (*src == '\0' || space < 10) {   /* flush */
05545          *dst++ = '\0';
05546          ast_str_append(out, 0, "%s", buf);
05547          dst = buf;
05548          space = sizeof(buf);
05549          if (*src == '\0') {
05550             break;
05551          }
05552       }
05553 
05554       if ( (mode & 2) && !isalnum(*src)) {
05555          *dst++ = '_';
05556          space--;
05557          continue;
05558       }
05559       switch (*src) {
05560       case '<':
05561          strcpy(dst, "&lt;");
05562          dst += 4;
05563          space -= 4;
05564          break;
05565       case '>':
05566          strcpy(dst, "&gt;");
05567          dst += 4;
05568          space -= 4;
05569          break;
05570       case '\"':
05571          strcpy(dst, "&quot;");
05572          dst += 6;
05573          space -= 6;
05574          break;
05575       case '\'':
05576          strcpy(dst, "&apos;");
05577          dst += 6;
05578          space -= 6;
05579          break;
05580       case '&':
05581          strcpy(dst, "&amp;");
05582          dst += 5;
05583          space -= 5;
05584          break;
05585 
05586       default:
05587          *dst++ = mode ? tolower(*src) : *src;
05588          space--;
05589       }
05590    }
05591 }
05592 
05593 struct variable_count {
05594    char *varname;
05595    int count;
05596 };
05597 
05598 static int variable_count_hash_fn(const void *vvc, const int flags)
05599 {
05600    const struct variable_count *vc = vvc;
05601 
05602    return ast_str_hash(vc->varname);
05603 }
05604 
05605 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
05606 {
05607    /* Due to the simplicity of struct variable_count, it makes no difference
05608     * if you pass in objects or strings, the same operation applies. This is
05609     * due to the fact that the hash occurs on the first element, which means
05610     * the address of both the struct and the string are exactly the same. */
05611    struct variable_count *vc = obj;
05612    char *str = vstr;
05613    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
05614 }
05615 
05616 /*! \brief Convert the input into XML or HTML.
05617  * The input is supposed to be a sequence of lines of the form
05618  * Name: value
05619  * optionally followed by a blob of unformatted text.
05620  * A blank line is a section separator. Basically, this is a
05621  * mixture of the format of Manager Interface and CLI commands.
05622  * The unformatted text is considered as a single value of a field
05623  * named 'Opaque-data'.
05624  *
05625  * At the moment the output format is the following (but it may
05626  * change depending on future requirements so don't count too
05627  * much on it when writing applications):
05628  *
05629  * General: the unformatted text is used as a value of
05630  * XML output:  to be completed
05631  *
05632  * \verbatim
05633  *   Each section is within <response type="object" id="xxx">
05634  *   where xxx is taken from ajaxdest variable or defaults to unknown
05635  *   Each row is reported as an attribute Name="value" of an XML
05636  *   entity named from the variable ajaxobjtype, default to "generic"
05637  * \endverbatim
05638  *
05639  * HTML output:
05640  *   each Name-value pair is output as a single row of a two-column table.
05641  *   Sections (blank lines in the input) are separated by a <HR>
05642  *
05643  */
05644 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
05645 {
05646    struct ast_variable *v;
05647    const char *dest = NULL;
05648    char *var, *val;
05649    const char *objtype = NULL;
05650    int in_data = 0;  /* parsing data */
05651    int inobj = 0;
05652    int xml = (format == FORMAT_XML);
05653    struct variable_count *vc = NULL;
05654    struct ao2_container *vco = NULL;
05655 
05656    if (xml) {
05657       /* dest and objtype need only for XML format */
05658       for (v = get_vars; v; v = v->next) {
05659          if (!strcasecmp(v->name, "ajaxdest")) {
05660             dest = v->value;
05661          } else if (!strcasecmp(v->name, "ajaxobjtype")) {
05662             objtype = v->value;
05663          }
05664       }
05665       if (ast_strlen_zero(dest)) {
05666          dest = "unknown";
05667       }
05668       if (ast_strlen_zero(objtype)) {
05669          objtype = "generic";
05670       }
05671    }
05672 
05673    /* we want to stop when we find an empty line */
05674    while (in && *in) {
05675       val = strsep(&in, "\r\n"); /* mark start and end of line */
05676       if (in && *in == '\n') {   /* remove trailing \n if any */
05677          in++;
05678       }
05679       ast_trim_blanks(val);
05680       ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
05681       if (ast_strlen_zero(val)) {
05682          /* empty line */
05683          if (in_data) {
05684             /* close data in Opaque mode */
05685             ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
05686             in_data = 0;
05687          }
05688 
05689          if (inobj) {
05690             /* close block */
05691             ast_str_append(out, 0, xml ? " /></response>\n" :
05692                "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
05693             inobj = 0;
05694             ao2_ref(vco, -1);
05695             vco = NULL;
05696          }
05697          continue;
05698       }
05699 
05700       if (!inobj) {
05701          /* start new block */
05702          if (xml) {
05703             ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
05704          }
05705          vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
05706          inobj = 1;
05707       }
05708 
05709       if (in_data) {
05710          /* Process data field in Opaque mode. This is a
05711           * followup, so we re-add line feeds. */
05712          ast_str_append(out, 0, xml ? "\n" : "<br>\n");
05713          xml_copy_escape(out, val, 0);   /* data field */
05714          continue;
05715       }
05716 
05717       /* We expect "Name: value" line here */
05718       var = strsep(&val, ":");
05719       if (val) {
05720          /* found the field name */
05721          val = ast_skip_blanks(val);
05722          ast_trim_blanks(var);
05723       } else {
05724          /* field name not found, switch to opaque mode */
05725          val = var;
05726          var = "Opaque-data";
05727          in_data = 1;
05728       }
05729 
05730 
05731       ast_str_append(out, 0, xml ? " " : "<tr><td>");
05732       if ((vc = ao2_find(vco, var, 0))) {
05733          vc->count++;
05734       } else {
05735          /* Create a new entry for this one */
05736          vc = ao2_alloc(sizeof(*vc), NULL);
05737          vc->varname = var;
05738          vc->count = 1;
05739          ao2_link(vco, vc);
05740       }
05741 
05742       xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
05743       if (vc->count > 1) {
05744          ast_str_append(out, 0, "-%d", vc->count);
05745       }
05746       ao2_ref(vc, -1);
05747       ast_str_append(out, 0, xml ? "='" : "</td><td>");
05748       xml_copy_escape(out, val, 0); /* data field */
05749       if (!in_data || !*in) {
05750          ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
05751       }
05752    }
05753 
05754    if (inobj) {
05755       ast_str_append(out, 0, xml ? " /></response>\n" :
05756          "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
05757       ao2_ref(vco, -1);
05758    }
05759 }
05760 
05761 static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
05762 {
05763    char *buf;
05764    size_t l;
05765 
05766    if (!s->f)
05767       return;
05768 
05769    /* Ensure buffer is NULL-terminated */
05770    fprintf(s->f, "%c", 0);
05771    fflush(s->f);
05772 
05773    if ((l = ftell(s->f)) > 0) {
05774       if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s->fd, 0))) {
05775          ast_log(LOG_WARNING, "mmap failed.  Manager output was not processed\n");
05776       } else {
05777          if (format == FORMAT_XML || format == FORMAT_HTML) {
05778             xml_translate(out, buf, params, format);
05779          } else {
05780             ast_str_append(out, 0, "%s", buf);
05781          }
05782          munmap(buf, l);
05783       }
05784    } else if (format == FORMAT_XML || format == FORMAT_HTML) {
05785       xml_translate(out, "", params, format);
05786    }
05787 
05788    if (s->f) {
05789       if (fclose(s->f)) {
05790          ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno));
05791       }
05792       s->f = NULL;
05793       s->fd = -1;
05794    } else if (s->fd != -1) {
05795       if (close(s->fd)) {
05796          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
05797       }
05798       s->fd = -1;
05799    } else {
05800       ast_log(LOG_ERROR, "process output attempted to close file/file descriptor on mansession without a valid file or file descriptor.\n");
05801    }
05802 }
05803 
05804 static int generic_http_callback(struct ast_tcptls_session_instance *ser,
05805                     enum ast_http_method method,
05806                     enum output_format format,
05807                     struct sockaddr_in *remote_address, const char *uri,
05808                     struct ast_variable *get_params,
05809                     struct ast_variable *headers)
05810 {
05811    struct mansession s = { .session = NULL, .tcptls_session = ser };
05812    struct mansession_session *session = NULL;
05813    uint32_t ident = 0;
05814    int blastaway = 0;
05815    struct ast_variable *v, *cookies, *params = get_params;
05816    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
05817    struct ast_str *http_header = NULL, *out = NULL;
05818    struct message m = { 0 };
05819    unsigned int idx;
05820    size_t hdrlen;
05821 
05822    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
05823       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
05824       return -1;
05825    }
05826 
05827    cookies = ast_http_get_cookies(headers);
05828    for (v = cookies; v; v = v->next) {
05829       if (!strcasecmp(v->name, "mansession_id")) {
05830          sscanf(v->value, "%30x", &ident);
05831          break;
05832       }
05833    }
05834    if (cookies) {
05835       ast_variables_destroy(cookies);
05836    }
05837 
05838    if (!(session = find_session(ident, 1))) {
05839 
05840       /**/
05841       /* Create new session.
05842        * While it is not in the list we don't need any locking
05843        */
05844       if (!(session = build_mansession(*remote_address))) {
05845          ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
05846          return -1;
05847       }
05848       ao2_lock(session);
05849       session->sin = *remote_address;
05850       session->fd = -1;
05851       session->waiting_thread = AST_PTHREADT_NULL;
05852       session->send_events = 0;
05853       session->inuse = 1;
05854       /*!\note There is approximately a 1 in 1.8E19 chance that the following
05855        * calculation will produce 0, which is an invalid ID, but due to the
05856        * properties of the rand() function (and the constantcy of s), that
05857        * won't happen twice in a row.
05858        */
05859       while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
05860       session->last_ev = grab_last();
05861       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
05862    }
05863    ao2_unlock(session);
05864 
05865    http_header = ast_str_create(128);
05866    out = ast_str_create(2048);
05867 
05868    ast_mutex_init(&s.lock);
05869 
05870    if (http_header == NULL || out == NULL) {
05871       ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
05872       goto generic_callback_out;
05873    }
05874 
05875    s.session = session;
05876    s.fd = mkstemp(template);  /* create a temporary file for command output */
05877    unlink(template);
05878    if (s.fd <= -1) {
05879       ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
05880       goto generic_callback_out;
05881    }
05882    s.f = fdopen(s.fd, "w+");
05883    if (!s.f) {
05884       ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
05885       ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
05886       close(s.fd);
05887       goto generic_callback_out;
05888    }
05889 
05890    if (method == AST_HTTP_POST) {
05891       params = ast_http_get_post_vars(ser, headers);
05892    }
05893 
05894    for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
05895       hdrlen = strlen(v->name) + strlen(v->value) + 3;
05896       m.headers[m.hdrcount] = ast_malloc(hdrlen);
05897       if (!m.headers[m.hdrcount]) {
05898          /* Allocation failure */
05899          continue;
05900       }
05901       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
05902       ast_debug(1, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
05903       ++m.hdrcount;
05904    }
05905 
05906    if (process_message(&s, &m)) {
05907       if (session->authenticated) {
05908          if (manager_displayconnects(session)) {
05909             ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
05910          }
05911       } else {
05912          if (displayconnects) {
05913             ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
05914          }
05915       }
05916       session->needdestroy = 1;
05917    }
05918 
05919    /* Free request headers. */
05920    for (idx = 0; idx < m.hdrcount; ++idx) {
05921       ast_free((void *) m.headers[idx]);
05922       m.headers[idx] = NULL;
05923    }
05924 
05925    ast_str_append(&http_header, 0,
05926       "Content-type: text/%s\r\n"
05927       "Cache-Control: no-cache;\r\n"
05928       "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
05929       "Pragma: SuppressEvents\r\n",
05930       contenttype[format],
05931       session->managerid, httptimeout);
05932 
05933    if (format == FORMAT_XML) {
05934       ast_str_append(&out, 0, "<ajax-response>\n");
05935    } else if (format == FORMAT_HTML) {
05936       /*
05937        * When handling AMI-over-HTTP in HTML format, we provide a simple form for
05938        * debugging purposes. This HTML code should not be here, we
05939        * should read from some config file...
05940        */
05941 
05942 #define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
05943 #define TEST_STRING \
05944    "<form action=\"manager\" method=\"post\">\n\
05945    Action: <select name=\"action\">\n\
05946       <option value=\"\">-----&gt;</option>\n\
05947       <option value=\"login\">login</option>\n\
05948       <option value=\"command\">Command</option>\n\
05949       <option value=\"waitevent\">waitevent</option>\n\
05950       <option value=\"listcommands\">listcommands</option>\n\
05951    </select>\n\
05952    or <input name=\"action\"><br/>\n\
05953    CLI Command <input name=\"command\"><br>\n\
05954    user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
05955    <input type=\"submit\">\n</form>\n"
05956 
05957       ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
05958       ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
05959       ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
05960       ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
05961    }
05962 
05963    process_output(&s, &out, params, format);
05964 
05965    if (format == FORMAT_XML) {
05966       ast_str_append(&out, 0, "</ajax-response>\n");
05967    } else if (format == FORMAT_HTML) {
05968       ast_str_append(&out, 0, "</table></body>\r\n");
05969    }
05970 
05971    ao2_lock(session);
05972    /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
05973    session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
05974 
05975    if (session->needdestroy) {
05976       if (session->inuse == 1) {
05977          ast_debug(1, "Need destroy, doing it now!\n");
05978          blastaway = 1;
05979       } else {
05980          ast_debug(1, "Need destroy, but can't do it yet!\n");
05981          if (session->waiting_thread != AST_PTHREADT_NULL) {
05982             pthread_kill(session->waiting_thread, SIGURG);
05983          }
05984          session->inuse--;
05985       }
05986    } else {
05987       session->inuse--;
05988    }
05989    ao2_unlock(session);
05990 
05991    ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
05992    http_header = out = NULL;
05993 
05994 generic_callback_out:
05995    ast_mutex_destroy(&s.lock);
05996 
05997    /* Clear resource */
05998 
05999    if (method == AST_HTTP_POST && params) {
06000       ast_variables_destroy(params);
06001    }
06002    if (http_header) {
06003       ast_free(http_header);
06004    }
06005    if (out) {
06006       ast_free(out);
06007    }
06008 
06009    if (session && blastaway) {
06010       session_destroy(session);
06011    } else if (session && session->f) {
06012       fclose(session->f);
06013       session->f = NULL;
06014    }
06015 
06016    return 0;
06017 }
06018 
06019 static int auth_http_callback(struct ast_tcptls_session_instance *ser,
06020                     enum ast_http_method method,
06021                     enum output_format format,
06022                     struct sockaddr_in *remote_address, const char *uri,
06023                     struct ast_variable *get_params,
06024                     struct ast_variable *headers)
06025 {
06026    struct mansession_session *session = NULL;
06027    struct mansession s = { .session = NULL, .tcptls_session = ser };
06028    struct ast_variable *v, *params = get_params;
06029    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
06030    struct ast_str *http_header = NULL, *out = NULL;
06031    size_t result_size = 512;
06032    struct message m = { 0 };
06033    unsigned int idx;
06034    size_t hdrlen;
06035 
06036    time_t time_now = time(NULL);
06037    unsigned long nonce = 0, nc;
06038    struct ast_http_digest d = { NULL, };
06039    struct ast_manager_user *user = NULL;
06040    int stale = 0;
06041    char resp_hash[256]="";
06042    /* Cache for user data */
06043    char u_username[80];
06044    int u_readperm;
06045    int u_writeperm;
06046    int u_writetimeout;
06047    int u_displayconnects;
06048    struct ast_sockaddr addr;
06049 
06050    if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
06051       ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
06052       return -1;
06053    }
06054 
06055    /* Find "Authorization: " header */
06056    for (v = headers; v; v = v->next) {
06057       if (!strcasecmp(v->name, "Authorization")) {
06058          break;
06059       }
06060    }
06061 
06062    if (!v || ast_strlen_zero(v->value)) {
06063       goto out_401; /* Authorization Header not present - send auth request */
06064    }
06065 
06066    /* Digest found - parse */
06067    if (ast_string_field_init(&d, 128)) {
06068       ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
06069       return -1;
06070    }
06071 
06072    if (ast_parse_digest(v->value, &d, 0, 1)) {
06073       /* Error in Digest - send new one */
06074       nonce = 0;
06075       goto out_401;
06076    }
06077    if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
06078       ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
06079       nonce = 0;
06080       goto out_401;
06081    }
06082 
06083    AST_RWLIST_WRLOCK(&users);
06084    user = get_manager_by_name_locked(d.username);
06085    if(!user) {
06086       AST_RWLIST_UNLOCK(&users);
06087       ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
06088       nonce = 0;
06089       goto out_401;
06090    }
06091 
06092    ast_sockaddr_from_sin(&addr, remote_address);
06093    /* --- We have User for this auth, now check ACL */
06094    if (user->ha && !ast_apply_ha(user->ha, &addr)) {
06095       AST_RWLIST_UNLOCK(&users);
06096       ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
06097       ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
06098       return -1;
06099    }
06100 
06101    /* --- We have auth, so check it */
06102 
06103    /* compute the expected response to compare with what we received */
06104    {
06105       char a2[256];
06106       char a2_hash[256];
06107       char resp[256];
06108 
06109       /* XXX Now request method are hardcoded in A2 */
06110       snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri);
06111       ast_md5_hash(a2_hash, a2);
06112 
06113       if (d.qop) {
06114          /* RFC 2617 */
06115          snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
06116       }  else {
06117          /* RFC 2069 */
06118          snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
06119       }
06120       ast_md5_hash(resp_hash, resp);
06121    }
06122 
06123    if (strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
06124       /* Something was wrong, so give the client to try with a new challenge */
06125       AST_RWLIST_UNLOCK(&users);
06126       nonce = 0;
06127       goto out_401;
06128    }
06129 
06130    /*
06131     * User are pass Digest authentication.
06132     * Now, cache the user data and unlock user list.
06133     */
06134    ast_copy_string(u_username, user->username, sizeof(u_username));
06135    u_readperm = user->readperm;
06136    u_writeperm = user->writeperm;
06137    u_displayconnects = user->displayconnects;
06138    u_writetimeout = user->writetimeout;
06139    AST_RWLIST_UNLOCK(&users);
06140 
06141    if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
06142       /*
06143        * Create new session.
06144        * While it is not in the list we don't need any locking
06145        */
06146       if (!(session = build_mansession(*remote_address))) {
06147          ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
06148          return -1;
06149       }
06150       ao2_lock(session);
06151 
06152       ast_copy_string(session->username, u_username, sizeof(session->username));
06153       session->managerid = nonce;
06154       session->last_ev = grab_last();
06155       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
06156 
06157       session->readperm = u_readperm;
06158       session->writeperm = u_writeperm;
06159       session->writetimeout = u_writetimeout;
06160 
06161       if (u_displayconnects) {
06162          ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
06163       }
06164       session->noncetime = session->sessionstart = time_now;
06165       session->authenticated = 1;
06166    } else if (stale) {
06167       /*
06168        * Session found, but nonce is stale.
06169        *
06170        * This could be because an old request (w/old nonce) arrived.
06171        *
06172        * This may be as the result of http proxy usage (separate delay or
06173        * multipath) or in a situation where a page was refreshed too quickly
06174        * (seen in Firefox).
06175        *
06176        * In this situation, we repeat the 401 auth with the current nonce
06177        * value.
06178        */
06179       nonce = session->managerid;
06180       ao2_unlock(session);
06181       stale = 1;
06182       goto out_401;
06183    } else {
06184       sscanf(d.nc, "%30lx", &nc);
06185       if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
06186          /*
06187           * Nonce time expired (> 2 minutes) or something wrong with nonce
06188           * counter.
06189           *
06190           * Create new nonce key and resend Digest auth request. Old nonce
06191           * is saved for stale checking...
06192           */
06193          session->nc = 0; /* Reset nonce counter */
06194          session->oldnonce = session->managerid;
06195          nonce = session->managerid = ast_random();
06196          session->noncetime = time_now;
06197          ao2_unlock(session);
06198          stale = 1;
06199          goto out_401;
06200       } else {
06201          session->nc = nc; /* All OK, save nonce counter */
06202       }
06203    }
06204 
06205 
06206    /* Reset session timeout. */
06207    session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
06208    ao2_unlock(session);
06209 
06210    ast_mutex_init(&s.lock);
06211    s.session = session;
06212    s.fd = mkstemp(template);  /* create a temporary file for command output */
06213    unlink(template);
06214    if (s.fd <= -1) {
06215       ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
06216       goto auth_callback_out;
06217    }
06218    s.f = fdopen(s.fd, "w+");
06219    if (!s.f) {
06220       ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
06221       ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
06222       close(s.fd);
06223       goto auth_callback_out;
06224    }
06225 
06226    if (method == AST_HTTP_POST) {
06227       params = ast_http_get_post_vars(ser, headers);
06228    }
06229 
06230    for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
06231       hdrlen = strlen(v->name) + strlen(v->value) + 3;
06232       m.headers[m.hdrcount] = ast_malloc(hdrlen);
06233       if (!m.headers[m.hdrcount]) {
06234          /* Allocation failure */
06235          continue;
06236       }
06237       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
06238       ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
06239       ++m.hdrcount;
06240    }
06241 
06242    if (process_message(&s, &m)) {
06243       if (u_displayconnects) {
06244          ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
06245       }
06246 
06247       session->needdestroy = 1;
06248    }
06249 
06250    /* Free request headers. */
06251    for (idx = 0; idx < m.hdrcount; ++idx) {
06252       ast_free((void *) m.headers[idx]);
06253       m.headers[idx] = NULL;
06254    }
06255 
06256    if (s.f) {
06257       result_size = ftell(s.f); /* Calculate approx. size of result */
06258    }
06259 
06260    http_header = ast_str_create(80);
06261    out = ast_str_create(result_size * 2 + 512);
06262 
06263    if (http_header == NULL || out == NULL) {
06264       ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
06265       goto auth_callback_out;
06266    }
06267 
06268    ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);
06269 
06270    if (format == FORMAT_XML) {
06271       ast_str_append(&out, 0, "<ajax-response>\n");
06272    } else if (format == FORMAT_HTML) {
06273       ast_str_append(&out, 0,
06274       "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
06275       "<html><head>\r\n"
06276       "<title>Asterisk&trade; Manager Interface</title>\r\n"
06277       "</head><body style=\"background-color: #ffffff;\">\r\n"
06278       "<form method=\"POST\">\r\n"
06279       "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
06280       "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
06281       "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
06282       "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
06283    }
06284 
06285    process_output(&s, &out, params, format);
06286 
06287    if (format == FORMAT_XML) {
06288       ast_str_append(&out, 0, "</ajax-response>\n");
06289    } else if (format == FORMAT_HTML) {
06290       ast_str_append(&out, 0, "</table></form></body></html>\r\n");
06291    }
06292 
06293    ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
06294    http_header = out = NULL;
06295 
06296 auth_callback_out:
06297    ast_mutex_destroy(&s.lock);
06298 
06299    /* Clear resources and unlock manager session */
06300    if (method == AST_HTTP_POST && params) {
06301       ast_variables_destroy(params);
06302    }
06303 
06304    ast_free(http_header);
06305    ast_free(out);
06306 
06307    ao2_lock(session);
06308    if (session->f) {
06309       fclose(session->f);
06310    }
06311    session->f = NULL;
06312    session->fd = -1;
06313    ao2_unlock(session);
06314 
06315    if (session->needdestroy) {
06316       ast_debug(1, "Need destroy, doing it now!\n");
06317       session_destroy(session);
06318    }
06319    ast_string_field_free_memory(&d);
06320    return 0;
06321 
06322 out_401:
06323    if (!nonce) {
06324       nonce = ast_random();
06325    }
06326 
06327    ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
06328    ast_string_field_free_memory(&d);
06329    return 0;
06330 }
06331 
06332 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)
06333 {
06334    int retval;
06335    struct sockaddr_in ser_remote_address_tmp;
06336 
06337    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06338    retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
06339    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06340    return retval;
06341 }
06342 
06343 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)
06344 {
06345    int retval;
06346    struct sockaddr_in ser_remote_address_tmp;
06347 
06348    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06349    retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
06350    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06351    return retval;
06352 }
06353 
06354 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)
06355 {
06356    int retval;
06357    struct sockaddr_in ser_remote_address_tmp;
06358 
06359    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06360    retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
06361    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06362    return retval;
06363 }
06364 
06365 static struct ast_http_uri rawmanuri = {
06366    .description = "Raw HTTP Manager Event Interface",
06367    .uri = "rawman",
06368    .callback = rawman_http_callback,
06369    .data = NULL,
06370    .key = __FILE__,
06371 };
06372 
06373 static struct ast_http_uri manageruri = {
06374    .description = "HTML Manager Event Interface",
06375    .uri = "manager",
06376    .callback = manager_http_callback,
06377    .data = NULL,
06378    .key = __FILE__,
06379 };
06380 
06381 static struct ast_http_uri managerxmluri = {
06382    .description = "XML Manager Event Interface",
06383    .uri = "mxml",
06384    .callback = mxml_http_callback,
06385    .data = NULL,
06386    .key = __FILE__,
06387 };
06388 
06389 
06390 /* Callback with Digest authentication */
06391 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)
06392 {
06393    int retval;
06394    struct sockaddr_in ser_remote_address_tmp;
06395 
06396    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06397    retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
06398    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06399    return retval;
06400 }
06401 
06402 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)
06403 {
06404    int retval;
06405    struct sockaddr_in ser_remote_address_tmp;
06406 
06407    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06408    retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
06409    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06410    return retval;
06411 }
06412 
06413 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)
06414 {
06415    int retval;
06416    struct sockaddr_in ser_remote_address_tmp;
06417 
06418    ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
06419    retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
06420    ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
06421    return retval;
06422 }
06423 
06424 static struct ast_http_uri arawmanuri = {
06425    .description = "Raw HTTP Manager Event Interface w/Digest authentication",
06426    .uri = "arawman",
06427    .has_subtree = 0,
06428    .callback = auth_rawman_http_callback,
06429    .data = NULL,
06430    .key = __FILE__,
06431 };
06432 
06433 static struct ast_http_uri amanageruri = {
06434    .description = "HTML Manager Event Interface w/Digest authentication",
06435    .uri = "amanager",
06436    .has_subtree = 0,
06437    .callback = auth_manager_http_callback,
06438    .data = NULL,
06439    .key = __FILE__,
06440 };
06441 
06442 static struct ast_http_uri amanagerxmluri = {
06443    .description = "XML Manager Event Interface w/Digest authentication",
06444    .uri = "amxml",
06445    .has_subtree = 0,
06446    .callback = auth_mxml_http_callback,
06447    .data = NULL,
06448    .key = __FILE__,
06449 };
06450 
06451 static int registered = 0;
06452 static int webregged = 0;
06453 
06454 /*! \brief cleanup code called at each iteration of server_root,
06455  * guaranteed to happen every 5 seconds at most
06456  */
06457 static void purge_old_stuff(void *data)
06458 {
06459    purge_sessions(1);
06460    purge_events();
06461 }
06462 
06463 static struct ast_tls_config ami_tls_cfg;
06464 static struct ast_tcptls_session_args ami_desc = {
06465    .accept_fd = -1,
06466    .master = AST_PTHREADT_NULL,
06467    .tls_cfg = NULL,
06468    .poll_timeout = 5000,   /* wake up every 5 seconds */
06469    .periodic_fn = purge_old_stuff,
06470    .name = "AMI server",
06471    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
06472    .worker_fn = session_do,   /* thread handling the session */
06473 };
06474 
06475 static struct ast_tcptls_session_args amis_desc = {
06476    .accept_fd = -1,
06477    .master = AST_PTHREADT_NULL,
06478    .tls_cfg = &ami_tls_cfg,
06479    .poll_timeout = -1,  /* the other does the periodic cleanup */
06480    .name = "AMI TLS server",
06481    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
06482    .worker_fn = session_do,   /* thread handling the session */
06483 };
06484 
06485 /*! \brief CLI command manager show settings */
06486 static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
06487 {
06488    switch (cmd) {
06489    case CLI_INIT:
06490       e->command = "manager show settings";
06491       e->usage =
06492          "Usage: manager show settings\n"
06493          "       Provides detailed list of the configuration of the Manager.\n";
06494       return NULL;
06495    case CLI_GENERATE:
06496       return NULL;
06497    }
06498 #define FORMAT "  %-25.25s  %-15.15s\n"
06499 #define FORMAT2 "  %-25.25s  %-15d\n"
06500    if (a->argc != 3) {
06501       return CLI_SHOWUSAGE;
06502    }
06503    ast_cli(a->fd, "\nGlobal Settings:\n");
06504    ast_cli(a->fd, "----------------\n");
06505    ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
06506    ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
06507    ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
06508    ast_cli(a->fd, FORMAT2, "HTTP Timeout (minutes):", httptimeout);
06509    ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
06510    ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
06511    ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
06512    ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
06513    ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
06514    ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
06515    ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
06516    ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
06517    ast_cli(a->fd, FORMAT, "Channel vars:", S_OR(manager_channelvars, ""));
06518    ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
06519    ast_cli(a->fd, FORMAT, "Block sockets:", AST_CLI_YESNO(block_sockets));
06520 #undef FORMAT
06521 #undef FORMAT2
06522 
06523    return CLI_SUCCESS;
06524 }
06525 
06526 static struct ast_cli_entry cli_manager[] = {
06527    AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
06528    AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
06529    AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
06530    AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
06531    AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
06532    AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
06533    AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
06534    AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
06535    AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
06536 };
06537 
06538 /*!
06539  * \internal
06540  * \brief Load the config channelvars variable.
06541  *
06542  * \param var Config variable to load.
06543  *
06544  * \return Nothing
06545  */
06546 static void load_channelvars(struct ast_variable *var)
06547 {
06548    struct manager_channel_variable *mcv;
06549    char *remaining = ast_strdupa(var->value);
06550    char *next;
06551 
06552    ast_free(manager_channelvars);
06553    manager_channelvars = ast_strdup(var->value);
06554 
06555    /*
06556     * XXX TODO: To allow dialplan functions to have more than one
06557     * parameter requires eliminating the '|' as a separator so we
06558     * could use AST_STANDARD_APP_ARGS() to separate items.
06559     */
06560    free_channelvars();
06561    AST_RWLIST_WRLOCK(&channelvars);
06562    while ((next = strsep(&remaining, ",|"))) {
06563       if (!(mcv = ast_calloc(1, sizeof(*mcv) + strlen(next) + 1))) {
06564          break;
06565       }
06566       strcpy(mcv->name, next); /* SAFE */
06567       if (strchr(next, '(')) {
06568          mcv->isfunc = 1;
06569       }
06570       AST_RWLIST_INSERT_TAIL(&channelvars, mcv, entry);
06571    }
06572    AST_RWLIST_UNLOCK(&channelvars);
06573 }
06574 
06575 static int __init_manager(int reload)
06576 {
06577    struct ast_config *ucfg = NULL, *cfg = NULL;
06578    const char *val;
06579    char *cat = NULL;
06580    int newhttptimeout = DEFAULT_HTTPTIMEOUT;
06581    struct ast_manager_user *user = NULL;
06582    struct ast_variable *var;
06583    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06584    char a1[256];
06585    char a1_hash[256];
06586    struct sockaddr_in ami_desc_local_address_tmp = { 0, };
06587    struct sockaddr_in amis_desc_local_address_tmp = { 0, };
06588    int tls_was_enabled = 0;
06589 
06590    if (!registered) {
06591       /* Register default actions */
06592       ast_manager_register_xml("Ping", 0, action_ping);
06593       ast_manager_register_xml("Events", 0, action_events);
06594       ast_manager_register_xml("Logoff", 0, action_logoff);
06595       ast_manager_register_xml("Login", 0, action_login);
06596       ast_manager_register_xml("Challenge", 0, action_challenge);
06597       ast_manager_register_xml("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup);
06598       ast_manager_register_xml("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status);
06599       ast_manager_register_xml("Setvar", EVENT_FLAG_CALL, action_setvar);
06600       ast_manager_register_xml("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar);
06601       ast_manager_register_xml("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig);
06602       ast_manager_register_xml("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson);
06603       ast_manager_register_xml("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig);
06604       ast_manager_register_xml("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig);
06605       ast_manager_register_xml("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
06606       ast_manager_register_xml("Redirect", EVENT_FLAG_CALL, action_redirect);
06607       ast_manager_register_xml("Atxfer", EVENT_FLAG_CALL, action_atxfer);
06608       ast_manager_register_xml("Originate", EVENT_FLAG_ORIGINATE, action_originate);
06609       ast_manager_register_xml("Command", EVENT_FLAG_COMMAND, action_command);
06610       ast_manager_register_xml("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
06611       ast_manager_register_xml("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
06612       ast_manager_register_xml("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
06613       ast_manager_register_xml("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
06614       ast_manager_register_xml("ListCommands", 0, action_listcommands);
06615       ast_manager_register_xml("SendText", EVENT_FLAG_CALL, action_sendtext);
06616       ast_manager_register_xml("UserEvent", EVENT_FLAG_USER, action_userevent);
06617       ast_manager_register_xml("WaitEvent", 0, action_waitevent);
06618       ast_manager_register_xml("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings);
06619       ast_manager_register_xml("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus);
06620       ast_manager_register_xml("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload);
06621       ast_manager_register_xml("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
06622       ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
06623       ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
06624       ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
06625 
06626       ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
06627       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
06628       registered = 1;
06629       /* Append placeholder event so master_eventq never runs dry */
06630       append_event("Event: Placeholder\r\n\r\n", 0);
06631    }
06632    if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
06633       return 0;
06634    }
06635 
06636    manager_enabled = DEFAULT_ENABLED;
06637    webmanager_enabled = DEFAULT_WEBENABLED;
06638    manager_debug = DEFAULT_MANAGERDEBUG;
06639    displayconnects = DEFAULT_DISPLAYCONNECTS;
06640    broken_events_action = DEFAULT_BROKENEVENTSACTION;
06641    block_sockets = DEFAULT_BLOCKSOCKETS;
06642    timestampevents = DEFAULT_TIMESTAMPEVENTS;
06643    httptimeout = DEFAULT_HTTPTIMEOUT;
06644    authtimeout = DEFAULT_AUTHTIMEOUT;
06645    authlimit = DEFAULT_AUTHLIMIT;
06646 
06647    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
06648       ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
06649       return 0;
06650    }
06651 
06652    /* default values */
06653    ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
06654    ast_sockaddr_setnull(&ami_desc.local_address);
06655    ast_sockaddr_setnull(&amis_desc.local_address);
06656 
06657    ami_desc_local_address_tmp.sin_family = AF_INET;
06658    amis_desc_local_address_tmp.sin_family = AF_INET;
06659 
06660    ami_desc_local_address_tmp.sin_port = htons(DEFAULT_MANAGER_PORT);
06661 
06662    tls_was_enabled = (reload && ami_tls_cfg.enabled);
06663 
06664    ami_tls_cfg.enabled = 0;
06665    if (ami_tls_cfg.certfile) {
06666       ast_free(ami_tls_cfg.certfile);
06667    }
06668    ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
06669    if (ami_tls_cfg.pvtfile) {
06670       ast_free(ami_tls_cfg.pvtfile);
06671    }
06672    ami_tls_cfg.pvtfile = ast_strdup("");
06673    if (ami_tls_cfg.cipher) {
06674       ast_free(ami_tls_cfg.cipher);
06675    }
06676    ami_tls_cfg.cipher = ast_strdup("");
06677 
06678    free_channelvars();
06679 
06680    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
06681       val = var->value;
06682 
06683       if (!ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
06684          continue;
06685       }
06686 
06687       if (!strcasecmp(var->name, "enabled")) {
06688          manager_enabled = ast_true(val);
06689       } else if (!strcasecmp(var->name, "block-sockets")) {
06690          block_sockets = ast_true(val);
06691       } else if (!strcasecmp(var->name, "webenabled")) {
06692          webmanager_enabled = ast_true(val);
06693       } else if (!strcasecmp(var->name, "port")) {
06694          ami_desc_local_address_tmp.sin_port = htons(atoi(val));
06695       } else if (!strcasecmp(var->name, "bindaddr")) {
06696          if (!inet_aton(val, &ami_desc_local_address_tmp.sin_addr)) {
06697             ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
06698             memset(&ami_desc_local_address_tmp.sin_addr, 0,
06699                    sizeof(ami_desc_local_address_tmp.sin_addr));
06700          }
06701       } else if (!strcasecmp(var->name, "brokeneventsaction")) {
06702          broken_events_action = ast_true(val);
06703       } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
06704          allowmultiplelogin = ast_true(val);
06705       } else if (!strcasecmp(var->name, "displayconnects")) {
06706          displayconnects = ast_true(val);
06707       } else if (!strcasecmp(var->name, "timestampevents")) {
06708          timestampevents = ast_true(val);
06709       } else if (!strcasecmp(var->name, "debug")) {
06710          manager_debug = ast_true(val);
06711       } else if (!strcasecmp(var->name, "httptimeout")) {
06712          newhttptimeout = atoi(val);
06713       } else if (!strcasecmp(var->name, "authtimeout")) {
06714          int timeout = atoi(var->value);
06715 
06716          if (timeout < 1) {
06717             ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
06718          } else {
06719             authtimeout = timeout;
06720          }
06721       } else if (!strcasecmp(var->name, "authlimit")) {
06722          int limit = atoi(var->value);
06723 
06724          if (limit < 1) {
06725             ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
06726          } else {
06727             authlimit = limit;
06728          }
06729       } else if (!strcasecmp(var->name, "channelvars")) {
06730          load_channelvars(var);
06731       } else {
06732          ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
06733             var->name, val);
06734       }
06735    }
06736 
06737    ast_sockaddr_to_sin(&amis_desc.local_address, &amis_desc_local_address_tmp);
06738 
06739    /* if the amis address has not been set, default is the same as non secure ami */
06740    if (!amis_desc_local_address_tmp.sin_addr.s_addr) {
06741       amis_desc_local_address_tmp.sin_addr =
06742           ami_desc_local_address_tmp.sin_addr;
06743    }
06744 
06745    if (!amis_desc_local_address_tmp.sin_port) {
06746       amis_desc_local_address_tmp.sin_port = htons(DEFAULT_MANAGER_TLS_PORT);
06747    }
06748 
06749    if (manager_enabled) {
06750       ast_sockaddr_from_sin(&ami_desc.local_address, &ami_desc_local_address_tmp);
06751       ast_sockaddr_from_sin(&amis_desc.local_address, &amis_desc_local_address_tmp);
06752    }
06753 
06754    AST_RWLIST_WRLOCK(&users);
06755 
06756    /* First, get users from users.conf */
06757    ucfg = ast_config_load2("users.conf", "manager", config_flags);
06758    if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
06759       const char *hasmanager;
06760       int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
06761 
06762       while ((cat = ast_category_browse(ucfg, cat))) {
06763          if (!strcasecmp(cat, "general")) {
06764             continue;
06765          }
06766 
06767          hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
06768          if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
06769             const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
06770             const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
06771             const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
06772             const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
06773             const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
06774 
06775             /* Look for an existing entry,
06776              * if none found - create one and add it to the list
06777              */
06778             if (!(user = get_manager_by_name_locked(cat))) {
06779                if (!(user = ast_calloc(1, sizeof(*user)))) {
06780                   break;
06781                }
06782 
06783                /* Copy name over */
06784                ast_copy_string(user->username, cat, sizeof(user->username));
06785                /* Insert into list */
06786                AST_LIST_INSERT_TAIL(&users, user, list);
06787                user->ha = NULL;
06788                user->keep = 1;
06789                user->readperm = -1;
06790                user->writeperm = -1;
06791                /* Default displayconnect from [general] */
06792                user->displayconnects = displayconnects;
06793                user->writetimeout = 100;
06794             }
06795 
06796             if (!user_secret) {
06797                user_secret = ast_variable_retrieve(ucfg, "general", "secret");
06798             }
06799             if (!user_read) {
06800                user_read = ast_variable_retrieve(ucfg, "general", "read");
06801             }
06802             if (!user_write) {
06803                user_write = ast_variable_retrieve(ucfg, "general", "write");
06804             }
06805             if (!user_displayconnects) {
06806                user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
06807             }
06808             if (!user_writetimeout) {
06809                user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
06810             }
06811 
06812             if (!ast_strlen_zero(user_secret)) {
06813                if (user->secret) {
06814                   ast_free(user->secret);
06815                }
06816                user->secret = ast_strdup(user_secret);
06817             }
06818 
06819             if (user_read) {
06820                user->readperm = get_perm(user_read);
06821             }
06822             if (user_write) {
06823                user->writeperm = get_perm(user_write);
06824             }
06825             if (user_displayconnects) {
06826                user->displayconnects = ast_true(user_displayconnects);
06827             }
06828             if (user_writetimeout) {
06829                int value = atoi(user_writetimeout);
06830                if (value < 100) {
06831                   ast_log(LOG_WARNING, "Invalid writetimeout value '%d' in users.conf\n", value);
06832                } else {
06833                   user->writetimeout = value;
06834                }
06835             }
06836          }
06837       }
06838       ast_config_destroy(ucfg);
06839    }
06840 
06841    /* cat is NULL here in any case */
06842 
06843    while ((cat = ast_category_browse(cfg, cat))) {
06844       struct ast_ha *oldha;
06845 
06846       if (!strcasecmp(cat, "general")) {
06847          continue;
06848       }
06849 
06850       /* Look for an existing entry, if none found - create one and add it to the list */
06851       if (!(user = get_manager_by_name_locked(cat))) {
06852          if (!(user = ast_calloc(1, sizeof(*user)))) {
06853             break;
06854          }
06855          /* Copy name over */
06856          ast_copy_string(user->username, cat, sizeof(user->username));
06857 
06858          user->ha = NULL;
06859          user->readperm = 0;
06860          user->writeperm = 0;
06861          /* Default displayconnect from [general] */
06862          user->displayconnects = displayconnects;
06863          user->writetimeout = 100;
06864          user->whitefilters = ao2_container_alloc(1, NULL, NULL);
06865          user->blackfilters = ao2_container_alloc(1, NULL, NULL);
06866 
06867          /* Insert into list */
06868          AST_RWLIST_INSERT_TAIL(&users, user, list);
06869       } else {
06870          ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
06871          ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
06872       }
06873 
06874       /* Make sure we keep this user and don't destroy it during cleanup */
06875       user->keep = 1;
06876       oldha = user->ha;
06877       user->ha = NULL;
06878 
06879       var = ast_variable_browse(cfg, cat);
06880       for (; var; var = var->next) {
06881          if (!strcasecmp(var->name, "secret")) {
06882             if (user->secret) {
06883                ast_free(user->secret);
06884             }
06885             user->secret = ast_strdup(var->value);
06886          } else if (!strcasecmp(var->name, "deny") ||
06887                    !strcasecmp(var->name, "permit")) {
06888             user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
06889          }  else if (!strcasecmp(var->name, "read") ) {
06890             user->readperm = get_perm(var->value);
06891          }  else if (!strcasecmp(var->name, "write") ) {
06892             user->writeperm = get_perm(var->value);
06893          }  else if (!strcasecmp(var->name, "displayconnects") ) {
06894             user->displayconnects = ast_true(var->value);
06895          } else if (!strcasecmp(var->name, "writetimeout")) {
06896             int value = atoi(var->value);
06897             if (value < 100) {
06898                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
06899             } else {
06900                user->writetimeout = value;
06901             }
06902          } else if (!strcasecmp(var->name, "eventfilter")) {
06903             const char *value = var->value;
06904             regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
06905             if (new_filter) {
06906                int is_blackfilter;
06907                if (value[0] == '!') {
06908                   is_blackfilter = 1;
06909                   value++;
06910                } else {
06911                   is_blackfilter = 0;
06912                }
06913                if (regcomp(new_filter, value, 0)) {
06914                   ao2_t_ref(new_filter, -1, "failed to make regx");
06915                } else {
06916                   if (is_blackfilter) {
06917                      ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
06918                   } else {
06919                      ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
06920                   }
06921                }
06922             }
06923          } else {
06924             ast_debug(1, "%s is an unknown option.\n", var->name);
06925          }
06926       }
06927       ast_free_ha(oldha);
06928    }
06929    ast_config_destroy(cfg);
06930 
06931    /* Perform cleanup - essentially prune out old users that no longer exist */
06932    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
06933       if (user->keep) { /* valid record. clear flag for the next round */
06934          user->keep = 0;
06935 
06936          /* Calculate A1 for Digest auth */
06937          snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
06938          ast_md5_hash(a1_hash,a1);
06939          if (user->a1_hash) {
06940             ast_free(user->a1_hash);
06941          }
06942          user->a1_hash = ast_strdup(a1_hash);
06943          continue;
06944       }
06945       /* We do not need to keep this user so take them out of the list */
06946       AST_RWLIST_REMOVE_CURRENT(list);
06947       ast_debug(4, "Pruning user '%s'\n", user->username);
06948       /* Free their memory now */
06949       if (user->a1_hash) {
06950          ast_free(user->a1_hash);
06951       }
06952       if (user->secret) {
06953          ast_free(user->secret);
06954       }
06955       ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
06956       ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
06957       ao2_t_ref(user->whitefilters, -1, "decrement ref for white container, should be last one");
06958       ao2_t_ref(user->blackfilters, -1, "decrement ref for black container, should be last one");
06959       ast_free_ha(user->ha);
06960       ast_free(user);
06961    }
06962    AST_RWLIST_TRAVERSE_SAFE_END;
06963 
06964    AST_RWLIST_UNLOCK(&users);
06965 
06966    if (!reload) {
06967       /* If you have a NULL hash fn, you only need a single bucket */
06968       sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn);
06969    }
06970 
06971    if (webmanager_enabled && manager_enabled) {
06972       if (!webregged) {
06973 
06974          ast_http_uri_link(&rawmanuri);
06975          ast_http_uri_link(&manageruri);
06976          ast_http_uri_link(&managerxmluri);
06977 
06978          ast_http_uri_link(&arawmanuri);
06979          ast_http_uri_link(&amanageruri);
06980          ast_http_uri_link(&amanagerxmluri);
06981          webregged = 1;
06982       }
06983    } else {
06984       if (webregged) {
06985          ast_http_uri_unlink(&rawmanuri);
06986          ast_http_uri_unlink(&manageruri);
06987          ast_http_uri_unlink(&managerxmluri);
06988 
06989          ast_http_uri_unlink(&arawmanuri);
06990          ast_http_uri_unlink(&amanageruri);
06991          ast_http_uri_unlink(&amanagerxmluri);
06992          webregged = 0;
06993       }
06994    }
06995 
06996    if (newhttptimeout > 0) {
06997       httptimeout = newhttptimeout;
06998    }
06999 
07000    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
07001 
07002    ast_tcptls_server_start(&ami_desc);
07003    if (tls_was_enabled && !ami_tls_cfg.enabled) {
07004       ast_tcptls_server_stop(&amis_desc);
07005    } else if (ast_ssl_setup(amis_desc.tls_cfg)) {
07006       ast_tcptls_server_start(&amis_desc);
07007    }
07008    return 0;
07009 }
07010 
07011 /* clear out every entry in the channelvar list */
07012 static void free_channelvars(void)
07013 {
07014    struct manager_channel_variable *var;
07015    AST_RWLIST_WRLOCK(&channelvars);
07016    while ((var = AST_RWLIST_REMOVE_HEAD(&channelvars, entry))) {
07017       ast_free(var);
07018    }
07019    AST_RWLIST_UNLOCK(&channelvars);
07020 }
07021 
07022 int init_manager(void)
07023 {
07024    return __init_manager(0);
07025 }
07026 
07027 int reload_manager(void)
07028 {
07029    return __init_manager(1);
07030 }
07031 
07032 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
07033 {
07034    AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
07035 
07036    return 0;
07037 }
07038 
07039 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
07040 {
07041    return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
07042 }
07043 
07044 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
07045 {
07046    struct ast_datastore *datastore = NULL;
07047 
07048    if (info == NULL)
07049       return NULL;
07050 
07051    AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
07052       if (datastore->info != info) {
07053          continue;
07054       }
07055 
07056       if (uid == NULL) {
07057          /* matched by type only */
07058          break;
07059       }
07060 
07061       if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
07062          /* Matched by type AND uid */
07063          break;
07064       }
07065    }
07066    AST_LIST_TRAVERSE_SAFE_END;
07067 
07068    return datastore;
07069 }

Generated on Mon Oct 8 12:39:03 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7