Thu Apr 3 08:20:22 2014

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

Generated on 3 Apr 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1