Sat Mar 10 01:54:20 2012

Asterisk developer's documentation


manager.c

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

Generated on Sat Mar 10 01:54:20 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7