Thu Apr 8 01:21:00 2010

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

Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1