Tue Aug 24 2010 19:41:31

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