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

Generated on Sat Feb 11 06:33:18 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6