Wed Nov 28 2012 04:18:12

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