Mon Apr 28 2014 10:05:44

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