Tue Aug 20 16:34:35 2013

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1