Mon Sep 20 2010 00:23:10

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 #include "asterisk.h"
00045 
00046 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 257184 $")
00047 
00048 #include "asterisk/_private.h"
00049 #include "asterisk/paths.h"   /* use various ast_config_AST_* */
00050 #include <ctype.h>
00051 #include <sys/time.h>
00052 #include <signal.h>
00053 #include <sys/mman.h>
00054 
00055 #include "asterisk/channel.h"
00056 #include "asterisk/file.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/cli.h"
00063 #include "asterisk/app.h"
00064 #include "asterisk/pbx.h"
00065 #include "asterisk/md5.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/utils.h"
00068 #include "asterisk/tcptls.h"
00069 #include "asterisk/http.h"
00070 #include "asterisk/ast_version.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/linkedlists.h"
00073 #include "asterisk/version.h"
00074 #include "asterisk/term.h"
00075 #include "asterisk/astobj2.h"
00076 #include "asterisk/features.h"
00077 
00078 enum error_type {
00079    UNKNOWN_ACTION = 1,
00080    UNKNOWN_CATEGORY,
00081    UNSPECIFIED_CATEGORY,
00082    UNSPECIFIED_ARGUMENT,
00083    FAILURE_ALLOCATION,
00084    FAILURE_NEWCAT,
00085    FAILURE_DELCAT,
00086    FAILURE_EMPTYCAT,
00087    FAILURE_UPDATE,
00088    FAILURE_DELETE,
00089    FAILURE_APPEND
00090 };
00091 
00092 
00093 /*!
00094  * Linked list of events.
00095  * Global events are appended to the list by append_event().
00096  * The usecount is the number of stored pointers to the element,
00097  * excluding the list pointers. So an element that is only in
00098  * the list has a usecount of 0, not 1.
00099  *
00100  * Clients have a pointer to the last event processed, and for each
00101  * of these clients we track the usecount of the elements.
00102  * If we have a pointer to an entry in the list, it is safe to navigate
00103  * it forward because elements will not be deleted, but only appended.
00104  * The worst that can happen is seeing the pointer still NULL.
00105  *
00106  * When the usecount of an element drops to 0, and the element is the
00107  * first in the list, we can remove it. Removal is done within the
00108  * main thread, which is woken up for the purpose.
00109  *
00110  * For simplicity of implementation, we make sure the list is never empty.
00111  */
00112 struct eventqent {
00113    int usecount;     /*!< # of clients who still need the event */
00114    int category;
00115    unsigned int seq; /*!< sequence number */
00116    AST_LIST_ENTRY(eventqent) eq_next;
00117    char eventdata[1];   /*!< really variable size, allocated by append_event() */
00118 };
00119 
00120 static AST_LIST_HEAD_STATIC(all_events, eventqent);
00121 
00122 static int displayconnects = 1;
00123 static int allowmultiplelogin = 1;
00124 static int timestampevents;
00125 static int httptimeout = 60;
00126 static int broken_events_action = 0;
00127 static int manager_enabled = 0;
00128 static int webmanager_enabled = 0;
00129 
00130 static int block_sockets;
00131 static int num_sessions;
00132 
00133 static int manager_debug;  /*!< enable some debugging code in the manager */
00134 
00135 /*! \brief
00136  * Descriptor for a manager session, either on the AMI socket or over HTTP.
00137  *
00138  * \note
00139  * AMI session have managerid == 0; the entry is created upon a connect,
00140  * and destroyed with the socket.
00141  * HTTP sessions have managerid != 0, the value is used as a search key
00142  * to lookup sessions (using the mansession_id cookie).
00143  */
00144 #define MAX_BLACKLIST_CMD_LEN 2
00145 static struct {
00146    char *words[AST_MAX_CMD_LEN];
00147 } command_blacklist[] = {
00148    {{ "module", "load", NULL }},
00149    {{ "module", "unload", NULL }},
00150    {{ "restart", "gracefully", NULL }},
00151 };
00152 
00153 /* In order to understand what the heck is going on with the
00154  * mansession_session and mansession structs, we need to have a bit of a history
00155  * lesson.
00156  *
00157  * In the beginning, there was the mansession. The mansession contained data that was
00158  * intrinsic to a manager session, such as the time that it started, the name of the logged-in
00159  * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
00160  * sessions, these were used to represent the TCP socket over which the AMI session was taking
00161  * place. It makes perfect sense for these fields to be a part of the session-specific data since
00162  * the session actually defines this information.
00163  *
00164  * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
00165  * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
00166  * but rather to the action that is being executed. Because a single session may execute many commands
00167  * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
00168  * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
00169  * has had a chance to properly close its handles.
00170  *
00171  * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
00172  * from being run at the same time in a single session. Some manager actions may block for a long time, thus
00173  * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
00174  * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
00175  * part of the action instead.
00176  *
00177  * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
00178  * contain the action-specific information, such as which file to write to. In order to maintain expectations
00179  * of action handlers and not have to change the public API of the manager code, we would need to name this
00180  * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
00181  * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
00182  * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
00183  * data.
00184  */
00185 struct mansession_session {
00186    pthread_t ms_t;      /*!< Execution thread, basically useless */
00187    ast_mutex_t __lock;  /*!< Thread lock -- don't use in action callbacks, it's already taken care of  */
00188             /* XXX need to document which fields it is protecting */
00189    struct sockaddr_in sin; /*!< address we are connecting from */
00190    FILE *f;    /*!< fdopen() on the underlying fd */
00191    int fd;        /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
00192    int inuse;     /*!< number of HTTP sessions using this entry */
00193    int needdestroy;  /*!< Whether an HTTP session should be destroyed */
00194    pthread_t waiting_thread;  /*!< Sleeping thread using this descriptor */
00195    uint32_t managerid;  /*!< Unique manager identifier, 0 for AMI sessions */
00196    time_t sessionstart;    /*!< Session start time */
00197    time_t sessiontimeout;  /*!< Session timeout if HTTP */
00198    char username[80];   /*!< Logged in username */
00199    char challenge[10];  /*!< Authentication challenge */
00200    int authenticated;   /*!< Authentication status */
00201    int readperm;     /*!< Authorization for reading */
00202    int writeperm;    /*!< Authorization for writing */
00203    char inbuf[1025]; /*!< Buffer */
00204             /* we use the extra byte to add a '\0' and simplify parsing */
00205    int inlen;     /*!< number of buffered bytes */
00206    int send_events;  /*!<  XXX what ? */
00207    struct eventqent *last_ev; /*!< last event processed. */
00208    int writetimeout; /*!< Timeout for ast_carefulwrite() */
00209    int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
00210    AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
00211    AST_LIST_ENTRY(mansession_session) list;
00212 };
00213 
00214 /* In case you didn't read that giant block of text above the mansession_session struct, the
00215  * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
00216  * represents data that is different from Manager action to Manager action. The mansession_session pointer
00217  * contained within points to session-specific data.
00218  */
00219 struct mansession {
00220    struct mansession_session *session;
00221    FILE *f;
00222    int fd;
00223 };
00224 
00225 #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
00226 
00227 static AST_LIST_HEAD_STATIC(sessions, mansession_session);
00228 
00229 /*! \brief user descriptor, as read from the config file.
00230  *
00231  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
00232  * lines which are not supported here, and readperm/writeperm/writetimeout
00233  * are not stored.
00234  */
00235 struct ast_manager_user {
00236    char username[80];
00237    char *secret;
00238    struct ast_ha *ha;      /*!< ACL setting */
00239    int readperm;        /*! Authorization for reading */
00240    int writeperm;       /*! Authorization for writing */
00241    int writetimeout;    /*! Per user Timeout for ast_carefulwrite() */
00242    int displayconnects; /*!< XXX unused */
00243    int keep;   /*!< mark entries created on a reload */
00244    AST_RWLIST_ENTRY(ast_manager_user) list;
00245 };
00246 
00247 /*! \brief list of users found in the config file */
00248 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
00249 
00250 /*! \brief list of actions registered */
00251 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
00252 
00253 /*! \brief list of hooks registered */
00254 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
00255 
00256 /*! \brief Add a custom hook to be called when an event is fired */
00257 void ast_manager_register_hook(struct manager_custom_hook *hook)
00258 {
00259    AST_RWLIST_WRLOCK(&manager_hooks);
00260    AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
00261    AST_RWLIST_UNLOCK(&manager_hooks);
00262    return;
00263 }
00264 
00265 /*! \brief Delete a custom hook to be called when an event is fired */
00266 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
00267 {
00268    AST_RWLIST_WRLOCK(&manager_hooks);
00269    AST_RWLIST_REMOVE(&manager_hooks, hook, list);
00270    AST_RWLIST_UNLOCK(&manager_hooks);
00271    return;
00272 }
00273 
00274 /*! \brief
00275  * Event list management functions.
00276  * We assume that the event list always has at least one element,
00277  * and the delete code will not remove the last entry even if the
00278  * 
00279  */
00280 #if 0
00281 static time_t __deb(time_t start, const char *msg)
00282 {
00283    time_t now = time(NULL);
00284    ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
00285    if (start != 0 && now - start > 5)
00286       ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
00287    return now;
00288 }
00289 
00290 static void LOCK_EVENTS(void)
00291 {
00292    time_t start = __deb(0, "about to lock events");
00293    AST_LIST_LOCK(&all_events);
00294    __deb(start, "done lock events");
00295 }
00296 
00297 static void UNLOCK_EVENTS(void)
00298 {
00299    __deb(0, "about to unlock events");
00300    AST_LIST_UNLOCK(&all_events);
00301 }
00302 
00303 static void LOCK_SESS(void)
00304 {
00305    time_t start = __deb(0, "about to lock sessions");
00306    AST_LIST_LOCK(&sessions);
00307    __deb(start, "done lock sessions");
00308 }
00309 
00310 static void UNLOCK_SESS(void)
00311 {
00312    __deb(0, "about to unlock sessions");
00313    AST_LIST_UNLOCK(&sessions);
00314 }
00315 #endif
00316 
00317 int check_manager_enabled()
00318 {
00319    return manager_enabled;
00320 }
00321 
00322 int check_webmanager_enabled()
00323 {
00324    return (webmanager_enabled && manager_enabled);
00325 }
00326 
00327 /*!
00328  * Grab a reference to the last event, update usecount as needed.
00329  * Can handle a NULL pointer.
00330  */
00331 static struct eventqent *grab_last(void)
00332 {
00333    struct eventqent *ret;
00334 
00335    AST_LIST_LOCK(&all_events);
00336    ret = AST_LIST_LAST(&all_events);
00337    /* the list is never empty now, but may become so when
00338     * we optimize it in the future, so be prepared.
00339     */
00340    if (ret)
00341       ast_atomic_fetchadd_int(&ret->usecount, 1);
00342    AST_LIST_UNLOCK(&all_events);
00343    return ret;
00344 }
00345 
00346 /*!
00347  * Purge unused events. Remove elements from the head
00348  * as long as their usecount is 0 and there is a next element.
00349  */
00350 static void purge_events(void)
00351 {
00352    struct eventqent *ev;
00353 
00354    AST_LIST_LOCK(&all_events);
00355    while ( (ev = AST_LIST_FIRST(&all_events)) &&
00356        ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
00357       AST_LIST_REMOVE_HEAD(&all_events, eq_next);
00358       ast_free(ev);
00359    }
00360    AST_LIST_UNLOCK(&all_events);
00361 }
00362 
00363 /*!
00364  * helper functions to convert back and forth between
00365  * string and numeric representation of set of flags
00366  */
00367 static struct permalias {
00368    int num;
00369    char *label;
00370 } perms[] = {
00371    { EVENT_FLAG_SYSTEM, "system" },
00372    { EVENT_FLAG_CALL, "call" },
00373    { EVENT_FLAG_LOG, "log" },
00374    { EVENT_FLAG_VERBOSE, "verbose" },
00375    { EVENT_FLAG_COMMAND, "command" },
00376    { EVENT_FLAG_AGENT, "agent" },
00377    { EVENT_FLAG_USER, "user" },
00378    { EVENT_FLAG_CONFIG, "config" },
00379    { EVENT_FLAG_DTMF, "dtmf" },
00380    { EVENT_FLAG_REPORTING, "reporting" },
00381    { EVENT_FLAG_CDR, "cdr" },
00382    { EVENT_FLAG_DIALPLAN, "dialplan" },
00383    { EVENT_FLAG_ORIGINATE, "originate" },
00384    { EVENT_FLAG_AGI, "agi" },
00385    { INT_MAX, "all" },
00386    { 0, "none" },
00387 };
00388 
00389 /*! \brief Convert authority code to a list of options */
00390 static char *authority_to_str(int authority, struct ast_str **res)
00391 {
00392    int i;
00393    char *sep = "";
00394 
00395    ast_str_reset(*res);
00396    for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
00397       if (authority & perms[i].num) {
00398          ast_str_append(res, 0, "%s%s", sep, perms[i].label);
00399          sep = ",";
00400       }
00401    }
00402 
00403    if (ast_str_strlen(*res) == 0)   /* replace empty string with something sensible */
00404       ast_str_append(res, 0, "<none>");
00405 
00406    return ast_str_buffer(*res);
00407 }
00408 
00409 /*! Tells you if smallstr exists inside bigstr
00410    which is delim by delim and uses no buf or stringsep
00411    ast_instring("this|that|more","this",'|') == 1;
00412 
00413    feel free to move this to app.c -anthm */
00414 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
00415 {
00416    const char *val = bigstr, *next;
00417 
00418    do {
00419       if ((next = strchr(val, delim))) {
00420          if (!strncmp(val, smallstr, (next - val)))
00421             return 1;
00422          else
00423             continue;
00424       } else
00425          return !strcmp(smallstr, val);
00426    } while (*(val = (next + 1)));
00427 
00428    return 0;
00429 }
00430 
00431 static int get_perm(const char *instr)
00432 {
00433    int x = 0, ret = 0;
00434 
00435    if (!instr)
00436       return 0;
00437 
00438    for (x = 0; x < ARRAY_LEN(perms); x++) {
00439       if (ast_instring(instr, perms[x].label, ','))
00440          ret |= perms[x].num;
00441    }
00442 
00443    return ret;
00444 }
00445 
00446 /*!
00447  * A number returns itself, false returns 0, true returns all flags,
00448  * other strings return the flags that are set.
00449  */
00450 static int strings_to_mask(const char *string)
00451 {
00452    const char *p;
00453 
00454    if (ast_strlen_zero(string))
00455       return -1;
00456 
00457    for (p = string; *p; p++)
00458       if (*p < '0' || *p > '9')
00459          break;
00460    if (!*p) /* all digits */
00461       return atoi(string);
00462    if (ast_false(string))
00463       return 0;
00464    if (ast_true(string)) { /* all permissions */
00465       int x, ret = 0;
00466       for (x = 0; x < ARRAY_LEN(perms); x++)
00467          ret |= perms[x].num;
00468       return ret;
00469    }
00470    return get_perm(string);
00471 }
00472 
00473 static int check_manager_session_inuse(const char *name)
00474 {
00475    struct mansession_session *session = NULL;
00476 
00477    AST_LIST_LOCK(&sessions);
00478    AST_LIST_TRAVERSE(&sessions, session, list) {
00479       if (!strcasecmp(session->username, name)) 
00480          break;
00481    }
00482    AST_LIST_UNLOCK(&sessions);
00483 
00484    return session ? 1 : 0;
00485 }
00486 
00487 
00488 /*!
00489  * lookup an entry in the list of registered users.
00490  * must be called with the list lock held.
00491  */
00492 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
00493 {
00494    struct ast_manager_user *user = NULL;
00495 
00496    AST_RWLIST_TRAVERSE(&users, user, list)
00497       if (!strcasecmp(user->username, name))
00498          break;
00499    return user;
00500 }
00501 
00502 /*! \brief Get displayconnects config option.
00503  *  \param session manager session to get parameter from.
00504  *  \return displayconnects config option value.
00505  */
00506 static int manager_displayconnects (struct mansession_session *session)
00507 {
00508    struct ast_manager_user *user = NULL;
00509    int ret = 0;
00510 
00511    AST_RWLIST_RDLOCK(&users);
00512    if ((user = get_manager_by_name_locked (session->username)))
00513       ret = user->displayconnects;
00514    AST_RWLIST_UNLOCK(&users);
00515    
00516    return ret;
00517 }
00518 
00519 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00520 {
00521    struct manager_action *cur;
00522    struct ast_str *authority;
00523    int num, l, which;
00524    char *ret = NULL;
00525    switch (cmd) {
00526    case CLI_INIT:
00527       e->command = "manager show command";
00528       e->usage = 
00529          "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
00530          "  Shows the detailed description for a specific Asterisk manager interface command.\n";
00531       return NULL;
00532    case CLI_GENERATE:
00533       l = strlen(a->word);
00534       which = 0;
00535       AST_RWLIST_RDLOCK(&actions);
00536       AST_RWLIST_TRAVERSE(&actions, cur, list) {
00537          if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
00538             ret = ast_strdup(cur->action);
00539             break;   /* make sure we exit even if ast_strdup() returns NULL */
00540          }
00541       }
00542       AST_RWLIST_UNLOCK(&actions);
00543       return ret;
00544    }
00545    authority = ast_str_alloca(80);
00546    if (a->argc < 4) {
00547       return CLI_SHOWUSAGE;
00548    }
00549 
00550    AST_RWLIST_RDLOCK(&actions);
00551    AST_RWLIST_TRAVERSE(&actions, cur, list) {
00552       for (num = 3; num < a->argc; num++) {
00553          if (!strcasecmp(cur->action, a->argv[num])) {
00554             ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
00555                cur->action, cur->synopsis,
00556                authority_to_str(cur->authority, &authority),
00557                S_OR(cur->description, ""));
00558          }
00559       }
00560    }
00561    AST_RWLIST_UNLOCK(&actions);
00562 
00563    return CLI_SUCCESS;
00564 }
00565 
00566 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00567 {
00568    switch (cmd) {
00569    case CLI_INIT:
00570       e->command = "manager set debug [on|off]";
00571       e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
00572       return NULL;
00573    case CLI_GENERATE:
00574       return NULL;   
00575    }
00576    if (a->argc == 3)
00577       ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
00578    else if (a->argc == 4) {
00579       if (!strcasecmp(a->argv[3], "on"))
00580          manager_debug = 1;
00581       else if (!strcasecmp(a->argv[3], "off"))
00582          manager_debug = 0;
00583       else
00584          return CLI_SHOWUSAGE;
00585    }
00586    return CLI_SUCCESS;
00587 }
00588 
00589 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00590 {
00591    struct ast_manager_user *user = NULL;
00592    int l, which;
00593    char *ret = NULL;
00594    struct ast_str *rauthority = ast_str_alloca(128);
00595    struct ast_str *wauthority = ast_str_alloca(128);
00596 
00597    switch (cmd) {
00598    case CLI_INIT:
00599       e->command = "manager show user";
00600       e->usage = 
00601          " Usage: manager show user <user>\n"
00602          "        Display all information related to the manager user specified.\n";
00603       return NULL;
00604    case CLI_GENERATE:
00605       l = strlen(a->word);
00606       which = 0;
00607       if (a->pos != 3)
00608          return NULL;
00609       AST_RWLIST_RDLOCK(&users);
00610       AST_RWLIST_TRAVERSE(&users, user, list) {
00611          if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
00612             ret = ast_strdup(user->username);
00613             break;
00614          }
00615       }
00616       AST_RWLIST_UNLOCK(&users);
00617       return ret;
00618    }
00619 
00620    if (a->argc != 4)
00621       return CLI_SHOWUSAGE;
00622 
00623    AST_RWLIST_RDLOCK(&users);
00624 
00625    if (!(user = get_manager_by_name_locked(a->argv[3]))) {
00626       ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
00627       AST_RWLIST_UNLOCK(&users);
00628       return CLI_SUCCESS;
00629    }
00630 
00631    ast_cli(a->fd, "\n");
00632    ast_cli(a->fd,
00633       "       username: %s\n"
00634       "         secret: %s\n"
00635       "            acl: %s\n"
00636       "      read perm: %s\n"
00637       "     write perm: %s\n"
00638       "displayconnects: %s\n",
00639       (user->username ? user->username : "(N/A)"),
00640       (user->secret ? "<Set>" : "(N/A)"),
00641       (user->ha ? "yes" : "no"),
00642       authority_to_str(user->readperm, &rauthority),
00643       authority_to_str(user->writeperm, &wauthority),
00644       (user->displayconnects ? "yes" : "no"));
00645 
00646    AST_RWLIST_UNLOCK(&users);
00647 
00648    return CLI_SUCCESS;
00649 }
00650 
00651 
00652 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00653 {
00654    struct ast_manager_user *user = NULL;
00655    int count_amu = 0;
00656    switch (cmd) {
00657    case CLI_INIT:
00658       e->command = "manager show users";
00659       e->usage = 
00660          "Usage: manager show users\n"
00661          "       Prints a listing of all managers that are currently configured on that\n"
00662          " system.\n";
00663       return NULL;
00664    case CLI_GENERATE:
00665       return NULL;
00666    }
00667    if (a->argc != 3)
00668       return CLI_SHOWUSAGE;
00669 
00670    AST_RWLIST_RDLOCK(&users);
00671 
00672    /* If there are no users, print out something along those lines */
00673    if (AST_RWLIST_EMPTY(&users)) {
00674       ast_cli(a->fd, "There are no manager users.\n");
00675       AST_RWLIST_UNLOCK(&users);
00676       return CLI_SUCCESS;
00677    }
00678 
00679    ast_cli(a->fd, "\nusername\n--------\n");
00680 
00681    AST_RWLIST_TRAVERSE(&users, user, list) {
00682       ast_cli(a->fd, "%s\n", user->username);
00683       count_amu++;
00684    }
00685 
00686    AST_RWLIST_UNLOCK(&users);
00687 
00688    ast_cli(a->fd, "-------------------\n");
00689    ast_cli(a->fd, "%d manager users configured.\n", count_amu);
00690 
00691    return CLI_SUCCESS;
00692 }
00693 
00694 
00695 /*! \brief  CLI command  manager list commands */
00696 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00697 {
00698    struct manager_action *cur;
00699    struct ast_str *authority;
00700 #define HSMC_FORMAT "  %-15.15s  %-15.15s  %-55.55s\n"
00701    switch (cmd) {
00702    case CLI_INIT:
00703       e->command = "manager show commands";
00704       e->usage = 
00705          "Usage: manager show commands\n"
00706          "  Prints a listing of all the available Asterisk manager interface commands.\n";
00707       return NULL;
00708    case CLI_GENERATE:
00709       return NULL;   
00710    }  
00711    authority = ast_str_alloca(80);
00712    ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
00713    ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
00714 
00715    AST_RWLIST_RDLOCK(&actions);
00716    AST_RWLIST_TRAVERSE(&actions, cur, list)
00717       ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
00718    AST_RWLIST_UNLOCK(&actions);
00719 
00720    return CLI_SUCCESS;
00721 }
00722 
00723 /*! \brief CLI command manager list connected */
00724 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00725 {
00726    struct mansession_session *session;
00727    time_t now = time(NULL);
00728 #define HSMCONN_FORMAT1 "  %-15.15s  %-15.15s  %-10.10s  %-10.10s  %-8.8s  %-8.8s  %-5.5s  %-5.5s\n"
00729 #define HSMCONN_FORMAT2 "  %-15.15s  %-15.15s  %-10d  %-10d  %-8d  %-8d  %-5.5d  %-5.5d\n"
00730    int count = 0;
00731    switch (cmd) {
00732    case CLI_INIT:
00733       e->command = "manager show connected";
00734       e->usage = 
00735          "Usage: manager show connected\n"
00736          "  Prints a listing of the users that are currently connected to the\n"
00737          "Asterisk manager interface.\n";
00738       return NULL;
00739    case CLI_GENERATE:
00740       return NULL;   
00741    }
00742 
00743    ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
00744 
00745    AST_LIST_LOCK(&sessions);
00746    AST_LIST_TRAVERSE(&sessions, session, list) {
00747       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);
00748       count++;
00749    }
00750    AST_LIST_UNLOCK(&sessions);
00751 
00752    ast_cli(a->fd, "%d users connected.\n", count);
00753 
00754    return CLI_SUCCESS;
00755 }
00756 
00757 /*! \brief CLI command manager list eventq */
00758 /* Should change to "manager show connected" */
00759 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00760 {
00761    struct eventqent *s;
00762    switch (cmd) {
00763    case CLI_INIT:
00764       e->command = "manager show eventq";
00765       e->usage = 
00766          "Usage: manager show eventq\n"
00767          "  Prints a listing of all events pending in the Asterisk manger\n"
00768          "event queue.\n";
00769       return NULL;
00770    case CLI_GENERATE:
00771       return NULL;
00772    }
00773    AST_LIST_LOCK(&all_events);
00774    AST_LIST_TRAVERSE(&all_events, s, eq_next) {
00775       ast_cli(a->fd, "Usecount: %d\n", s->usecount);
00776       ast_cli(a->fd, "Category: %d\n", s->category);
00777       ast_cli(a->fd, "Event:\n%s", s->eventdata);
00778    }
00779    AST_LIST_UNLOCK(&all_events);
00780 
00781    return CLI_SUCCESS;
00782 }
00783 
00784 /*! \brief CLI command manager reload */
00785 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00786 {
00787    switch (cmd) {
00788    case CLI_INIT:
00789       e->command = "manager reload";
00790       e->usage =
00791          "Usage: manager reload\n"
00792          "       Reloads the manager configuration.\n";
00793       return NULL;
00794    case CLI_GENERATE:
00795       return NULL;
00796    }
00797    if (a->argc > 2)
00798       return CLI_SHOWUSAGE;
00799    reload_manager();
00800    return CLI_SUCCESS;
00801 }
00802 
00803 
00804 static struct ast_cli_entry cli_manager[] = {
00805    AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
00806    AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
00807    AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
00808    AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
00809    AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
00810    AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
00811    AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
00812    AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
00813 };
00814 
00815 /*
00816  * Decrement the usecount for the event; if it goes to zero,
00817  * (why check for e->next ?) wakeup the
00818  * main thread, which is in charge of freeing the record.
00819  * Returns the next record.
00820  */
00821 static struct eventqent *unref_event(struct eventqent *e)
00822 {
00823    ast_atomic_fetchadd_int(&e->usecount, -1);
00824    return AST_LIST_NEXT(e, eq_next);
00825 }
00826 
00827 static void ref_event(struct eventqent *e)
00828 {
00829    ast_atomic_fetchadd_int(&e->usecount, 1);
00830 }
00831 
00832 /*
00833  * destroy a session, leaving the usecount
00834  */
00835 static void free_session(struct mansession_session *session)
00836 {
00837    struct eventqent *eqe = session->last_ev;
00838    struct ast_datastore *datastore;
00839 
00840    /* Get rid of each of the data stores on the session */
00841    while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
00842       /* Free the data store */
00843       ast_datastore_free(datastore);
00844    }
00845 
00846    if (session->f != NULL)
00847       fclose(session->f);
00848    ast_mutex_destroy(&session->__lock);
00849    ast_free(session);
00850    unref_event(eqe);
00851 }
00852 
00853 static void destroy_session(struct mansession_session *session)
00854 {
00855    AST_LIST_LOCK(&sessions);
00856    AST_LIST_REMOVE(&sessions, session, list);
00857    ast_atomic_fetchadd_int(&num_sessions, -1);
00858    free_session(session);
00859    AST_LIST_UNLOCK(&sessions);
00860 }
00861 
00862 /*
00863  * Generic function to return either the first or the last matching header
00864  * from a list of variables, possibly skipping empty strings.
00865  * At the moment there is only one use of this function in this file,
00866  * so we make it static.
00867  */
00868 #define  GET_HEADER_FIRST_MATCH  0
00869 #define  GET_HEADER_LAST_MATCH   1
00870 #define  GET_HEADER_SKIP_EMPTY   2
00871 static const char *__astman_get_header(const struct message *m, char *var, int mode)
00872 {
00873    int x, l = strlen(var);
00874    const char *result = "";
00875 
00876    for (x = 0; x < m->hdrcount; x++) {
00877       const char *h = m->headers[x];
00878       if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
00879          const char *value = h + l + 2;
00880          /* found a potential candidate */
00881          if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
00882             continue;   /* not interesting */
00883          if (mode & GET_HEADER_LAST_MATCH)
00884             result = value;   /* record the last match so far */
00885          else
00886             return value;
00887       }
00888    }
00889 
00890    return "";
00891 }
00892 
00893 /*
00894  * Return the first matching variable from an array.
00895  * This is the legacy function and is implemented in therms of
00896  * __astman_get_header().
00897  */
00898 const char *astman_get_header(const struct message *m, char *var)
00899 {
00900    return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
00901 }
00902 
00903 
00904 struct ast_variable *astman_get_variables(const struct message *m)
00905 {
00906    int varlen, x, y;
00907    struct ast_variable *head = NULL, *cur;
00908 
00909    AST_DECLARE_APP_ARGS(args,
00910       AST_APP_ARG(vars)[32];
00911    );
00912 
00913    varlen = strlen("Variable: ");
00914 
00915    for (x = 0; x < m->hdrcount; x++) {
00916       char *parse, *var, *val;
00917 
00918       if (strncasecmp("Variable: ", m->headers[x], varlen))
00919          continue;
00920       parse = ast_strdupa(m->headers[x] + varlen);
00921 
00922       AST_STANDARD_APP_ARGS(args, parse);
00923       if (!args.argc)
00924          continue;
00925       for (y = 0; y < args.argc; y++) {
00926          if (!args.vars[y])
00927             continue;
00928          var = val = ast_strdupa(args.vars[y]);
00929          strsep(&val, "=");
00930          if (!val || ast_strlen_zero(var))
00931             continue;
00932          cur = ast_variable_new(var, val, "");
00933          cur->next = head;
00934          head = cur;
00935       }
00936    }
00937 
00938    return head;
00939 }
00940 
00941 /*!
00942  * helper function to send a string to the socket.
00943  * Return -1 on error (e.g. buffer full).
00944  */
00945 static int send_string(struct mansession *s, char *string)
00946 {
00947    if (s->f) {
00948       return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
00949    } else {
00950       return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
00951    }
00952 }
00953 
00954 /*!
00955  * \brief thread local buffer for astman_append
00956  *
00957  * \note This can not be defined within the astman_append() function
00958  *       because it declares a couple of functions that get used to
00959  *       initialize the thread local storage key.
00960  */
00961 AST_THREADSTORAGE(astman_append_buf);
00962 AST_THREADSTORAGE(userevent_buf);
00963 
00964 /*! \brief initial allocated size for the astman_append_buf */
00965 #define ASTMAN_APPEND_BUF_INITSIZE   256
00966 
00967 /*!
00968  * utility functions for creating AMI replies
00969  */
00970 void astman_append(struct mansession *s, const char *fmt, ...)
00971 {
00972    va_list ap;
00973    struct ast_str *buf;
00974 
00975    if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
00976       return;
00977 
00978    va_start(ap, fmt);
00979    ast_str_set_va(&buf, 0, fmt, ap);
00980    va_end(ap);
00981 
00982    if (s->f != NULL || s->session->f != NULL) {
00983       send_string(s, ast_str_buffer(buf));
00984    } else {
00985       ast_verbose("fd == -1 in astman_append, should not happen\n");
00986    }
00987 }
00988 
00989 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
00990    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
00991    hold the session lock _or_ be running in an action callback (in which case s->session->busy will
00992    be non-zero). In either of these cases, there is no need to lock-protect the session's
00993    fd, since no other output will be sent (events will be queued), and no input will
00994    be read until either the current action finishes or get_input() obtains the session
00995    lock.
00996  */
00997 
00998 /*! \brief send a response with an optional message,
00999  * and terminate it with an empty line.
01000  * m is used only to grab the 'ActionID' field.
01001  *
01002  * Use the explicit constant MSG_MOREDATA to remove the empty line.
01003  * XXX MSG_MOREDATA should go to a header file.
01004  */
01005 #define MSG_MOREDATA ((char *)astman_send_response)
01006 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
01007 {
01008    const char *id = astman_get_header(m, "ActionID");
01009 
01010    astman_append(s, "Response: %s\r\n", resp);
01011    if (!ast_strlen_zero(id))
01012       astman_append(s, "ActionID: %s\r\n", id);
01013    if (listflag)
01014       astman_append(s, "Eventlist: %s\r\n", listflag);   /* Start, complete, cancelled */
01015    if (msg == MSG_MOREDATA)
01016       return;
01017    else if (msg)
01018       astman_append(s, "Message: %s\r\n\r\n", msg);
01019    else
01020       astman_append(s, "\r\n");
01021 }
01022 
01023 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
01024 {
01025    astman_send_response_full(s, m, resp, msg, NULL);
01026 }
01027 
01028 void astman_send_error(struct mansession *s, const struct message *m, char *error)
01029 {
01030    astman_send_response_full(s, m, "Error", error, NULL);
01031 }
01032 
01033 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
01034 {
01035    astman_send_response_full(s, m, "Success", msg, NULL);
01036 }
01037 
01038 static void astman_start_ack(struct mansession *s, const struct message *m)
01039 {
01040    astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
01041 }
01042 
01043 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
01044 {
01045    astman_send_response_full(s, m, "Success", msg, listflag);
01046 }
01047 
01048 
01049 /*! \brief
01050    Rather than braindead on,off this now can also accept a specific int mask value
01051    or a ',' delim list of mask strings (the same as manager.conf) -anthm
01052 */
01053 static int set_eventmask(struct mansession *s, const char *eventmask)
01054 {
01055    int maskint = strings_to_mask(eventmask);
01056 
01057    ast_mutex_lock(&s->session->__lock);
01058    if (maskint >= 0)
01059       s->session->send_events = maskint;
01060    ast_mutex_unlock(&s->session->__lock);
01061 
01062    return maskint;
01063 }
01064 
01065 /*
01066  * Here we start with action_ handlers for AMI actions,
01067  * and the internal functions used by them.
01068  * Generally, the handlers are called action_foo()
01069  */
01070 
01071 /* helper function for action_login() */
01072 static int authenticate(struct mansession *s, const struct message *m)
01073 {
01074    const char *username = astman_get_header(m, "Username");
01075    const char *password = astman_get_header(m, "Secret");
01076    int error = -1;
01077    struct ast_manager_user *user = NULL;
01078 
01079    if (ast_strlen_zero(username))   /* missing username */
01080       return -1;
01081 
01082    /* locate user in locked state */
01083    AST_RWLIST_WRLOCK(&users);
01084 
01085    if (!(user = get_manager_by_name_locked(username))) {
01086       ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01087    } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
01088       ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01089    } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
01090       const char *key = astman_get_header(m, "Key");
01091       if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
01092          int x;
01093          int len = 0;
01094          char md5key[256] = "";
01095          struct MD5Context md5;
01096          unsigned char digest[16];
01097 
01098          MD5Init(&md5);
01099          MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
01100          MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
01101          MD5Final(digest, &md5);
01102          for (x = 0; x < 16; x++)
01103             len += sprintf(md5key + len, "%2.2x", digest[x]);
01104          if (!strcmp(md5key, key))
01105             error = 0;
01106       } else {
01107          ast_debug(1, "MD5 authentication is not possible.  challenge: '%s'\n", 
01108             S_OR(s->session->challenge, ""));
01109       }
01110    } else if (password && user->secret && !strcmp(password, user->secret))
01111       error = 0;
01112 
01113    if (error) {
01114       ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
01115       AST_RWLIST_UNLOCK(&users);
01116       return -1;
01117    }
01118 
01119    /* auth complete */
01120    
01121    ast_copy_string(s->session->username, username, sizeof(s->session->username));
01122    s->session->readperm = user->readperm;
01123    s->session->writeperm = user->writeperm;
01124    s->session->writetimeout = user->writetimeout;
01125    s->session->sessionstart = time(NULL);
01126    set_eventmask(s, astman_get_header(m, "Events"));
01127    
01128    AST_RWLIST_UNLOCK(&users);
01129    return 0;
01130 }
01131 
01132 /*! \brief Manager PING */
01133 static char mandescr_ping[] =
01134 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
01135 "  manager connection open.\n"
01136 "Variables: NONE\n";
01137 
01138 static int action_ping(struct mansession *s, const struct message *m)
01139 {
01140    const char *actionid = astman_get_header(m, "ActionID");
01141 
01142    astman_append(s, "Response: Success\r\n");
01143    if (!ast_strlen_zero(actionid)){
01144       astman_append(s, "ActionID: %s\r\n", actionid);
01145    }
01146    astman_append(s, "Ping: Pong\r\n\r\n");
01147    return 0;
01148 }
01149 
01150 static char mandescr_getconfig[] =
01151 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01152 "file by category and contents or optionally by specified category only.\n"
01153 "Variables: (Names marked with * are required)\n"
01154 "   *Filename: Configuration filename (e.g. foo.conf)\n"
01155 "   Category: Category in configuration file\n";
01156 
01157 static int action_getconfig(struct mansession *s, const struct message *m)
01158 {
01159    struct ast_config *cfg;
01160    const char *fn = astman_get_header(m, "Filename");
01161    const char *category = astman_get_header(m, "Category");
01162    int catcount = 0;
01163    int lineno = 0;
01164    char *cur_category = NULL;
01165    struct ast_variable *v;
01166    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01167 
01168    if (ast_strlen_zero(fn)) {
01169       astman_send_error(s, m, "Filename not specified");
01170       return 0;
01171    }
01172    cfg = ast_config_load2(fn, "manager", config_flags);
01173    if (cfg == CONFIG_STATUS_FILEMISSING) {
01174       astman_send_error(s, m, "Config file not found");
01175       return 0;
01176    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01177       astman_send_error(s, m, "Config file has invalid format");
01178       return 0;
01179    }
01180 
01181    astman_start_ack(s, m);
01182    while ((cur_category = ast_category_browse(cfg, cur_category))) {
01183       if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
01184          lineno = 0;
01185          astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
01186          for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
01187             astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01188          catcount++;
01189       }
01190    }
01191    if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
01192       astman_append(s, "No categories found\r\n");
01193    ast_config_destroy(cfg);
01194    astman_append(s, "\r\n");
01195 
01196    return 0;
01197 }
01198 
01199 static char mandescr_listcategories[] =
01200 "Description: A 'ListCategories' action will dump the categories in\n"
01201 "a given file.\n"
01202 "Variables:\n"
01203 "   Filename: Configuration filename (e.g. foo.conf)\n";
01204 
01205 static int action_listcategories(struct mansession *s, const struct message *m)
01206 {
01207    struct ast_config *cfg;
01208    const char *fn = astman_get_header(m, "Filename");
01209    char *category = NULL;
01210    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01211    int catcount = 0;
01212 
01213    if (ast_strlen_zero(fn)) {
01214       astman_send_error(s, m, "Filename not specified");
01215       return 0;
01216    }
01217    if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
01218       astman_send_error(s, m, "Config file not found");
01219       return 0;
01220    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01221       astman_send_error(s, m, "Config file has invalid format");
01222       return 0;
01223    }
01224    astman_start_ack(s, m);
01225    while ((category = ast_category_browse(cfg, category))) {
01226       astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01227       catcount++;
01228    }
01229    if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
01230       astman_append(s, "Error: no categories found\r\n");
01231    ast_config_destroy(cfg);
01232    astman_append(s, "\r\n");
01233 
01234    return 0;
01235 }
01236 
01237 
01238    
01239 
01240 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
01241 static void json_escape(char *out, const char *in)
01242 {
01243    for (; *in; in++) {
01244       if (*in == '\\' || *in == '\"')
01245          *out++ = '\\';
01246       *out++ = *in;
01247    }
01248    *out = '\0';
01249 }
01250 
01251 static char mandescr_getconfigjson[] =
01252 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
01253 "file by category and contents in JSON format.  This only makes sense to be used\n"
01254 "using rawman over the HTTP interface.\n"
01255 "Variables:\n"
01256 "   Filename: Configuration filename (e.g. foo.conf)\n";
01257 
01258 static int action_getconfigjson(struct mansession *s, const struct message *m)
01259 {
01260    struct ast_config *cfg;
01261    const char *fn = astman_get_header(m, "Filename");
01262    char *category = NULL;
01263    struct ast_variable *v;
01264    int comma1 = 0;
01265    char *buf = NULL;
01266    unsigned int buf_len = 0;
01267    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01268 
01269    if (ast_strlen_zero(fn)) {
01270       astman_send_error(s, m, "Filename not specified");
01271       return 0;
01272    }
01273 
01274    if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
01275       astman_send_error(s, m, "Config file not found");
01276       return 0;
01277    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01278       astman_send_error(s, m, "Config file has invalid format");
01279       return 0;
01280    }
01281 
01282    buf_len = 512;
01283    buf = alloca(buf_len);
01284 
01285    astman_start_ack(s, m);
01286    astman_append(s, "JSON: {");
01287    while ((category = ast_category_browse(cfg, category))) {
01288       int comma2 = 0;
01289       if (buf_len < 2 * strlen(category) + 1) {
01290          buf_len *= 2;
01291          buf = alloca(buf_len);
01292       }
01293       json_escape(buf, category);
01294       astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
01295       if (!comma1)
01296          comma1 = 1;
01297       for (v = ast_variable_browse(cfg, category); v; v = v->next) {
01298          if (comma2)
01299             astman_append(s, ",");
01300          if (buf_len < 2 * strlen(v->name) + 1) {
01301             buf_len *= 2;
01302             buf = alloca(buf_len);
01303          }
01304          json_escape(buf, v->name);
01305          astman_append(s, "\"%s", buf);
01306          if (buf_len < 2 * strlen(v->value) + 1) {
01307             buf_len *= 2;
01308             buf = alloca(buf_len);
01309          }
01310          json_escape(buf, v->value);
01311          astman_append(s, "%s\"", buf);
01312          if (!comma2)
01313             comma2 = 1;
01314       }
01315       astman_append(s, "]");
01316    }
01317    astman_append(s, "}\r\n\r\n");
01318 
01319    ast_config_destroy(cfg);
01320 
01321    return 0;
01322 }
01323 
01324 /* helper function for action_updateconfig */
01325 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
01326 {
01327    int x;
01328    char hdr[40];
01329    const char *action, *cat, *var, *value, *match, *line;
01330    struct ast_category *category;
01331    struct ast_variable *v;
01332    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
01333    enum error_type result = 0;
01334 
01335    for (x = 0; x < 100000; x++) {   /* 100000 = the max number of allowed updates + 1 */
01336       unsigned int object = 0;
01337 
01338       snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01339       action = astman_get_header(m, hdr);
01340       if (ast_strlen_zero(action))     /* breaks the for loop if no action header */
01341          break;                        /* this could cause problems if actions come in misnumbered */
01342 
01343       snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01344       cat = astman_get_header(m, hdr);
01345       if (ast_strlen_zero(cat)) {      /* every action needs a category */
01346          result =  UNSPECIFIED_CATEGORY;
01347          break;
01348       }
01349 
01350       snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01351       var = astman_get_header(m, hdr);
01352 
01353       snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01354       value = astman_get_header(m, hdr);
01355 
01356       if (!ast_strlen_zero(value) && *value == '>') {
01357          object = 1;
01358          value++;
01359       }
01360    
01361       snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01362       match = astman_get_header(m, hdr);
01363 
01364       snprintf(hdr, sizeof(hdr), "Line-%06d", x);
01365       line = astman_get_header(m, hdr);
01366 
01367       if (!strcasecmp(action, "newcat")) {
01368          if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
01369             result = FAILURE_NEWCAT;   /* already exist */
01370             break;
01371          }
01372          if (!(category = ast_category_new(cat, dfn, -1))) {
01373             result = FAILURE_ALLOCATION;
01374             break;
01375          }
01376          if (ast_strlen_zero(match)) {
01377             ast_category_append(cfg, category);
01378          } else
01379             ast_category_insert(cfg, category, match);
01380       } else if (!strcasecmp(action, "renamecat")) {
01381          if (ast_strlen_zero(value)) {
01382             result = UNSPECIFIED_ARGUMENT;
01383             break;
01384          }
01385          if (!(category = ast_category_get(cfg, cat))) {
01386             result = UNKNOWN_CATEGORY;
01387             break;
01388          }
01389          ast_category_rename(category, value);
01390       } else if (!strcasecmp(action, "delcat")) {
01391          if (ast_category_delete(cfg, cat)) {
01392             result = FAILURE_DELCAT;
01393             break;
01394          }
01395       } else if (!strcasecmp(action, "emptycat")) {
01396          if (ast_category_empty(cfg, cat)) {
01397             result = FAILURE_EMPTYCAT;
01398             break;
01399          }
01400       } else if (!strcasecmp(action, "update")) {
01401          if (ast_strlen_zero(var)) {
01402             result = UNSPECIFIED_ARGUMENT;
01403             break;
01404          }
01405          if (!(category = ast_category_get(cfg,cat))) {
01406             result = UNKNOWN_CATEGORY;
01407             break;
01408          }
01409          if (ast_variable_update(category, var, value, match, object)) {
01410             result = FAILURE_UPDATE;
01411             break;
01412          }
01413       } else if (!strcasecmp(action, "delete")) {
01414          if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
01415             result = UNSPECIFIED_ARGUMENT;
01416             break;
01417          }
01418          if (!(category = ast_category_get(cfg, cat))) {
01419             result = UNKNOWN_CATEGORY;
01420             break;
01421          }
01422          if (ast_variable_delete(category, var, match, line)) {
01423             result = FAILURE_DELETE;
01424             break;
01425          }
01426       } else if (!strcasecmp(action, "append")) {
01427          if (ast_strlen_zero(var)) {
01428             result = UNSPECIFIED_ARGUMENT;
01429             break;
01430          }
01431          if (!(category = ast_category_get(cfg, cat))) {
01432             result = UNKNOWN_CATEGORY; 
01433             break;
01434          }
01435          if (!(v = ast_variable_new(var, value, dfn))) {
01436             result = FAILURE_ALLOCATION;
01437             break;
01438          }
01439          if (object || (match && !strcasecmp(match, "object")))
01440             v->object = 1;
01441          ast_variable_append(category, v);
01442       } else if (!strcasecmp(action, "insert")) {
01443          if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
01444             result = UNSPECIFIED_ARGUMENT;
01445             break;
01446          }
01447          if (!(category = ast_category_get(cfg, cat))) {
01448             result = UNKNOWN_CATEGORY;
01449             break;
01450          }
01451          if (!(v = ast_variable_new(var, value, dfn))) {
01452             result = FAILURE_ALLOCATION;
01453             break;
01454          }
01455          ast_variable_insert(category, v, line);
01456       }
01457       else {
01458          ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
01459          result = UNKNOWN_ACTION;
01460          break;
01461       }
01462    }
01463    ast_free(str1);
01464    ast_free(str2);
01465    return result;
01466 }
01467 
01468 static char mandescr_updateconfig[] =
01469 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
01470 "configuration elements in Asterisk configuration files.\n"
01471 "Variables (X's represent 6 digit number beginning with 000000):\n"
01472 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
01473 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
01474 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
01475 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
01476 "   Cat-XXXXXX:    Category to operate on\n"
01477 "   Var-XXXXXX:    Variable to work on\n"
01478 "   Value-XXXXXX:  Value to work on\n"
01479 "   Match-XXXXXX:  Extra match required to match line\n"
01480 "   Line-XXXXXX:   Line in category to operate on (used with delete and insert actions)\n";
01481 
01482 static int action_updateconfig(struct mansession *s, const struct message *m)
01483 {
01484    struct ast_config *cfg;
01485    const char *sfn = astman_get_header(m, "SrcFilename");
01486    const char *dfn = astman_get_header(m, "DstFilename");
01487    int res;
01488    const char *rld = astman_get_header(m, "Reload");
01489    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
01490    enum error_type result;
01491 
01492    if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01493       astman_send_error(s, m, "Filename not specified");
01494       return 0;
01495    }
01496    if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
01497       astman_send_error(s, m, "Config file not found");
01498       return 0;
01499    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01500       astman_send_error(s, m, "Config file has invalid format");
01501       return 0;
01502    }
01503    result = handle_updates(s, m, cfg, dfn);
01504    if (!result) {
01505       ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
01506       res = ast_config_text_file_save(dfn, cfg, "Manager");
01507       ast_config_destroy(cfg);
01508       if (res) {
01509          astman_send_error(s, m, "Save of config failed");
01510          return 0;
01511       }
01512       astman_send_ack(s, m, NULL);
01513       if (!ast_strlen_zero(rld)) {
01514          if (ast_true(rld))
01515             rld = NULL;
01516          ast_module_reload(rld);
01517       }
01518    } else {
01519       ast_config_destroy(cfg);
01520       switch(result) {
01521       case UNKNOWN_ACTION:
01522          astman_send_error(s, m, "Unknown action command");
01523          break;
01524       case UNKNOWN_CATEGORY:
01525          astman_send_error(s, m, "Given category does not exist");
01526          break;
01527       case UNSPECIFIED_CATEGORY:
01528          astman_send_error(s, m, "Category not specified");
01529          break;
01530       case UNSPECIFIED_ARGUMENT:
01531          astman_send_error(s, m, "Problem with category, value, or line (if required)");
01532          break;
01533       case FAILURE_ALLOCATION:
01534          astman_send_error(s, m, "Memory allocation failure, this should not happen");
01535          break;
01536       case FAILURE_NEWCAT:
01537          astman_send_error(s, m, "Create category did not complete successfully");
01538          break;
01539       case FAILURE_DELCAT:
01540          astman_send_error(s, m, "Delete category did not complete successfully");
01541          break;
01542       case FAILURE_EMPTYCAT:
01543          astman_send_error(s, m, "Empty category did not complete successfully");
01544          break;
01545       case FAILURE_UPDATE:
01546          astman_send_error(s, m, "Update did not complete successfully");
01547          break;
01548       case FAILURE_DELETE:
01549          astman_send_error(s, m, "Delete did not complete successfully");
01550          break;
01551       case FAILURE_APPEND:
01552          astman_send_error(s, m, "Append did not complete successfully");
01553          break;
01554       }
01555    }
01556    return 0;
01557 }
01558 
01559 static char mandescr_createconfig[] =
01560 "Description: A 'CreateConfig' action will create an empty file in the\n"
01561 "configuration directory. This action is intended to be used before an\n"
01562 "UpdateConfig action.\n"
01563 "Variables\n"
01564 "   Filename:   The configuration filename to create (e.g. foo.conf)\n";
01565 
01566 static int action_createconfig(struct mansession *s, const struct message *m)
01567 {
01568    int fd;
01569    const char *fn = astman_get_header(m, "Filename");
01570    struct ast_str *filepath = ast_str_alloca(PATH_MAX);
01571    ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
01572    ast_str_append(&filepath, 0, "%s", fn);
01573 
01574    if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
01575       close(fd);
01576       astman_send_ack(s, m, "New configuration file created successfully");
01577    } else {
01578       astman_send_error(s, m, strerror(errno));
01579    }
01580 
01581    return 0;
01582 }
01583 
01584 /*! \brief Manager WAITEVENT */
01585 static char mandescr_waitevent[] =
01586 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
01587 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
01588 "session, events will be generated and queued.\n"
01589 "Variables: \n"
01590 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
01591 
01592 static int action_waitevent(struct mansession *s, const struct message *m)
01593 {
01594    const char *timeouts = astman_get_header(m, "Timeout");
01595    int timeout = -1;
01596    int x;
01597    int needexit = 0;
01598    const char *id = astman_get_header(m, "ActionID");
01599    char idText[256];
01600 
01601    if (!ast_strlen_zero(id))
01602       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01603    else
01604       idText[0] = '\0';
01605 
01606    if (!ast_strlen_zero(timeouts)) {
01607       sscanf(timeouts, "%30i", &timeout);
01608       if (timeout < -1)
01609          timeout = -1;
01610       /* XXX maybe put an upper bound, or prevent the use of 0 ? */
01611    }
01612 
01613    ast_mutex_lock(&s->session->__lock);
01614    if (s->session->waiting_thread != AST_PTHREADT_NULL)
01615       pthread_kill(s->session->waiting_thread, SIGURG);
01616 
01617    if (s->session->managerid) { /* AMI-over-HTTP session */
01618       /*
01619        * Make sure the timeout is within the expire time of the session,
01620        * as the client will likely abort the request if it does not see
01621        * data coming after some amount of time.
01622        */
01623       time_t now = time(NULL);
01624       int max = s->session->sessiontimeout - now - 10;
01625 
01626       if (max < 0)   /* We are already late. Strange but possible. */
01627          max = 0;
01628       if (timeout < 0 || timeout > max)
01629          timeout = max;
01630       if (!s->session->send_events) /* make sure we record events */
01631          s->session->send_events = -1;
01632    }
01633    ast_mutex_unlock(&s->session->__lock);
01634 
01635    /* XXX should this go inside the lock ? */
01636    s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
01637    ast_debug(1, "Starting waiting for an event!\n");
01638 
01639    for (x = 0; x < timeout || timeout < 0; x++) {
01640       ast_mutex_lock(&s->session->__lock);
01641       if (NEW_EVENT(s))
01642          needexit = 1;
01643       /* We can have multiple HTTP session point to the same mansession entry.
01644        * The way we deal with it is not very nice: newcomers kick out the previous
01645        * HTTP session. XXX this needs to be improved.
01646        */
01647       if (s->session->waiting_thread != pthread_self())
01648          needexit = 1;
01649       if (s->session->needdestroy)
01650          needexit = 1;
01651       ast_mutex_unlock(&s->session->__lock);
01652       if (needexit)
01653          break;
01654       if (s->session->managerid == 0) {   /* AMI session */
01655          if (ast_wait_for_input(s->session->fd, 1000))
01656             break;
01657       } else { /* HTTP session */
01658          sleep(1);
01659       }
01660    }
01661    ast_debug(1, "Finished waiting for an event!\n");
01662    ast_mutex_lock(&s->session->__lock);
01663    if (s->session->waiting_thread == pthread_self()) {
01664       struct eventqent *eqe;
01665       astman_send_response(s, m, "Success", "Waiting for Event completed.");
01666       while ( (eqe = NEW_EVENT(s)) ) {
01667          ref_event(eqe);
01668          if (((s->session->readperm & eqe->category) == eqe->category) &&
01669              ((s->session->send_events & eqe->category) == eqe->category)) {
01670             astman_append(s, "%s", eqe->eventdata);
01671          }
01672          s->session->last_ev = unref_event(s->session->last_ev);
01673       }
01674       astman_append(s,
01675          "Event: WaitEventComplete\r\n"
01676          "%s"
01677          "\r\n", idText);
01678       s->session->waiting_thread = AST_PTHREADT_NULL;
01679    } else {
01680       ast_debug(1, "Abandoning event request!\n");
01681    }
01682    ast_mutex_unlock(&s->session->__lock);
01683    return 0;
01684 }
01685 
01686 static char mandescr_listcommands[] =
01687 "Description: Returns the action name and synopsis for every\n"
01688 "  action that is available to the user\n"
01689 "Variables: NONE\n";
01690 
01691 /*! \note The actionlock is read-locked by the caller of this function */
01692 static int action_listcommands(struct mansession *s, const struct message *m)
01693 {
01694    struct manager_action *cur;
01695    struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
01696 
01697    astman_start_ack(s, m);
01698    AST_RWLIST_TRAVERSE(&actions, cur, list) {
01699       if (s->session->writeperm & cur->authority || cur->authority == 0)
01700          astman_append(s, "%s: %s (Priv: %s)\r\n",
01701             cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
01702    }
01703    astman_append(s, "\r\n");
01704 
01705    return 0;
01706 }
01707 
01708 static char mandescr_events[] =
01709 "Description: Enable/Disable sending of events to this manager\n"
01710 "  client.\n"
01711 "Variables:\n"
01712 "  EventMask: 'on' if all events should be sent,\n"
01713 "     'off' if no events should be sent,\n"
01714 "     'system,call,log' to select which flags events should have to be sent.\n";
01715 
01716 static int action_events(struct mansession *s, const struct message *m)
01717 {
01718    const char *mask = astman_get_header(m, "EventMask");
01719    int res, x;
01720 
01721    res = set_eventmask(s, mask);
01722    if (broken_events_action) {
01723       /* if this option is set we should not return a response on
01724        * error, or when all events are set */
01725 
01726       if (res > 0) {
01727          for (x = 0; x < ARRAY_LEN(perms); x++) {
01728             if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
01729                return 0;
01730             }
01731          }
01732          astman_append(s, "Response: Success\r\n"
01733                 "Events: On\r\n\r\n");
01734       } else if (res == 0)
01735          astman_append(s, "Response: Success\r\n"
01736                 "Events: Off\r\n\r\n");
01737       return 0;
01738    }
01739 
01740    if (res > 0)
01741       astman_append(s, "Response: Success\r\n"
01742              "Events: On\r\n\r\n");
01743    else if (res == 0)
01744       astman_append(s, "Response: Success\r\n"
01745              "Events: Off\r\n\r\n");
01746    else
01747       astman_send_error(s, m, "Invalid event mask");
01748 
01749    return 0;
01750 }
01751 
01752 static char mandescr_logoff[] =
01753 "Description: Logoff this manager session\n"
01754 "Variables: NONE\n";
01755 
01756 static int action_logoff(struct mansession *s, const struct message *m)
01757 {
01758    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01759    return -1;
01760 }
01761 
01762 static int action_login(struct mansession *s, const struct message *m)
01763 {
01764    if (authenticate(s, m)) {
01765       sleep(1);
01766       astman_send_error(s, m, "Authentication failed");
01767       return -1;
01768    }
01769    s->session->authenticated = 1;
01770    if (manager_displayconnects(s->session))
01771       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));
01772    ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
01773    astman_send_ack(s, m, "Authentication accepted");
01774    return 0;
01775 }
01776 
01777 static int action_challenge(struct mansession *s, const struct message *m)
01778 {
01779    const char *authtype = astman_get_header(m, "AuthType");
01780 
01781    if (!strcasecmp(authtype, "MD5")) {
01782       if (ast_strlen_zero(s->session->challenge))
01783          snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
01784       ast_mutex_lock(&s->session->__lock);
01785       astman_start_ack(s, m);
01786       astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
01787       ast_mutex_unlock(&s->session->__lock);
01788    } else {
01789       astman_send_error(s, m, "Must specify AuthType");
01790    }
01791    return 0;
01792 }
01793 
01794 static char mandescr_hangup[] =
01795 "Description: Hangup a channel\n"
01796 "Variables: \n"
01797 "  Channel: The channel name to be hungup\n";
01798 
01799 static int action_hangup(struct mansession *s, const struct message *m)
01800 {
01801    struct ast_channel *c = NULL;
01802    const char *name = astman_get_header(m, "Channel");
01803    if (ast_strlen_zero(name)) {
01804       astman_send_error(s, m, "No channel specified");
01805       return 0;
01806    }
01807    c = ast_get_channel_by_name_locked(name);
01808    if (!c) {
01809       astman_send_error(s, m, "No such channel");
01810       return 0;
01811    }
01812    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01813    ast_channel_unlock(c);
01814    astman_send_ack(s, m, "Channel Hungup");
01815    return 0;
01816 }
01817 
01818 static char mandescr_setvar[] =
01819 "Description: Set a global or local channel variable.\n"
01820 "Variables: (Names marked with * are required)\n"
01821 "  Channel: Channel to set variable for\n"
01822 "  *Variable: Variable name\n"
01823 "  *Value: Value\n";
01824 
01825 static int action_setvar(struct mansession *s, const struct message *m)
01826 {
01827    struct ast_channel *c = NULL;
01828    const char *name = astman_get_header(m, "Channel");
01829    const char *varname = astman_get_header(m, "Variable");
01830    const char *varval = astman_get_header(m, "Value");
01831    int res = 0;
01832    
01833    if (ast_strlen_zero(varname)) {
01834       astman_send_error(s, m, "No variable specified");
01835       return 0;
01836    }
01837 
01838    if (!ast_strlen_zero(name)) {
01839       c = ast_get_channel_by_name_locked(name);
01840       if (!c) {
01841          astman_send_error(s, m, "No such channel");
01842          return 0;
01843       }
01844    }
01845    if (varname[strlen(varname)-1] == ')') {
01846       char *function = ast_strdupa(varname);
01847       res = ast_func_write(c, function, varval);
01848    } else {
01849       pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01850    }
01851 
01852    if (c)
01853       ast_channel_unlock(c);
01854    if (res == 0) {
01855       astman_send_ack(s, m, "Variable Set"); 
01856    } else {
01857       astman_send_error(s, m, "Variable not set");
01858    }
01859    return 0;
01860 }
01861 
01862 static char mandescr_getvar[] =
01863 "Description: Get the value of a global or local channel variable.\n"
01864 "Variables: (Names marked with * are required)\n"
01865 "  Channel: Channel to read variable from\n"
01866 "  *Variable: Variable name\n"
01867 "  ActionID: Optional Action id for message matching.\n";
01868 
01869 static int action_getvar(struct mansession *s, const struct message *m)
01870 {
01871    struct ast_channel *c = NULL;
01872    const char *name = astman_get_header(m, "Channel");
01873    const char *varname = astman_get_header(m, "Variable");
01874    char *varval;
01875    char workspace[1024] = "";
01876 
01877    if (ast_strlen_zero(varname)) {
01878       astman_send_error(s, m, "No variable specified");
01879       return 0;
01880    }
01881 
01882    if (!ast_strlen_zero(name)) {
01883       c = ast_get_channel_by_name_locked(name);
01884       if (!c) {
01885          astman_send_error(s, m, "No such channel");
01886          return 0;
01887       }
01888    }
01889 
01890    if (varname[strlen(varname) - 1] == ')') {
01891       if (!c) {
01892          c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01893          if (c) {
01894             ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01895             ast_channel_free(c);
01896             c = NULL;
01897          } else
01898             ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
01899       } else
01900          ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01901       varval = workspace;
01902    } else {
01903       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01904    }
01905 
01906    if (c)
01907       ast_channel_unlock(c);
01908    astman_start_ack(s, m);
01909    astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
01910 
01911    return 0;
01912 }
01913 
01914 static char mandescr_status[] = 
01915 "Description: Lists channel status along with requested channel vars.\n"
01916 "Variables: (Names marked with * are required)\n"
01917 "  *Channel: Name of the channel to query for status\n"
01918 "  Variables: Comma ',' separated list of variables to include\n"
01919 "  ActionID: Optional ID for this transaction\n"
01920 "Will return the status information of each channel along with the\n"
01921 "value for the specified channel variables.\n";
01922  
01923 
01924 /*! \brief Manager "status" command to show channels */
01925 /* Needs documentation... */
01926 static int action_status(struct mansession *s, const struct message *m)
01927 {
01928    const char *name = astman_get_header(m, "Channel");
01929    const char *cvariables = astman_get_header(m, "Variables");
01930    char *variables = ast_strdupa(S_OR(cvariables, ""));
01931    struct ast_channel *c;
01932    char bridge[256];
01933    struct timeval now = ast_tvnow();
01934    long elapsed_seconds = 0;
01935    int channels = 0;
01936    int all = ast_strlen_zero(name); /* set if we want all channels */
01937    const char *id = astman_get_header(m, "ActionID");
01938    char idText[256];
01939    AST_DECLARE_APP_ARGS(vars,
01940       AST_APP_ARG(name)[100];
01941    );
01942    struct ast_str *str = ast_str_create(1000);
01943 
01944    if (!ast_strlen_zero(id))
01945       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01946    else
01947       idText[0] = '\0';
01948 
01949    if (all)
01950       c = ast_channel_walk_locked(NULL);
01951    else {
01952       c = ast_get_channel_by_name_locked(name);
01953       if (!c) {
01954          astman_send_error(s, m, "No such channel");
01955          ast_free(str);
01956          return 0;
01957       }
01958    }
01959    astman_send_ack(s, m, "Channel status will follow");
01960 
01961    if (!ast_strlen_zero(cvariables)) {
01962       AST_STANDARD_APP_ARGS(vars, variables);
01963    }
01964 
01965    /* if we look by name, we break after the first iteration */
01966    while (c) {
01967       if (!ast_strlen_zero(cvariables)) {
01968          int i;
01969          ast_str_reset(str);
01970          for (i = 0; i < vars.argc; i++) {
01971             char valbuf[512], *ret = NULL;
01972 
01973             if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
01974                if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
01975                   valbuf[0] = '\0';
01976                }
01977                ret = valbuf;
01978             } else {
01979                pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
01980             }
01981 
01982             ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
01983          }
01984       }
01985 
01986       channels++;
01987       if (c->_bridge)
01988          snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
01989       else
01990          bridge[0] = '\0';
01991       if (c->pbx) {
01992          if (c->cdr) {
01993             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01994          }
01995          astman_append(s,
01996          "Event: Status\r\n"
01997          "Privilege: Call\r\n"
01998          "Channel: %s\r\n"
01999          "CallerIDNum: %s\r\n"
02000          "CallerIDName: %s\r\n"
02001          "Accountcode: %s\r\n"
02002          "ChannelState: %d\r\n"
02003          "ChannelStateDesc: %s\r\n"
02004          "Context: %s\r\n"
02005          "Extension: %s\r\n"
02006          "Priority: %d\r\n"
02007          "Seconds: %ld\r\n"
02008          "%s"
02009          "Uniqueid: %s\r\n"
02010          "%s"
02011          "%s"
02012          "\r\n",
02013          c->name,
02014          S_OR(c->cid.cid_num, ""),
02015          S_OR(c->cid.cid_name, ""),
02016          c->accountcode,
02017          c->_state,
02018          ast_state2str(c->_state), c->context,
02019          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
02020       } else {
02021          astman_append(s,
02022          "Event: Status\r\n"
02023          "Privilege: Call\r\n"
02024          "Channel: %s\r\n"
02025          "CallerIDNum: %s\r\n"
02026          "CallerIDName: %s\r\n"
02027          "Account: %s\r\n"
02028          "State: %s\r\n"
02029          "%s"
02030          "Uniqueid: %s\r\n"
02031          "%s"
02032          "%s"
02033          "\r\n",
02034          c->name,
02035          S_OR(c->cid.cid_num, "<unknown>"),
02036          S_OR(c->cid.cid_name, "<unknown>"),
02037          c->accountcode,
02038          ast_state2str(c->_state), bridge, c->uniqueid, ast_str_buffer(str), idText);
02039       }
02040       ast_channel_unlock(c);
02041       if (!all)
02042          break;
02043       c = ast_channel_walk_locked(c);
02044    }
02045    astman_append(s,
02046    "Event: StatusComplete\r\n"
02047    "%s"
02048    "Items: %d\r\n"
02049    "\r\n", idText, channels);
02050    ast_free(str);
02051    return 0;
02052 }
02053 
02054 static char mandescr_sendtext[] =
02055 "Description: Sends A Text Message while in a call.\n"
02056 "Variables: (Names marked with * are required)\n"
02057 "       *Channel: Channel to send message to\n"
02058 "       *Message: Message to send\n"
02059 "       ActionID: Optional Action id for message matching.\n";
02060 
02061 static int action_sendtext(struct mansession *s, const struct message *m)
02062 {
02063    struct ast_channel *c = NULL;
02064    const char *name = astman_get_header(m, "Channel");
02065    const char *textmsg = astman_get_header(m, "Message");
02066    int res = 0;
02067 
02068    if (ast_strlen_zero(name)) {
02069       astman_send_error(s, m, "No channel specified");
02070       return 0;
02071    }
02072 
02073    if (ast_strlen_zero(textmsg)) {
02074       astman_send_error(s, m, "No Message specified");
02075       return 0;
02076    }
02077 
02078    c = ast_get_channel_by_name_locked(name);
02079    if (!c) {
02080       astman_send_error(s, m, "No such channel");
02081       return 0;
02082    }
02083 
02084    res = ast_sendtext(c, textmsg);
02085    ast_channel_unlock(c);
02086    
02087    if (res > 0)
02088       astman_send_ack(s, m, "Success");
02089    else
02090       astman_send_error(s, m, "Failure");
02091    
02092    return res;
02093 }
02094 
02095 static char mandescr_redirect[] =
02096 "Description: Redirect (transfer) a call.\n"
02097 "Variables: (Names marked with * are required)\n"
02098 "  *Channel: Channel to redirect\n"
02099 "  ExtraChannel: Second call leg to transfer (optional)\n"
02100 "  *Exten: Extension to transfer to\n"
02101 "  *Context: Context to transfer to\n"
02102 "  *Priority: Priority to transfer to\n"
02103 "  ActionID: Optional Action id for message matching.\n";
02104 
02105 /*! \brief  action_redirect: The redirect manager command */
02106 static int action_redirect(struct mansession *s, const struct message *m)
02107 {
02108    const char *name = astman_get_header(m, "Channel");
02109    const char *name2 = astman_get_header(m, "ExtraChannel");
02110    const char *exten = astman_get_header(m, "Exten");
02111    const char *context = astman_get_header(m, "Context");
02112    const char *priority = astman_get_header(m, "Priority");
02113    struct ast_channel *chan, *chan2 = NULL;
02114    int pi = 0;
02115    int res;
02116 
02117    if (ast_strlen_zero(name)) {
02118       astman_send_error(s, m, "Channel not specified");
02119       return 0;
02120    }
02121    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02122       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02123          astman_send_error(s, m, "Invalid priority");
02124          return 0;
02125       }
02126    }
02127    /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
02128    chan = ast_get_channel_by_name_locked(name);
02129    if (!chan) {
02130       char buf[256];
02131       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
02132       astman_send_error(s, m, buf);
02133       return 0;
02134    }
02135    if (ast_check_hangup(chan)) {
02136       astman_send_error(s, m, "Redirect failed, channel not up.");
02137       ast_channel_unlock(chan);
02138       return 0;
02139    }
02140    if (!ast_strlen_zero(name2))
02141       chan2 = ast_get_channel_by_name_locked(name2);
02142    if (chan2 && ast_check_hangup(chan2)) {
02143       astman_send_error(s, m, "Redirect failed, extra channel not up.");
02144       ast_channel_unlock(chan);
02145       ast_channel_unlock(chan2);
02146       return 0;
02147    }
02148    if (chan->pbx) {
02149       ast_channel_lock(chan);
02150       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02151       ast_channel_unlock(chan);
02152    }
02153    res = ast_async_goto(chan, context, exten, pi);
02154    if (!res) {
02155       if (!ast_strlen_zero(name2)) {
02156          if (chan2) {
02157             if (chan2->pbx) {
02158                ast_channel_lock(chan2);
02159                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02160                ast_channel_unlock(chan2);
02161             }
02162             res = ast_async_goto(chan2, context, exten, pi);
02163          } else {
02164             res = -1;
02165          }
02166          if (!res)
02167             astman_send_ack(s, m, "Dual Redirect successful");
02168          else
02169             astman_send_error(s, m, "Secondary redirect failed");
02170       } else
02171          astman_send_ack(s, m, "Redirect successful");
02172    } else
02173       astman_send_error(s, m, "Redirect failed");
02174    if (chan)
02175       ast_channel_unlock(chan);
02176    if (chan2)
02177       ast_channel_unlock(chan2);
02178    return 0;
02179 }
02180 
02181 static char mandescr_atxfer[] =
02182 "Description: Attended transfer.\n"
02183 "Variables: (Names marked with * are required)\n"
02184 "  *Channel: Transferer's channel\n"
02185 "  *Exten: Extension to transfer to\n"
02186 "  *Context: Context to transfer to\n"
02187 "  *Priority: Priority to transfer to\n"
02188 "  ActionID: Optional Action id for message matching.\n";
02189 
02190 static int action_atxfer(struct mansession *s, const struct message *m)
02191 {
02192    const char *name = astman_get_header(m, "Channel");
02193    const char *exten = astman_get_header(m, "Exten");
02194    const char *context = astman_get_header(m, "Context");
02195    struct ast_channel *chan = NULL;
02196    struct ast_call_feature *atxfer_feature = NULL;
02197    char *feature_code = NULL;
02198 
02199    if (ast_strlen_zero(name)) { 
02200       astman_send_error(s, m, "No channel specified");
02201       return 0;
02202    }
02203    if (ast_strlen_zero(exten)) {
02204       astman_send_error(s, m, "No extension specified");
02205       return 0;
02206    }
02207 
02208    if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
02209       astman_send_error(s, m, "No attended transfer feature found");
02210       return 0;
02211    }
02212 
02213    if (!(chan = ast_get_channel_by_name_locked(name))) {
02214       astman_send_error(s, m, "Channel specified does not exist");
02215       return 0;
02216    }
02217 
02218    if (!ast_strlen_zero(context)) {
02219       pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
02220    }
02221 
02222    for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
02223       struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02224       ast_queue_frame(chan, &f);
02225    }
02226 
02227    for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
02228       struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02229       ast_queue_frame(chan, &f);
02230    }
02231 
02232    astman_send_ack(s, m, "Atxfer successfully queued");
02233    ast_channel_unlock(chan);
02234 
02235    return 0;
02236 }
02237 
02238 static int check_blacklist(const char *cmd)
02239 {
02240    char *cmd_copy, *cur_cmd;
02241    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
02242    int i;
02243 
02244    cmd_copy = ast_strdupa(cmd);
02245    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
02246       cur_cmd = ast_strip(cur_cmd);
02247       if (ast_strlen_zero(cur_cmd)) {
02248          i--;
02249          continue;
02250       }
02251 
02252       cmd_words[i] = cur_cmd;
02253    }
02254 
02255    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
02256       int j, match = 1;
02257 
02258       for (j = 0; command_blacklist[i].words[j]; j++) {
02259          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
02260             match = 0;
02261             break;
02262          }
02263       }
02264 
02265       if (match) {
02266          return 1;
02267       }
02268    }
02269 
02270    return 0;
02271 }
02272 
02273 static char mandescr_command[] =
02274 "Description: Run a CLI command.\n"
02275 "Variables: (Names marked with * are required)\n"
02276 "  *Command: Asterisk CLI command to run\n"
02277 "  ActionID: Optional Action id for message matching.\n";
02278 
02279 /*! \brief  Manager command "command" - execute CLI command */
02280 static int action_command(struct mansession *s, const struct message *m)
02281 {
02282    const char *cmd = astman_get_header(m, "Command");
02283    const char *id = astman_get_header(m, "ActionID");
02284    char *buf, *final_buf;
02285    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
02286    int fd;
02287    off_t l;
02288 
02289    if (ast_strlen_zero(cmd)) {
02290       astman_send_error(s, m, "No command provided");
02291       return 0;
02292    }
02293 
02294    if (check_blacklist(cmd)) {
02295       astman_send_error(s, m, "Command blacklisted");
02296       return 0;
02297    }
02298 
02299    fd = mkstemp(template);
02300 
02301    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
02302    if (!ast_strlen_zero(id))
02303       astman_append(s, "ActionID: %s\r\n", id);
02304    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
02305    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
02306    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
02307 
02308    /* This has a potential to overflow the stack.  Hence, use the heap. */
02309    buf = ast_calloc(1, l + 1);
02310    final_buf = ast_calloc(1, l + 1);
02311    if (buf) {
02312       lseek(fd, 0, SEEK_SET);
02313       if (read(fd, buf, l) < 0) {
02314          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
02315       }
02316       buf[l] = '\0';
02317       if (final_buf) {
02318          term_strip(final_buf, buf, l);
02319          final_buf[l] = '\0';
02320       }
02321       astman_append(s, "%s", S_OR(final_buf, buf));
02322       ast_free(buf);
02323    }
02324    close(fd);
02325    unlink(template);
02326    astman_append(s, "--END COMMAND--\r\n\r\n");
02327    if (final_buf)
02328       ast_free(final_buf);
02329    return 0;
02330 }
02331 
02332 /*! \brief helper function for originate */
02333 struct fast_originate_helper {
02334    char tech[AST_MAX_EXTENSION];
02335    /*! data can contain a channel name, extension number, username, password, etc. */
02336    char data[512];
02337    int timeout;
02338    int format;          /*!< Codecs used for a call */
02339    char app[AST_MAX_APP];
02340    char appdata[AST_MAX_EXTENSION];
02341    char cid_name[AST_MAX_EXTENSION];
02342    char cid_num[AST_MAX_EXTENSION];
02343    char context[AST_MAX_CONTEXT];
02344    char exten[AST_MAX_EXTENSION];
02345    char idtext[AST_MAX_EXTENSION];
02346    char account[AST_MAX_ACCOUNT_CODE];
02347    int priority;
02348    struct ast_variable *vars;
02349 };
02350 
02351 static void *fast_originate(void *data)
02352 {
02353    struct fast_originate_helper *in = data;
02354    int res;
02355    int reason = 0;
02356    struct ast_channel *chan = NULL;
02357    char requested_channel[AST_CHANNEL_NAME];
02358 
02359    if (!ast_strlen_zero(in->app)) {
02360       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
02361          S_OR(in->cid_num, NULL),
02362          S_OR(in->cid_name, NULL),
02363          in->vars, in->account, &chan);
02364    } else {
02365       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
02366          S_OR(in->cid_num, NULL),
02367          S_OR(in->cid_name, NULL),
02368          in->vars, in->account, &chan);
02369    }
02370 
02371    if (!chan)
02372       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
02373    /* Tell the manager what happened with the channel */
02374    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
02375       "%s%s"
02376       "Response: %s\r\n"
02377       "Channel: %s\r\n"
02378       "Context: %s\r\n"
02379       "Exten: %s\r\n"
02380       "Reason: %d\r\n"
02381       "Uniqueid: %s\r\n"
02382       "CallerIDNum: %s\r\n"
02383       "CallerIDName: %s\r\n",
02384       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", 
02385       chan ? chan->name : requested_channel, in->context, in->exten, reason, 
02386       chan ? chan->uniqueid : "<null>",
02387       S_OR(in->cid_num, "<unknown>"),
02388       S_OR(in->cid_name, "<unknown>")
02389       );
02390 
02391    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
02392    if (chan)
02393       ast_channel_unlock(chan);
02394    ast_free(in);
02395    return NULL;
02396 }
02397 
02398 static char mandescr_originate[] =
02399 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
02400 "  Application/Data\n"
02401 "Variables: (Names marked with * are required)\n"
02402 "  *Channel: Channel name to call\n"
02403 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
02404 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
02405 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
02406 "  Application: Application to use\n"
02407 "  Data: Data to use (requires 'Application')\n"
02408 "  Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
02409 "  CallerID: Caller ID to be set on the outgoing channel\n"
02410 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
02411 "  Account: Account code\n"
02412 "  Async: Set to 'true' for fast origination\n";
02413 
02414 static int action_originate(struct mansession *s, const struct message *m)
02415 {
02416    const char *name = astman_get_header(m, "Channel");
02417    const char *exten = astman_get_header(m, "Exten");
02418    const char *context = astman_get_header(m, "Context");
02419    const char *priority = astman_get_header(m, "Priority");
02420    const char *timeout = astman_get_header(m, "Timeout");
02421    const char *callerid = astman_get_header(m, "CallerID");
02422    const char *account = astman_get_header(m, "Account");
02423    const char *app = astman_get_header(m, "Application");
02424    const char *appdata = astman_get_header(m, "Data");
02425    const char *async = astman_get_header(m, "Async");
02426    const char *id = astman_get_header(m, "ActionID");
02427    const char *codecs = astman_get_header(m, "Codecs");
02428    struct ast_variable *vars = astman_get_variables(m);
02429    char *tech, *data;
02430    char *l = NULL, *n = NULL;
02431    int pi = 0;
02432    int res;
02433    int to = 30000;
02434    int reason = 0;
02435    char tmp[256];
02436    char tmp2[256];
02437    int format = AST_FORMAT_SLINEAR;
02438 
02439    pthread_t th;
02440    if (ast_strlen_zero(name)) {
02441       astman_send_error(s, m, "Channel not specified");
02442       return 0;
02443    }
02444    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02445       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02446          astman_send_error(s, m, "Invalid priority");
02447          return 0;
02448       }
02449    }
02450    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02451       astman_send_error(s, m, "Invalid timeout");
02452       return 0;
02453    }
02454    ast_copy_string(tmp, name, sizeof(tmp));
02455    tech = tmp;
02456    data = strchr(tmp, '/');
02457    if (!data) {
02458       astman_send_error(s, m, "Invalid channel");
02459       return 0;
02460    }
02461    *data++ = '\0';
02462    ast_copy_string(tmp2, callerid, sizeof(tmp2));
02463    ast_callerid_parse(tmp2, &n, &l);
02464    if (n) {
02465       if (ast_strlen_zero(n))
02466          n = NULL;
02467    }
02468    if (l) {
02469       ast_shrink_phone_number(l);
02470       if (ast_strlen_zero(l))
02471          l = NULL;
02472    }
02473    if (!ast_strlen_zero(codecs)) {
02474       format = 0;
02475       ast_parse_allow_disallow(NULL, &format, codecs, 1);
02476    }
02477    if (ast_true(async)) {
02478       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02479       if (!fast) {
02480          res = -1;
02481       } else {
02482          if (!ast_strlen_zero(id))
02483             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02484          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02485             ast_copy_string(fast->data, data, sizeof(fast->data));
02486          ast_copy_string(fast->app, app, sizeof(fast->app));
02487          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02488          if (l)
02489             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02490          if (n)
02491             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02492          fast->vars = vars;
02493          ast_copy_string(fast->context, context, sizeof(fast->context));
02494          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02495          ast_copy_string(fast->account, account, sizeof(fast->account));
02496          fast->format = format;
02497          fast->timeout = to;
02498          fast->priority = pi;
02499          if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
02500             ast_free(fast);
02501             res = -1;
02502          } else {
02503             res = 0;
02504          }
02505       }
02506    } else if (!ast_strlen_zero(app)) {
02507       /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
02508       if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
02509          && (
02510             strcasestr(app, "system") == 0 || /* System(rm -rf /)
02511                                                  TrySystem(rm -rf /)       */
02512             strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
02513                                                  TryExec(System(rm -rf /)) */
02514             strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
02515                                                  EAGI(/bin/rm,-rf /)       */
02516             strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
02517             strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
02518             )) {
02519          astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
02520          return 0;
02521       }
02522       res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02523    } else {
02524       if (exten && context && pi)
02525          res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02526       else {
02527          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02528          return 0;
02529       }
02530    }
02531    if (!res)
02532       astman_send_ack(s, m, "Originate successfully queued");
02533    else
02534       astman_send_error(s, m, "Originate failed");
02535    return 0;
02536 }
02537 
02538 /*! \brief Help text for manager command mailboxstatus
02539  */
02540 static char mandescr_mailboxstatus[] =
02541 "Description: Checks a voicemail account for status.\n"
02542 "Variables: (Names marked with * are required)\n"
02543 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02544 "  ActionID: Optional ActionID for message matching.\n"
02545 "Returns number of messages.\n"
02546 "  Message: Mailbox Status\n"
02547 "  Mailbox: <mailboxid>\n"
02548 "  Waiting: <count>\n"
02549 "\n";
02550 
02551 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02552 {
02553    const char *mailbox = astman_get_header(m, "Mailbox");
02554    int ret;
02555 
02556    if (ast_strlen_zero(mailbox)) {
02557       astman_send_error(s, m, "Mailbox not specified");
02558       return 0;
02559    }
02560    ret = ast_app_has_voicemail(mailbox, NULL);
02561    astman_start_ack(s, m);
02562    astman_append(s, "Message: Mailbox Status\r\n"
02563           "Mailbox: %s\r\n"
02564           "Waiting: %d\r\n\r\n", mailbox, ret);
02565    return 0;
02566 }
02567 
02568 static char mandescr_mailboxcount[] =
02569 "Description: Checks a voicemail account for new messages.\n"
02570 "Variables: (Names marked with * are required)\n"
02571 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02572 "  ActionID: Optional ActionID for message matching.\n"
02573 "Returns number of urgent, new and old messages.\n"
02574 "  Message: Mailbox Message Count\n"
02575 "  Mailbox: <mailboxid>\n"
02576 "  UrgentMessages: <count>\n"
02577 "  NewMessages: <count>\n"
02578 "  OldMessages: <count>\n"
02579 "\n";
02580 static int action_mailboxcount(struct mansession *s, const struct message *m)
02581 {
02582    const char *mailbox = astman_get_header(m, "Mailbox");
02583    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
02584 
02585    if (ast_strlen_zero(mailbox)) {
02586       astman_send_error(s, m, "Mailbox not specified");
02587       return 0;
02588    }
02589    ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
02590    astman_start_ack(s, m);
02591    astman_append(s,   "Message: Mailbox Message Count\r\n"
02592             "Mailbox: %s\r\n"
02593             "UrgMessages: %d\r\n"
02594             "NewMessages: %d\r\n"
02595             "OldMessages: %d\r\n"
02596             "\r\n",
02597             mailbox, urgentmsgs, newmsgs, oldmsgs);
02598    return 0;
02599 }
02600 
02601 static char mandescr_extensionstate[] =
02602 "Description: Report the extension state for given extension.\n"
02603 "  If the extension has a hint, will use devicestate to check\n"
02604 "  the status of the device connected to the extension.\n"
02605 "Variables: (Names marked with * are required)\n"
02606 "  *Exten: Extension to check state on\n"
02607 "  *Context: Context for extension\n"
02608 "  ActionId: Optional ID for this transaction\n"
02609 "Will return an \"Extension Status\" message.\n"
02610 "The response will include the hint for the extension and the status.\n";
02611 
02612 static int action_extensionstate(struct mansession *s, const struct message *m)
02613 {
02614    const char *exten = astman_get_header(m, "Exten");
02615    const char *context = astman_get_header(m, "Context");
02616    char hint[256] = "";
02617    int status;
02618    if (ast_strlen_zero(exten)) {
02619       astman_send_error(s, m, "Extension not specified");
02620       return 0;
02621    }
02622    if (ast_strlen_zero(context))
02623       context = "default";
02624    status = ast_extension_state(NULL, context, exten);
02625    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02626    astman_start_ack(s, m);
02627    astman_append(s,   "Message: Extension Status\r\n"
02628             "Exten: %s\r\n"
02629             "Context: %s\r\n"
02630             "Hint: %s\r\n"
02631             "Status: %d\r\n\r\n",
02632             exten, context, hint, status);
02633    return 0;
02634 }
02635 
02636 static char mandescr_timeout[] =
02637 "Description: Hangup a channel after a certain time.\n"
02638 "Variables: (Names marked with * are required)\n"
02639 "  *Channel: Channel name to hangup\n"
02640 "  *Timeout: Maximum duration of the call (sec)\n"
02641 "Acknowledges set time with 'Timeout Set' message\n";
02642 
02643 static int action_timeout(struct mansession *s, const struct message *m)
02644 {
02645    struct ast_channel *c;
02646    const char *name = astman_get_header(m, "Channel");
02647    double timeout = atof(astman_get_header(m, "Timeout"));
02648    struct timeval when = { timeout, 0 };
02649 
02650    if (ast_strlen_zero(name)) {
02651       astman_send_error(s, m, "No channel specified");
02652       return 0;
02653    }
02654    if (!timeout || timeout < 0) {
02655       astman_send_error(s, m, "No timeout specified");
02656       return 0;
02657    }
02658    c = ast_get_channel_by_name_locked(name);
02659    if (!c) {
02660       astman_send_error(s, m, "No such channel");
02661       return 0;
02662    }
02663 
02664    when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
02665    ast_channel_setwhentohangup_tv(c, when);
02666    ast_channel_unlock(c);
02667    astman_send_ack(s, m, "Timeout Set");
02668    return 0;
02669 }
02670 
02671 /*!
02672  * Send any applicable events to the client listening on this socket.
02673  * Wait only for a finite time on each event, and drop all events whether
02674  * they are successfully sent or not.
02675  */
02676 static int process_events(struct mansession *s)
02677 {
02678    int ret = 0;
02679 
02680    ast_mutex_lock(&s->session->__lock);
02681    if (s->session->f != NULL) {
02682       struct eventqent *eqe;
02683 
02684       while ( (eqe = NEW_EVENT(s)) ) {
02685          ref_event(eqe);
02686          if (!ret && s->session->authenticated &&
02687              (s->session->readperm & eqe->category) == eqe->category &&
02688              (s->session->send_events & eqe->category) == eqe->category) {
02689             if (send_string(s, eqe->eventdata) < 0)
02690                ret = -1;   /* don't send more */
02691          }
02692          s->session->last_ev = unref_event(s->session->last_ev);
02693       }
02694    }
02695    ast_mutex_unlock(&s->session->__lock);
02696    return ret;
02697 }
02698 
02699 static char mandescr_userevent[] =
02700 "Description: Send an event to manager sessions.\n"
02701 "Variables: (Names marked with * are required)\n"
02702 "       *UserEvent: EventStringToSend\n"
02703 "       Header1: Content1\n"
02704 "       HeaderN: ContentN\n";
02705 
02706 static int action_userevent(struct mansession *s, const struct message *m)
02707 {
02708    const char *event = astman_get_header(m, "UserEvent");
02709    struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
02710    int x;
02711 
02712    ast_str_reset(body);
02713 
02714    for (x = 0; x < m->hdrcount; x++) {
02715       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02716          ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
02717       }
02718    }
02719 
02720    astman_send_ack(s, m, "Event Sent");   
02721    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
02722    return 0;
02723 }
02724 
02725 static char mandescr_coresettings[] =
02726 "Description: Query for Core PBX settings.\n"
02727 "Variables: (Names marked with * are optional)\n"
02728 "       *ActionID: ActionID of this transaction\n";
02729 
02730 /*! \brief Show PBX core settings information */
02731 static int action_coresettings(struct mansession *s, const struct message *m)
02732 {
02733    const char *actionid = astman_get_header(m, "ActionID");
02734    char idText[150];
02735 
02736    if (!ast_strlen_zero(actionid))
02737       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02738    else
02739       idText[0] = '\0';
02740 
02741    astman_append(s, "Response: Success\r\n"
02742          "%s"
02743          "AMIversion: %s\r\n"
02744          "AsteriskVersion: %s\r\n"
02745          "SystemName: %s\r\n"
02746          "CoreMaxCalls: %d\r\n"
02747          "CoreMaxLoadAvg: %f\r\n"
02748          "CoreRunUser: %s\r\n"
02749          "CoreRunGroup: %s\r\n"
02750          "CoreMaxFilehandles: %d\r\n" 
02751          "CoreRealTimeEnabled: %s\r\n"
02752          "CoreCDRenabled: %s\r\n"
02753          "CoreHTTPenabled: %s\r\n"
02754          "\r\n",
02755          idText,
02756          AMI_VERSION,
02757          ast_get_version(), 
02758          ast_config_AST_SYSTEM_NAME,
02759          option_maxcalls,
02760          option_maxload,
02761          ast_config_AST_RUN_USER,
02762          ast_config_AST_RUN_GROUP,
02763          option_maxfiles,
02764          ast_realtime_enabled() ? "Yes" : "No",
02765          check_cdr_enabled() ? "Yes" : "No",
02766          check_webmanager_enabled() ? "Yes" : "No"
02767          );
02768    return 0;
02769 }
02770 
02771 static char mandescr_corestatus[] =
02772 "Description: Query for Core PBX status.\n"
02773 "Variables: (Names marked with * are optional)\n"
02774 "       *ActionID: ActionID of this transaction\n";
02775 
02776 /*! \brief Show PBX core status information */
02777 static int action_corestatus(struct mansession *s, const struct message *m)
02778 {
02779    const char *actionid = astman_get_header(m, "ActionID");
02780    char idText[150];
02781    char startuptime[150];
02782    char reloadtime[150];
02783    struct ast_tm tm;
02784 
02785    if (!ast_strlen_zero(actionid))
02786       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02787    else
02788       idText[0] = '\0';
02789 
02790    ast_localtime(&ast_startuptime, &tm, NULL);
02791    ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02792    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02793    ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02794 
02795    astman_append(s, "Response: Success\r\n"
02796          "%s"
02797          "CoreStartupTime: %s\r\n"
02798          "CoreReloadTime: %s\r\n"
02799          "CoreCurrentCalls: %d\r\n"
02800          "\r\n",
02801          idText,
02802          startuptime,
02803          reloadtime,
02804          ast_active_channels()
02805          );
02806    return 0;
02807 }
02808 
02809 static char mandescr_reload[] =
02810 "Description: Send a reload event.\n"
02811 "Variables: (Names marked with * are optional)\n"
02812 "       *ActionID: ActionID of this transaction\n"
02813 "       *Module: Name of the module to reload\n";
02814 
02815 /*! \brief Send a reload event */
02816 static int action_reload(struct mansession *s, const struct message *m)
02817 {
02818    const char *module = astman_get_header(m, "Module");
02819    int res = ast_module_reload(S_OR(module, NULL));
02820 
02821    if (res == 2)
02822       astman_send_ack(s, m, "Module Reloaded");
02823    else
02824       astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
02825    return 0;
02826 }
02827 
02828 static char mandescr_coreshowchannels[] =
02829 "Description: List currently defined channels and some information\n"
02830 "             about them.\n"
02831 "Variables:\n"
02832 "          ActionID: Optional Action id for message matching.\n";
02833 
02834 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels 
02835  *          and some information about them. */
02836 static int action_coreshowchannels(struct mansession *s, const struct message *m)
02837 {
02838    const char *actionid = astman_get_header(m, "ActionID");
02839    char idText[256];
02840    struct ast_channel *c = NULL;
02841    int numchans = 0;
02842    int duration, durh, durm, durs;
02843 
02844    if (!ast_strlen_zero(actionid))
02845       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02846    else
02847       idText[0] = '\0';
02848 
02849    astman_send_listack(s, m, "Channels will follow", "start"); 
02850 
02851    while ((c = ast_channel_walk_locked(c)) != NULL) {
02852       struct ast_channel *bc = ast_bridged_channel(c);
02853       char durbuf[10] = "";
02854 
02855       if (c->cdr && !ast_tvzero(c->cdr->start)) {
02856          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
02857          durh = duration / 3600;
02858          durm = (duration % 3600) / 60;
02859          durs = duration % 60;
02860          snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
02861       }
02862 
02863       astman_append(s,
02864          "Event: CoreShowChannel\r\n"
02865          "%s"
02866          "Channel: %s\r\n"
02867          "UniqueID: %s\r\n"
02868          "Context: %s\r\n"
02869          "Extension: %s\r\n"
02870          "Priority: %d\r\n"
02871          "ChannelState: %d\r\n"
02872          "ChannelStateDesc: %s\r\n"
02873          "Application: %s\r\n"
02874          "ApplicationData: %s\r\n"
02875          "CallerIDnum: %s\r\n"
02876          "Duration: %s\r\n"
02877          "AccountCode: %s\r\n"
02878          "BridgedChannel: %s\r\n"
02879          "BridgedUniqueID: %s\r\n"
02880          "\r\n", idText, c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state,
02881          ast_state2str(c->_state), c->appl ? c->appl : "", c->data ? S_OR(c->data, "") : "",
02882          S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
02883       ast_channel_unlock(c);
02884       numchans++;
02885    }
02886 
02887    astman_append(s,
02888       "Event: CoreShowChannelsComplete\r\n"
02889       "EventList: Complete\r\n"
02890       "ListItems: %d\r\n"
02891       "%s"
02892       "\r\n", numchans, idText);
02893 
02894    return 0;
02895 }
02896 
02897 static char mandescr_modulecheck[] = 
02898 "Description: Checks if Asterisk module is loaded\n"
02899 "Variables: \n"
02900 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
02901 "  Module: <name>          Asterisk module name (not including extension)\n"
02902 "\n"
02903 "Will return Success/Failure\n"
02904 "For success returns, the module revision number is included.\n";
02905 
02906 /* Manager function to check if module is loaded */
02907 static int manager_modulecheck(struct mansession *s, const struct message *m)
02908 {
02909    int res;
02910    const char *module = astman_get_header(m, "Module");
02911    const char *id = astman_get_header(m, "ActionID");
02912    char idText[256];
02913 #if !defined(LOW_MEMORY)
02914    const char *version;
02915 #endif
02916    char filename[PATH_MAX];
02917    char *cut;
02918 
02919    ast_copy_string(filename, module, sizeof(filename));
02920    if ((cut = strchr(filename, '.'))) {
02921       *cut = '\0';
02922    } else {
02923       cut = filename + strlen(filename);
02924    }
02925    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
02926    ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
02927    res = ast_module_check(filename);
02928    if (!res) {
02929       astman_send_error(s, m, "Module not loaded");
02930       return 0;
02931    }
02932    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
02933    ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
02934 #if !defined(LOW_MEMORY)
02935    version = ast_file_version_find(filename);
02936 #endif
02937 
02938    if (!ast_strlen_zero(id))
02939       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02940    else
02941       idText[0] = '\0';
02942    astman_append(s, "Response: Success\r\n%s", idText);
02943 #if !defined(LOW_MEMORY)
02944    astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
02945 #endif
02946    return 0;
02947 }
02948 
02949 static char mandescr_moduleload[] = 
02950 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
02951 "Variables: \n"
02952 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
02953 "  Module: <name>          Asterisk module name (including .so extension)\n"
02954 "                          or subsystem identifier:\n"
02955 "           cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
02956 "  LoadType: load | unload | reload\n"
02957 "                          The operation to be done on module\n"
02958 " If no module is specified for a reload loadtype, all modules are reloaded";
02959 
02960 static int manager_moduleload(struct mansession *s, const struct message *m)
02961 {
02962    int res;
02963    const char *module = astman_get_header(m, "Module");
02964    const char *loadtype = astman_get_header(m, "LoadType");
02965 
02966    if (!loadtype || strlen(loadtype) == 0)
02967       astman_send_error(s, m, "Incomplete ModuleLoad action.");
02968    if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
02969       astman_send_error(s, m, "Need module name");
02970 
02971    if (!strcasecmp(loadtype, "load")) {
02972       res = ast_load_resource(module);
02973       if (res)
02974          astman_send_error(s, m, "Could not load module.");
02975       else
02976          astman_send_ack(s, m, "Module loaded.");
02977    } else if (!strcasecmp(loadtype, "unload")) {
02978       res = ast_unload_resource(module, AST_FORCE_SOFT);
02979       if (res)
02980          astman_send_error(s, m, "Could not unload module.");
02981       else
02982          astman_send_ack(s, m, "Module unloaded.");
02983    } else if (!strcasecmp(loadtype, "reload")) {
02984       if (module != NULL) {
02985          res = ast_module_reload(module);
02986          if (res == 0)
02987             astman_send_error(s, m, "No such module.");
02988          else if (res == 1)
02989             astman_send_error(s, m, "Module does not support reload action.");
02990          else
02991             astman_send_ack(s, m, "Module reloaded.");
02992       } else {
02993          ast_module_reload(NULL);   /* Reload all modules */
02994          astman_send_ack(s, m, "All modules reloaded");
02995       }
02996    } else 
02997       astman_send_error(s, m, "Incomplete ModuleLoad action.");
02998    return 0;
02999 }
03000 
03001 /*
03002  * Done with the action handlers here, we start with the code in charge
03003  * of accepting connections and serving them.
03004  * accept_thread() forks a new thread for each connection, session_do(),
03005  * which in turn calls get_input() repeatedly until a full message has
03006  * been accumulated, and then invokes process_message() to pass it to
03007  * the appropriate handler.
03008  */
03009 
03010 /*
03011  * Process an AMI message, performing desired action.
03012  * Return 0 on success, -1 on error that require the session to be destroyed.
03013  */
03014 static int process_message(struct mansession *s, const struct message *m)
03015 {
03016    char action[80] = "";
03017    int ret = 0;
03018    struct manager_action *tmp;
03019    const char *user = astman_get_header(m, "Username");
03020 
03021    ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
03022    ast_debug(1, "Manager received command '%s'\n", action);
03023 
03024    if (ast_strlen_zero(action)) {
03025       ast_mutex_lock(&s->session->__lock);
03026       astman_send_error(s, m, "Missing action in request");
03027       ast_mutex_unlock(&s->session->__lock);
03028       return 0;
03029    }
03030 
03031    if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
03032       ast_mutex_lock(&s->session->__lock);
03033       astman_send_error(s, m, "Permission denied");
03034       ast_mutex_unlock(&s->session->__lock);
03035       return 0;
03036    }
03037 
03038    if (!allowmultiplelogin && !s->session->authenticated && user &&
03039       (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
03040       if (check_manager_session_inuse(user)) {
03041          sleep(1);
03042          ast_mutex_lock(&s->session->__lock);
03043          astman_send_error(s, m, "Login Already In Use");
03044          ast_mutex_unlock(&s->session->__lock);
03045          return -1;
03046       }
03047    }
03048 
03049    AST_RWLIST_RDLOCK(&actions);
03050    AST_RWLIST_TRAVERSE(&actions, tmp, list) {
03051       if (strcasecmp(action, tmp->action))
03052          continue;
03053       if (s->session->writeperm & tmp->authority || tmp->authority == 0)
03054          ret = tmp->func(s, m);
03055       else
03056          astman_send_error(s, m, "Permission denied");
03057       break;
03058    }
03059    AST_RWLIST_UNLOCK(&actions);
03060 
03061    if (!tmp) {
03062       char buf[512];
03063       snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
03064       ast_mutex_lock(&s->session->__lock);
03065       astman_send_error(s, m, buf);
03066       ast_mutex_unlock(&s->session->__lock);
03067    }
03068    if (ret)
03069       return ret;
03070    /* Once done with our message, deliver any pending events unless the
03071       requester doesn't want them as part of this response.
03072    */
03073    if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
03074       return process_events(s);
03075    } else {
03076       return ret;
03077    }
03078 }
03079 
03080 /*!
03081  * Read one full line (including crlf) from the manager socket.
03082  * \note \verbatim
03083  * \r\n is the only valid terminator for the line.
03084  * (Note that, later, '\0' will be considered as the end-of-line marker,
03085  * so everything between the '\0' and the '\r\n' will not be used).
03086  * Also note that we assume output to have at least "maxlen" space.
03087  * \endverbatim
03088  */
03089 static int get_input(struct mansession *s, char *output)
03090 {
03091    int res, x;
03092    int maxlen = sizeof(s->session->inbuf) - 1;
03093    char *src = s->session->inbuf;
03094 
03095    /*
03096     * Look for \r\n within the buffer. If found, copy to the output
03097     * buffer and return, trimming the \r\n (not used afterwards).
03098     */
03099    for (x = 0; x < s->session->inlen; x++) {
03100       int cr;  /* set if we have \r */
03101       if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n')
03102          cr = 2;  /* Found. Update length to include \r\n */
03103       else if (src[x] == '\n')
03104          cr = 1;  /* also accept \n only */
03105       else
03106          continue;
03107       memmove(output, src, x);   /*... but trim \r\n */
03108       output[x] = '\0';    /* terminate the string */
03109       x += cr;       /* number of bytes used */
03110       s->session->inlen -= x;       /* remaining size */
03111       memmove(src, src + x, s->session->inlen); /* remove used bytes */
03112       return 1;
03113    }
03114    if (s->session->inlen >= maxlen) {
03115       /* no crlf found, and buffer full - sorry, too long for us */
03116       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
03117       s->session->inlen = 0;
03118    }
03119    res = 0;
03120    while (res == 0) {
03121       /* XXX do we really need this locking ? */
03122       ast_mutex_lock(&s->session->__lock);
03123       if (s->session->pending_event) {
03124          s->session->pending_event = 0;
03125          ast_mutex_unlock(&s->session->__lock);
03126          return 0;
03127       }
03128       s->session->waiting_thread = pthread_self();
03129       ast_mutex_unlock(&s->session->__lock);
03130 
03131       res = ast_wait_for_input(s->session->fd, -1);   /* return 0 on timeout ? */
03132 
03133       ast_mutex_lock(&s->session->__lock);
03134       s->session->waiting_thread = AST_PTHREADT_NULL;
03135       ast_mutex_unlock(&s->session->__lock);
03136    }
03137    if (res < 0) {
03138       /* If we get a signal from some other thread (typically because
03139        * there are new events queued), return 0 to notify the caller.
03140        */
03141       if (errno == EINTR || errno == EAGAIN)
03142          return 0;
03143       ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
03144       return -1;
03145    }
03146    ast_mutex_lock(&s->session->__lock);
03147    res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
03148    if (res < 1)
03149       res = -1;   /* error return */
03150    else {
03151       s->session->inlen += res;
03152       src[s->session->inlen] = '\0';
03153       res = 0;
03154    }
03155    ast_mutex_unlock(&s->session->__lock);
03156    return res;
03157 }
03158 
03159 static int do_message(struct mansession *s)
03160 {
03161    struct message m = { 0 };
03162    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
03163    int res;
03164 
03165    for (;;) {
03166       /* Check if any events are pending and do them if needed */
03167       if (process_events(s))
03168          return -1;
03169       res = get_input(s, header_buf);
03170       if (res == 0) {
03171          continue;
03172       } else if (res > 0) {
03173          if (ast_strlen_zero(header_buf))
03174             return process_message(s, &m) ? -1 : 0;
03175          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
03176             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
03177       } else {
03178          return res;
03179       }
03180    }
03181 }
03182 
03183 /*! \brief The body of the individual manager session.
03184  * Call get_input() to read one line at a time
03185  * (or be woken up on new events), collect the lines in a
03186  * message until found an empty line, and execute the request.
03187  * In any case, deliver events asynchronously through process_events()
03188  * (called from here if no line is available, or at the end of
03189  * process_message(). )
03190  */
03191 static void *session_do(void *data)
03192 {
03193    struct ast_tcptls_session_instance *ser = data;
03194    struct mansession_session *session = ast_calloc(1, sizeof(*session));
03195    struct mansession s = {.session = NULL, };
03196    int flags;
03197    int res;
03198 
03199    if (session == NULL)
03200       goto done;
03201 
03202    session->writetimeout = 100;
03203    session->waiting_thread = AST_PTHREADT_NULL;
03204 
03205    flags = fcntl(ser->fd, F_GETFL);
03206    if (!block_sockets) /* make sure socket is non-blocking */
03207       flags |= O_NONBLOCK;
03208    else
03209       flags &= ~O_NONBLOCK;
03210    fcntl(ser->fd, F_SETFL, flags);
03211 
03212    ast_mutex_init(&session->__lock);
03213    session->send_events = -1;
03214    /* Hook to the tail of the event queue */
03215    session->last_ev = grab_last();
03216 
03217    /* these fields duplicate those in the 'ser' structure */
03218    session->fd = ser->fd;
03219    session->f = ser->f;
03220    session->sin = ser->remote_address;
03221    s.session = session;
03222 
03223    AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03224 
03225    AST_LIST_LOCK(&sessions);
03226    AST_LIST_INSERT_HEAD(&sessions, session, list);
03227    ast_atomic_fetchadd_int(&num_sessions, 1);
03228    AST_LIST_UNLOCK(&sessions);
03229 
03230    astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);   /* welcome prompt */
03231    for (;;) {
03232       if ((res = do_message(&s)) < 0)
03233          break;
03234    }
03235    /* session is over, explain why and terminate */
03236    if (session->authenticated) {
03237          if (manager_displayconnects(session))
03238          ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03239       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03240    } else {
03241          if (displayconnects)
03242          ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03243       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03244    }
03245 
03246    /* It is possible under certain circumstances for this session thread
03247       to complete its work and exit *before* the thread that created it
03248       has finished executing the ast_pthread_create_background() function.
03249       If this occurs, some versions of glibc appear to act in a buggy
03250       fashion and attempt to write data into memory that it thinks belongs
03251       to the thread but is in fact not owned by the thread (or may have
03252       been freed completely).
03253 
03254       Causing this thread to yield to other threads at least one time
03255       appears to work around this bug.
03256    */
03257    usleep(1);
03258 
03259    destroy_session(session);
03260 
03261 done:
03262    ao2_ref(ser, -1);
03263    ser = NULL;
03264    return NULL;
03265 }
03266 
03267 /*! \brief remove at most n_max stale session from the list. */
03268 static void purge_sessions(int n_max)
03269 {
03270    struct mansession_session *session;
03271    time_t now = time(NULL);
03272 
03273    AST_LIST_LOCK(&sessions);
03274    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) {
03275       if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
03276          AST_LIST_REMOVE_CURRENT(list);
03277          ast_atomic_fetchadd_int(&num_sessions, -1);
03278          if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
03279             ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
03280                session->username, ast_inet_ntoa(session->sin.sin_addr));
03281          }
03282          free_session(session);  /* XXX outside ? */
03283          if (--n_max <= 0)
03284             break;
03285       }
03286    }
03287    AST_LIST_TRAVERSE_SAFE_END;
03288    AST_LIST_UNLOCK(&sessions);
03289 }
03290 
03291 /*
03292  * events are appended to a queue from where they
03293  * can be dispatched to clients.
03294  */
03295 static int append_event(const char *str, int category)
03296 {
03297    struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
03298    static int seq;   /* sequence number */
03299 
03300    if (!tmp)
03301       return -1;
03302 
03303    /* need to init all fields, because ast_malloc() does not */
03304    tmp->usecount = 0;
03305    tmp->category = category;
03306    tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
03307    AST_LIST_NEXT(tmp, eq_next) = NULL;
03308    strcpy(tmp->eventdata, str);
03309 
03310    AST_LIST_LOCK(&all_events);
03311    AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
03312    AST_LIST_UNLOCK(&all_events);
03313 
03314    return 0;
03315 }
03316 
03317 /* XXX see if can be moved inside the function */
03318 AST_THREADSTORAGE(manager_event_buf);
03319 #define MANAGER_EVENT_BUF_INITSIZE   256
03320 
03321 /*! \brief  manager_event: Send AMI event to client */
03322 int __manager_event(int category, const char *event,
03323    const char *file, int line, const char *func, const char *fmt, ...)
03324 {
03325    struct mansession_session *session;
03326    struct manager_custom_hook *hook;
03327    struct ast_str *auth = ast_str_alloca(80);
03328    const char *cat_str;
03329    va_list ap;
03330    struct timeval now;
03331    struct ast_str *buf;
03332 
03333    /* Abort if there are neither any manager sessions nor hooks */
03334    if (!num_sessions && AST_RWLIST_EMPTY(&manager_hooks))
03335       return 0;
03336 
03337    if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
03338       return -1;
03339 
03340    cat_str = authority_to_str(category, &auth);
03341    ast_str_set(&buf, 0,
03342          "Event: %s\r\nPrivilege: %s\r\n",
03343           event, cat_str);
03344 
03345    if (timestampevents) {
03346       now = ast_tvnow();
03347       ast_str_append(&buf, 0,
03348             "Timestamp: %ld.%06lu\r\n",
03349              (long)now.tv_sec, (unsigned long) now.tv_usec);
03350    }
03351    if (manager_debug) {
03352       static int seq;
03353       ast_str_append(&buf, 0,
03354             "SequenceNumber: %d\r\n",
03355              ast_atomic_fetchadd_int(&seq, 1));
03356       ast_str_append(&buf, 0,
03357             "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
03358    }
03359 
03360    va_start(ap, fmt);
03361    ast_str_append_va(&buf, 0, fmt, ap);
03362    va_end(ap);
03363 
03364    ast_str_append(&buf, 0, "\r\n");
03365 
03366    append_event(ast_str_buffer(buf), category);
03367 
03368    if (num_sessions) {
03369       /* Wake up any sleeping sessions */
03370       AST_LIST_LOCK(&sessions);
03371       AST_LIST_TRAVERSE(&sessions, session, list) {
03372          ast_mutex_lock(&session->__lock);
03373          if (session->waiting_thread != AST_PTHREADT_NULL)
03374             pthread_kill(session->waiting_thread, SIGURG);
03375          else
03376             /* We have an event to process, but the mansession is
03377              * not waiting for it. We still need to indicate that there
03378              * is an event waiting so that get_input processes the pending
03379              * event instead of polling.
03380              */
03381             session->pending_event = 1;
03382          ast_mutex_unlock(&session->__lock);
03383       }
03384       AST_LIST_UNLOCK(&sessions);
03385    }
03386 
03387    if (!AST_RWLIST_EMPTY(&manager_hooks)) {
03388       AST_RWLIST_RDLOCK(&manager_hooks);
03389       AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
03390          hook->helper(category, event, ast_str_buffer(buf));
03391       }
03392       AST_RWLIST_UNLOCK(&manager_hooks);
03393    }
03394 
03395    return 0;
03396 }
03397 
03398 /*
03399  * support functions to register/unregister AMI action handlers,
03400  */
03401 int ast_manager_unregister(char *action)
03402 {
03403    struct manager_action *cur;
03404    struct timespec tv = { 5, };
03405 
03406    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03407       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03408       return -1;
03409    }
03410    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
03411       if (!strcasecmp(action, cur->action)) {
03412          AST_RWLIST_REMOVE_CURRENT(list);
03413          ast_free(cur);
03414          ast_verb(2, "Manager unregistered action %s\n", action);
03415          break;
03416       }
03417    }
03418    AST_RWLIST_TRAVERSE_SAFE_END;
03419    AST_RWLIST_UNLOCK(&actions);
03420 
03421    return 0;
03422 }
03423 
03424 static int manager_state_cb(char *context, char *exten, int state, void *data)
03425 {
03426    /* Notify managers of change */
03427    char hint[512];
03428    ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
03429 
03430    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
03431    return 0;
03432 }
03433 
03434 static int ast_manager_register_struct(struct manager_action *act)
03435 {
03436    struct manager_action *cur, *prev = NULL;
03437    struct timespec tv = { 5, };
03438 
03439    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03440       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03441       return -1;
03442    }
03443    AST_RWLIST_TRAVERSE(&actions, cur, list) {
03444       int ret = strcasecmp(cur->action, act->action);
03445       if (ret == 0) {
03446          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
03447          AST_RWLIST_UNLOCK(&actions);
03448          return -1;
03449       }
03450       if (ret > 0) { /* Insert these alphabetically */
03451          prev = cur;
03452          break;
03453       }
03454    }
03455 
03456    if (prev)
03457       AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
03458    else
03459       AST_RWLIST_INSERT_HEAD(&actions, act, list);
03460 
03461    ast_verb(2, "Manager registered action %s\n", act->action);
03462 
03463    AST_RWLIST_UNLOCK(&actions);
03464 
03465    return 0;
03466 }
03467 
03468 /*! \brief register a new command with manager, including online help. This is
03469    the preferred way to register a manager command */
03470 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
03471 {
03472    struct manager_action *cur = NULL;
03473 
03474    if (!(cur = ast_calloc(1, sizeof(*cur))))
03475       return -1;
03476 
03477    cur->action = action;
03478    cur->authority = auth;
03479    cur->func = func;
03480    cur->synopsis = synopsis;
03481    cur->description = description;
03482 
03483    if (ast_manager_register_struct(cur)) {
03484       ast_free(cur);
03485       return -1;
03486    }
03487 
03488    return 0;
03489 }
03490 /*! @}
03491  END Doxygen group */
03492 
03493 /*
03494  * The following are support functions for AMI-over-http.
03495  * The common entry point is generic_http_callback(),
03496  * which extracts HTTP header and URI fields and reformats
03497  * them into AMI messages, locates a proper session
03498  * (using the mansession_id Cookie or GET variable),
03499  * and calls process_message() as for regular AMI clients.
03500  * When done, the output (which goes to a temporary file)
03501  * is read back into a buffer and reformatted as desired,
03502  * then fed back to the client over the original socket.
03503  */
03504 
03505 enum output_format {
03506    FORMAT_RAW,
03507    FORMAT_HTML,
03508    FORMAT_XML,
03509 };
03510 
03511 static char *contenttype[] = {
03512    [FORMAT_RAW] = "plain",
03513    [FORMAT_HTML] = "html",
03514    [FORMAT_XML] =  "xml",
03515 };
03516 
03517 /*!
03518  * locate an http session in the list. The search key (ident) is
03519  * the value of the mansession_id cookie (0 is not valid and means
03520  * a session on the AMI socket).
03521  */
03522 static struct mansession_session *find_session(uint32_t ident, int incinuse)
03523 {
03524    struct mansession_session *session;
03525 
03526    if (ident == 0)
03527       return NULL;
03528 
03529    AST_LIST_LOCK(&sessions);
03530    AST_LIST_TRAVERSE(&sessions, session, list) {
03531       ast_mutex_lock(&session->__lock);
03532       if (session->managerid == ident && !session->needdestroy) {
03533          ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
03534          break;
03535       }
03536       ast_mutex_unlock(&session->__lock);
03537    }
03538    AST_LIST_UNLOCK(&sessions);
03539 
03540    return session;
03541 }
03542 
03543 int astman_is_authed(uint32_t ident) 
03544 {
03545    int authed;
03546    struct mansession_session *session;
03547 
03548    if (!(session = find_session(ident, 0)))
03549       return 0;
03550 
03551    authed = (session->authenticated != 0);
03552 
03553    ast_mutex_unlock(&session->__lock);
03554 
03555    return authed;
03556 }
03557 
03558 int astman_verify_session_readpermissions(uint32_t ident, int perm)
03559 {
03560    int result = 0;
03561    struct mansession_session *session;
03562 
03563    AST_LIST_LOCK(&sessions);
03564    AST_LIST_TRAVERSE(&sessions, session, list) {
03565       ast_mutex_lock(&session->__lock);
03566       if ((session->managerid == ident) && (session->readperm & perm)) {
03567          result = 1;
03568          ast_mutex_unlock(&session->__lock);
03569          break;
03570       }
03571       ast_mutex_unlock(&session->__lock);
03572    }
03573    AST_LIST_UNLOCK(&sessions);
03574    return result;
03575 }
03576 
03577 int astman_verify_session_writepermissions(uint32_t ident, int perm)
03578 {
03579    int result = 0;
03580    struct mansession_session *session;
03581 
03582    AST_LIST_LOCK(&sessions);
03583    AST_LIST_TRAVERSE(&sessions, session, list) {
03584       ast_mutex_lock(&session->__lock);
03585       if ((session->managerid == ident) && (session->writeperm & perm)) {
03586          result = 1;
03587          ast_mutex_unlock(&session->__lock);
03588          break;
03589       }
03590       ast_mutex_unlock(&session->__lock);
03591    }
03592    AST_LIST_UNLOCK(&sessions);
03593    return result;
03594 }
03595 
03596 /*
03597  * convert to xml with various conversion:
03598  * mode & 1 -> lowercase;
03599  * mode & 2 -> replace non-alphanumeric chars with underscore
03600  */
03601 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
03602 {
03603    /* store in a local buffer to avoid calling ast_str_append too often */
03604    char buf[256];
03605    char *dst = buf;
03606    int space = sizeof(buf);
03607    /* repeat until done and nothing to flush */
03608    for ( ; *src || dst != buf ; src++) {
03609       if (*src == '\0' || space < 10) {   /* flush */
03610          *dst++ = '\0';
03611          ast_str_append(out, 0, "%s", buf);
03612          dst = buf;
03613          space = sizeof(buf);
03614          if (*src == '\0')
03615             break;
03616       }
03617          
03618       if ( (mode & 2) && !isalnum(*src)) {
03619          *dst++ = '_';
03620          space--;
03621          continue;
03622       }
03623       switch (*src) {
03624       case '<':
03625          strcpy(dst, "&lt;");
03626          dst += 4;
03627          space -= 4;
03628          break;
03629       case '>':
03630          strcpy(dst, "&gt;");
03631          dst += 4;
03632          space -= 4;
03633          break;
03634       case '\"':
03635          strcpy(dst, "&quot;");
03636          dst += 6;
03637          space -= 6;
03638          break;
03639       case '\'':
03640          strcpy(dst, "&apos;");
03641          dst += 6;
03642          space -= 6;
03643          break;
03644       case '&':
03645          strcpy(dst, "&amp;");
03646          dst += 5;
03647          space -= 5;
03648          break;
03649 
03650       default:
03651          *dst++ = mode ? tolower(*src) : *src;
03652          space--;
03653       }
03654    }
03655 }
03656 
03657 struct variable_count {
03658    char *varname;
03659    int count;
03660 };
03661 
03662 static int compress_char(char c)
03663 {
03664    c &= 0x7f;
03665    if (c < 32)
03666       return 0;
03667    else if (c >= 'a' && c <= 'z')
03668       return c - 64;
03669    else if (c > 'z')
03670       return '_';
03671    else
03672       return c - 32;
03673 }
03674 
03675 static int variable_count_hash_fn(const void *vvc, const int flags)
03676 {
03677    const struct variable_count *vc = vvc;
03678    int res = 0, i;
03679    for (i = 0; i < 5; i++) {
03680       if (vc->varname[i] == '\0')
03681          break;
03682       res += compress_char(vc->varname[i]) << (i * 6);
03683    }
03684    return res;
03685 }
03686 
03687 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
03688 {
03689    /* Due to the simplicity of struct variable_count, it makes no difference
03690     * if you pass in objects or strings, the same operation applies. This is
03691     * due to the fact that the hash occurs on the first element, which means
03692     * the address of both the struct and the string are exactly the same. */
03693    struct variable_count *vc = obj;
03694    char *str = vstr;
03695    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
03696 }
03697 
03698 /*! \brief Convert the input into XML or HTML.
03699  * The input is supposed to be a sequence of lines of the form
03700  * Name: value
03701  * optionally followed by a blob of unformatted text.
03702  * A blank line is a section separator. Basically, this is a
03703  * mixture of the format of Manager Interface and CLI commands.
03704  * The unformatted text is considered as a single value of a field
03705  * named 'Opaque-data'.
03706  *
03707  * At the moment the output format is the following (but it may
03708  * change depending on future requirements so don't count too
03709  * much on it when writing applications):
03710  *
03711  * General: the unformatted text is used as a value of
03712  * XML output:  to be completed
03713  * 
03714  * \verbatim
03715  *   Each section is within <response type="object" id="xxx">
03716  *   where xxx is taken from ajaxdest variable or defaults to unknown
03717  *   Each row is reported as an attribute Name="value" of an XML
03718  *   entity named from the variable ajaxobjtype, default to "generic"
03719  * \endverbatim
03720  *
03721  * HTML output:
03722  *   each Name-value pair is output as a single row of a two-column table.
03723  *   Sections (blank lines in the input) are separated by a <HR>
03724  *
03725  */
03726 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
03727 {
03728    struct ast_variable *v;
03729    const char *dest = NULL;
03730    char *var, *val;
03731    const char *objtype = NULL;
03732    int in_data = 0;  /* parsing data */
03733    int inobj = 0;
03734    int xml = (format == FORMAT_XML);
03735    struct variable_count *vc = NULL;
03736    struct ao2_container *vco = NULL;
03737 
03738    for (v = vars; v; v = v->next) {
03739       if (!dest && !strcasecmp(v->name, "ajaxdest"))
03740          dest = v->value;
03741       else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
03742          objtype = v->value;
03743    }
03744    if (!dest)
03745       dest = "unknown";
03746    if (!objtype)
03747       objtype = "generic";
03748 
03749    /* we want to stop when we find an empty line */
03750    while (in && *in) {
03751       val = strsep(&in, "\r\n"); /* mark start and end of line */
03752       if (in && *in == '\n')     /* remove trailing \n if any */
03753          in++;
03754       ast_trim_blanks(val);
03755       ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
03756       if (ast_strlen_zero(val)) {
03757          if (in_data) { /* close data */
03758             ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03759             in_data = 0;
03760          }
03761          if (inobj) {
03762             ast_str_append(out, 0, xml ? " /></response>\n" :
03763                "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03764             inobj = 0;
03765             ao2_ref(vco, -1);
03766             vco = NULL;
03767          }
03768          continue;
03769       }
03770 
03771       /* we expect Name: value lines */
03772       if (in_data) {
03773          var = NULL;
03774       } else {
03775          var = strsep(&val, ":");
03776          if (val) {  /* found the field name */
03777             val = ast_skip_blanks(val);
03778             ast_trim_blanks(var);
03779          } else {    /* field name not found, move to opaque mode */
03780             val = var;
03781             var = "Opaque-data";
03782          }
03783       }
03784 
03785       if (!inobj) {
03786          if (xml)
03787             ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
03788          else
03789             ast_str_append(out, 0, "<body>\n");
03790          vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
03791          inobj = 1;
03792       }
03793 
03794       if (!in_data) {   /* build appropriate line start */
03795          ast_str_append(out, 0, xml ? " " : "<tr><td>");
03796          if ((vc = ao2_find(vco, var, 0)))
03797             vc->count++;
03798          else {
03799             /* Create a new entry for this one */
03800             vc = ao2_alloc(sizeof(*vc), NULL);
03801             vc->varname = var;
03802             vc->count = 1;
03803             ao2_link(vco, vc);
03804          }
03805          xml_copy_escape(out, var, xml ? 1 | 2 : 0);
03806          if (vc->count > 1)
03807             ast_str_append(out, 0, "-%d", vc->count);
03808          ao2_ref(vc, -1);
03809          ast_str_append(out, 0, xml ? "='" : "</td><td>");
03810          if (!strcmp(var, "Opaque-data"))
03811             in_data = 1;
03812       }
03813       xml_copy_escape(out, val, 0); /* data field */
03814       if (!in_data)
03815          ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03816       else
03817          ast_str_append(out, 0, xml ? "\n" : "<br>\n");
03818    }
03819    if (inobj) {
03820       ast_str_append(out, 0, xml ? " /></response>\n" :
03821          "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03822       ao2_ref(vco, -1);
03823    }
03824 }
03825 
03826 static struct ast_str *generic_http_callback(enum output_format format,
03827                     struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method,
03828                     struct ast_variable *params, int *status,
03829                     char **title, int *contentlength)
03830 {
03831    struct mansession s = {.session = NULL, };
03832    struct mansession_session *session = NULL;
03833    uint32_t ident = 0;
03834    int blastaway = 0;
03835    struct ast_variable *v;
03836    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
03837    struct ast_str *out = NULL;
03838    struct message m = { 0 };
03839    unsigned int x;
03840    size_t hdrlen;
03841 
03842    for (v = params; v; v = v->next) {
03843       if (!strcasecmp(v->name, "mansession_id")) {
03844          sscanf(v->value, "%30x", &ident);
03845          break;
03846       }
03847    }
03848 
03849    if (!(session = find_session(ident, 1))) {
03850       /* Create new session.
03851        * While it is not in the list we don't need any locking
03852        */
03853       if (!(session = ast_calloc(1, sizeof(*session)))) {
03854          *status = 500;
03855          goto generic_callback_out;
03856       }
03857       session->sin = *remote_address;
03858       session->fd = -1;
03859       session->waiting_thread = AST_PTHREADT_NULL;
03860       session->send_events = 0;
03861       ast_mutex_init(&session->__lock);
03862       ast_mutex_lock(&session->__lock);
03863       session->inuse = 1;
03864       /*!\note There is approximately a 1 in 1.8E19 chance that the following
03865        * calculation will produce 0, which is an invalid ID, but due to the
03866        * properties of the rand() function (and the constantcy of s), that
03867        * won't happen twice in a row.
03868        */
03869       while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
03870       session->last_ev = grab_last();
03871       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03872       AST_LIST_LOCK(&sessions);
03873       AST_LIST_INSERT_HEAD(&sessions, session, list);
03874       ast_atomic_fetchadd_int(&num_sessions, 1);
03875       AST_LIST_UNLOCK(&sessions);
03876    }
03877 
03878    s.session = session;
03879 
03880    ast_mutex_unlock(&session->__lock);
03881 
03882    if (!(out = ast_str_create(1024))) {
03883       *status = 500;
03884       goto generic_callback_out;
03885    }
03886 
03887    s.fd = mkstemp(template);  /* create a temporary file for command output */
03888    unlink(template);
03889    s.f = fdopen(s.fd, "w+");
03890 
03891    for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
03892       hdrlen = strlen(v->name) + strlen(v->value) + 3;
03893       m.headers[m.hdrcount] = alloca(hdrlen);
03894       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
03895       ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
03896       m.hdrcount = x + 1;
03897    }
03898 
03899    if (process_message(&s, &m)) {
03900       if (session->authenticated) {
03901          if (manager_displayconnects(session)) {
03902             ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03903          }
03904          ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03905       } else {
03906          if (displayconnects) {
03907             ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03908          }
03909          ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03910       }
03911       session->needdestroy = 1;
03912    }
03913 
03914    ast_str_append(&out, 0,
03915              "Content-type: text/%s\r\n"
03916              "Cache-Control: no-cache;\r\n"
03917              "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
03918              "Pragma: SuppressEvents\r\n"
03919              "\r\n",
03920          contenttype[format],
03921          session->managerid, httptimeout);
03922 
03923    if (format == FORMAT_XML) {
03924       ast_str_append(&out, 0, "<ajax-response>\n");
03925    } else if (format == FORMAT_HTML) {
03926       /*
03927        * When handling AMI-over-HTTP in HTML format, we provide a simple form for
03928        * debugging purposes. This HTML code should not be here, we
03929        * should read from some config file...
03930        */
03931 
03932 #define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
03933 #define TEST_STRING \
03934    "<form action=\"manager\">\n\
03935    Action: <select name=\"action\">\n\
03936       <option value=\"\">-----&gt;</option>\n\
03937       <option value=\"login\">login</option>\n\
03938       <option value=\"command\">Command</option>\n\
03939       <option value=\"waitevent\">waitevent</option>\n\
03940       <option value=\"listcommands\">listcommands</option>\n\
03941    </select>\n\
03942    or <input name=\"action\"><br/>\n\
03943    CLI Command <input name=\"command\"><br>\n\
03944    user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
03945    <input type=\"submit\">\n</form>\n"
03946 
03947       ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
03948       ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03949       ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
03950       ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
03951    }
03952 
03953    if (s.f != NULL) {   /* have temporary output */
03954       char *buf;
03955       size_t l;
03956       
03957       /* Ensure buffer is NULL-terminated */
03958       fprintf(s.f, "%c", 0);
03959 
03960       if ((l = ftell(s.f))) {
03961          if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) {
03962             if (format == FORMAT_XML || format == FORMAT_HTML)
03963                xml_translate(&out, buf, params, format);
03964             else
03965                ast_str_append(&out, 0, "%s", buf);
03966             munmap(buf, l);
03967          }
03968       } else if (format == FORMAT_XML || format == FORMAT_HTML) {
03969          xml_translate(&out, "", params, format);
03970       }
03971       fclose(s.f);
03972       s.f = NULL;
03973       s.fd = -1;
03974    }
03975 
03976    if (format == FORMAT_XML) {
03977       ast_str_append(&out, 0, "</ajax-response>\n");
03978    } else if (format == FORMAT_HTML)
03979       ast_str_append(&out, 0, "</table></body>\r\n");
03980 
03981    ast_mutex_lock(&session->__lock);
03982    /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
03983    session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
03984 
03985    if (session->needdestroy) {
03986       if (session->inuse == 1) {
03987          ast_debug(1, "Need destroy, doing it now!\n");
03988          blastaway = 1;
03989       } else {
03990          ast_debug(1, "Need destroy, but can't do it yet!\n");
03991          if (session->waiting_thread != AST_PTHREADT_NULL)
03992             pthread_kill(session->waiting_thread, SIGURG);
03993          session->inuse--;
03994       }
03995    } else
03996       session->inuse--;
03997    ast_mutex_unlock(&session->__lock);
03998 
03999    if (blastaway)
04000       destroy_session(session);
04001 generic_callback_out:
04002    if (*status != 200)
04003       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
04004    return out;
04005 }
04006 
04007 static struct ast_str *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 *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04008 {
04009    return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength);
04010 }
04011 
04012 static struct ast_str *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 *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04013 {
04014    return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength);
04015 }
04016 
04017 static struct ast_str *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 *params, struct ast_variable *headers, int *status, char **title, int *contentlength)
04018 {
04019    return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength);
04020 }
04021 
04022 struct ast_http_uri rawmanuri = {
04023    .description = "Raw HTTP Manager Event Interface",
04024    .uri = "rawman",
04025    .callback = rawman_http_callback,
04026    .supports_get = 1,
04027    .data = NULL,
04028    .key = __FILE__,
04029 };
04030 
04031 struct ast_http_uri manageruri = {
04032    .description = "HTML Manager Event Interface",
04033    .uri = "manager",
04034    .callback = manager_http_callback,
04035    .supports_get = 1,
04036    .data = NULL,
04037    .key = __FILE__,
04038 };
04039 
04040 struct ast_http_uri managerxmluri = {
04041    .description = "XML Manager Event Interface",
04042    .uri = "mxml",
04043    .callback = mxml_http_callback,
04044    .supports_get = 1,
04045    .data = NULL,
04046    .key = __FILE__,
04047 };
04048 
04049 static int registered = 0;
04050 static int webregged = 0;
04051 
04052 /*! \brief cleanup code called at each iteration of server_root,
04053  * guaranteed to happen every 5 seconds at most
04054  */
04055 static void purge_old_stuff(void *data)
04056 {
04057    purge_sessions(1);
04058    purge_events();
04059 }
04060 
04061 struct ast_tls_config ami_tls_cfg;
04062 static struct ast_tcptls_session_args ami_desc = {
04063    .accept_fd = -1,
04064    .master = AST_PTHREADT_NULL,
04065    .tls_cfg = NULL, 
04066    .poll_timeout = 5000,   /* wake up every 5 seconds */
04067    .periodic_fn = purge_old_stuff,
04068    .name = "AMI server",
04069    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
04070    .worker_fn = session_do,   /* thread handling the session */
04071 };
04072 
04073 static struct ast_tcptls_session_args amis_desc = {
04074    .accept_fd = -1,
04075    .master = AST_PTHREADT_NULL,
04076    .tls_cfg = &ami_tls_cfg, 
04077    .poll_timeout = -1,  /* the other does the periodic cleanup */
04078    .name = "AMI TLS server",
04079    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
04080    .worker_fn = session_do,   /* thread handling the session */
04081 };
04082 
04083 static int __init_manager(int reload)
04084 {
04085    struct ast_config *ucfg = NULL, *cfg = NULL;
04086    const char *val;
04087    char *cat = NULL;
04088    int newhttptimeout = 60;
04089    int have_sslbindaddr = 0;
04090    struct hostent *hp;
04091    struct ast_hostent ahp;
04092    struct ast_manager_user *user = NULL;
04093    struct ast_variable *var;
04094    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
04095 
04096    manager_enabled = 0;
04097 
04098    if (!registered) {
04099       /* Register default actions */
04100       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
04101       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
04102       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
04103       ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
04104       ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
04105       ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
04106       ast_manager_register2("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status", mandescr_status);
04107       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar);
04108       ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar);
04109       ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
04110       ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
04111       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
04112       ast_manager_register2("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig, "Creates an empty file in the configuration directory", mandescr_createconfig);
04113       ast_manager_register2("ListCategories", EVENT_FLAG_CONFIG, action_listcategories, "List categories in configuration file", mandescr_listcategories);
04114       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
04115       ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer);
04116       ast_manager_register2("Originate", EVENT_FLAG_ORIGINATE, action_originate, "Originate Call", mandescr_originate);
04117       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
04118       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
04119       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
04120       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
04121       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
04122       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
04123       ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
04124       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
04125       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
04126       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
04127       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
04128       ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
04129       ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
04130       ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
04131       ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
04132 
04133       ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
04134       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
04135       registered = 1;
04136       /* Append placeholder event so master_eventq never runs dry */
04137       append_event("Event: Placeholder\r\n\r\n", 0);
04138    }
04139    if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
04140       return 0;
04141 
04142    displayconnects = 1;
04143    broken_events_action = 0;
04144    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
04145       ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
04146       return 0;
04147    }
04148 
04149    /* default values */
04150    memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
04151    memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
04152    amis_desc.local_address.sin_port = htons(5039);
04153    ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT);
04154 
04155    ami_tls_cfg.enabled = 0;
04156    if (ami_tls_cfg.certfile)
04157       ast_free(ami_tls_cfg.certfile);
04158    ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
04159    if (ami_tls_cfg.cipher)
04160       ast_free(ami_tls_cfg.cipher);
04161    ami_tls_cfg.cipher = ast_strdup("");
04162 
04163    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
04164       val = var->value;
04165       if (!strcasecmp(var->name, "sslenable"))
04166          ami_tls_cfg.enabled = ast_true(val);
04167       else if (!strcasecmp(var->name, "sslbindport"))
04168          amis_desc.local_address.sin_port = htons(atoi(val));
04169       else if (!strcasecmp(var->name, "sslbindaddr")) {
04170          if ((hp = ast_gethostbyname(val, &ahp))) {
04171             memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr));
04172             have_sslbindaddr = 1;
04173          } else {
04174             ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
04175          }
04176       } else if (!strcasecmp(var->name, "sslcert")) {
04177          ast_free(ami_tls_cfg.certfile);
04178          ami_tls_cfg.certfile = ast_strdup(val);
04179       } else if (!strcasecmp(var->name, "sslcipher")) {
04180          ast_free(ami_tls_cfg.cipher);
04181          ami_tls_cfg.cipher = ast_strdup(val);
04182       } else if (!strcasecmp(var->name, "enabled")) {
04183          manager_enabled = ast_true(val);
04184       } else if (!strcasecmp(var->name, "block-sockets")) {
04185          block_sockets = ast_true(val);
04186       } else if (!strcasecmp(var->name, "webenabled")) {
04187          webmanager_enabled = ast_true(val);
04188       } else if (!strcasecmp(var->name, "port")) {
04189          ami_desc.local_address.sin_port = htons(atoi(val));
04190       } else if (!strcasecmp(var->name, "bindaddr")) {
04191          if (!inet_aton(val, &ami_desc.local_address.sin_addr)) {
04192             ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
04193             memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr));
04194          }
04195       } else if (!strcasecmp(var->name, "brokeneventsaction")) {
04196          broken_events_action = ast_true(val);
04197       } else if (!strcasecmp(var->name, "allowmultiplelogin")) { 
04198          allowmultiplelogin = ast_true(val);
04199       } else if (!strcasecmp(var->name, "displayconnects")) {
04200          displayconnects = ast_true(val);
04201       } else if (!strcasecmp(var->name, "timestampevents")) {
04202          timestampevents = ast_true(val);
04203       } else if (!strcasecmp(var->name, "debug")) {
04204          manager_debug = ast_true(val);
04205       } else if (!strcasecmp(var->name, "httptimeout")) {
04206          newhttptimeout = atoi(val);
04207       } else {
04208          ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
04209             var->name, val);
04210       }  
04211    }
04212 
04213    if (manager_enabled)
04214       ami_desc.local_address.sin_family = AF_INET;
04215    if (!have_sslbindaddr)
04216       amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr;
04217    if (ami_tls_cfg.enabled)
04218       amis_desc.local_address.sin_family = AF_INET;
04219 
04220    
04221    AST_RWLIST_WRLOCK(&users);
04222 
04223    /* First, get users from users.conf */
04224    ucfg = ast_config_load2("users.conf", "manager", config_flags);
04225    if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
04226       const char *hasmanager;
04227       int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
04228 
04229       while ((cat = ast_category_browse(ucfg, cat))) {
04230          if (!strcasecmp(cat, "general"))
04231             continue;
04232          
04233          hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
04234          if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
04235             const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
04236             const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
04237             const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
04238             const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
04239             const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
04240             
04241             /* Look for an existing entry,
04242              * if none found - create one and add it to the list
04243              */
04244             if (!(user = get_manager_by_name_locked(cat))) {
04245                if (!(user = ast_calloc(1, sizeof(*user))))
04246                   break;
04247 
04248                /* Copy name over */
04249                ast_copy_string(user->username, cat, sizeof(user->username));
04250                /* Insert into list */
04251                AST_LIST_INSERT_TAIL(&users, user, list);
04252                user->ha = NULL;
04253                user->keep = 1;
04254                user->readperm = -1;
04255                user->writeperm = -1;
04256                /* Default displayconnect from [general] */
04257                user->displayconnects = displayconnects;
04258                user->writetimeout = 100;
04259             }
04260 
04261             if (!user_secret)
04262                user_secret = ast_variable_retrieve(ucfg, "general", "secret");
04263             if (!user_read)
04264                user_read = ast_variable_retrieve(ucfg, "general", "read");
04265             if (!user_write)
04266                user_write = ast_variable_retrieve(ucfg, "general", "write");
04267             if (!user_displayconnects)
04268                user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
04269             if (!user_writetimeout)
04270                user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
04271 
04272             if (!ast_strlen_zero(user_secret)) {
04273                if (user->secret)
04274                   ast_free(user->secret);
04275                user->secret = ast_strdup(user_secret);
04276             }
04277 
04278             if (user_read)
04279                user->readperm = get_perm(user_read);
04280             if (user_write)
04281                user->writeperm = get_perm(user_write);
04282             if (user_displayconnects)
04283                user->displayconnects = ast_true(user_displayconnects);
04284 
04285             if (user_writetimeout) {
04286                int value = atoi(user_writetimeout);
04287                if (value < 100)
04288                   ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
04289                else
04290                   user->writetimeout = value;
04291             }
04292          }
04293       }
04294       ast_config_destroy(ucfg);
04295    }
04296 
04297    /* cat is NULL here in any case */
04298 
04299    while ((cat = ast_category_browse(cfg, cat))) {
04300       struct ast_ha *oldha;
04301 
04302       if (!strcasecmp(cat, "general"))
04303          continue;
04304 
04305       /* Look for an existing entry, if none found - create one and add it to the list */
04306       if (!(user = get_manager_by_name_locked(cat))) {
04307          if (!(user = ast_calloc(1, sizeof(*user))))
04308             break;
04309          /* Copy name over */
04310          ast_copy_string(user->username, cat, sizeof(user->username));
04311 
04312          user->ha = NULL;
04313          user->readperm = 0;
04314          user->writeperm = 0;
04315          /* Default displayconnect from [general] */
04316          user->displayconnects = displayconnects;
04317          user->writetimeout = 100;
04318 
04319          /* Insert into list */
04320          AST_RWLIST_INSERT_TAIL(&users, user, list);
04321       }
04322 
04323       /* Make sure we keep this user and don't destroy it during cleanup */
04324       user->keep = 1;
04325       oldha = user->ha;
04326       user->ha = NULL;
04327 
04328       var = ast_variable_browse(cfg, cat);
04329       for (; var; var = var->next) {
04330          if (!strcasecmp(var->name, "secret")) {
04331             if (user->secret)
04332                ast_free(user->secret);
04333             user->secret = ast_strdup(var->value);
04334          } else if (!strcasecmp(var->name, "deny") ||
04335                    !strcasecmp(var->name, "permit")) {
04336             user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
04337          }  else if (!strcasecmp(var->name, "read") ) {
04338             user->readperm = get_perm(var->value);
04339          }  else if (!strcasecmp(var->name, "write") ) {
04340             user->writeperm = get_perm(var->value);
04341          }  else if (!strcasecmp(var->name, "displayconnects") ) {
04342             user->displayconnects = ast_true(var->value);
04343          } else if (!strcasecmp(var->name, "writetimeout")) {
04344             int value = atoi(var->value);
04345             if (value < 100)
04346                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
04347             else
04348                user->writetimeout = value;
04349          } else
04350             ast_debug(1, "%s is an unknown option.\n", var->name);
04351       }
04352       ast_free_ha(oldha);
04353    }
04354    ast_config_destroy(cfg);
04355 
04356    /* Perform cleanup - essentially prune out old users that no longer exist */
04357    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
04358       if (user->keep) { /* valid record. clear flag for the next round */
04359          user->keep = 0;
04360          continue;
04361       }
04362       /* We do not need to keep this user so take them out of the list */
04363       AST_RWLIST_REMOVE_CURRENT(list);
04364       /* Free their memory now */
04365       if (user->secret)
04366          ast_free(user->secret);
04367       ast_free_ha(user->ha);
04368       ast_free(user);
04369    }
04370    AST_RWLIST_TRAVERSE_SAFE_END;
04371 
04372    AST_RWLIST_UNLOCK(&users);
04373 
04374    if (webmanager_enabled && manager_enabled) {
04375       if (!webregged) {
04376          ast_http_uri_link(&rawmanuri);
04377          ast_http_uri_link(&manageruri);
04378          ast_http_uri_link(&managerxmluri);
04379          webregged = 1;
04380       }
04381    } else {
04382       if (webregged) {
04383          ast_http_uri_unlink(&rawmanuri);
04384          ast_http_uri_unlink(&manageruri);
04385          ast_http_uri_unlink(&managerxmluri);
04386          webregged = 0;
04387       }
04388    }
04389 
04390    if (newhttptimeout > 0)
04391       httptimeout = newhttptimeout;
04392 
04393    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
04394 
04395    ast_tcptls_server_start(&ami_desc);
04396    if (ast_ssl_setup(amis_desc.tls_cfg))
04397       ast_tcptls_server_start(&amis_desc);
04398    return 0;
04399 }
04400 
04401 int init_manager(void)
04402 {
04403    return __init_manager(0);
04404 }
04405 
04406 int reload_manager(void)
04407 {
04408    return __init_manager(1);
04409 }
04410 
04411 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
04412 {
04413    AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
04414 
04415    return 0;
04416 }
04417 
04418 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
04419 {
04420    return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
04421 }
04422 
04423 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
04424 {
04425    struct ast_datastore *datastore = NULL;
04426    
04427    if (info == NULL)
04428       return NULL;
04429 
04430    AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
04431       if (datastore->info != info) {
04432          continue;
04433       }
04434 
04435       if (uid == NULL) {
04436          /* matched by type only */
04437          break;
04438       }
04439 
04440       if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
04441          /* Matched by type AND uid */
04442          break;
04443       }
04444    }
04445    AST_LIST_TRAVERSE_SAFE_END;
04446 
04447    return datastore;
04448 }