Tue Mar 2 17:31:50 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: 232584 $")
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    { -1, "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 
01792    if (ast_strlen_zero(varname)) {
01793       astman_send_error(s, m, "No variable specified");
01794       return 0;
01795    }
01796 
01797    if (!ast_strlen_zero(name)) {
01798       c = ast_get_channel_by_name_locked(name);
01799       if (!c) {
01800          astman_send_error(s, m, "No such channel");
01801          return 0;
01802       }
01803    }
01804 
01805    pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01806 
01807    if (c)
01808       ast_channel_unlock(c);
01809 
01810    astman_send_ack(s, m, "Variable Set");
01811 
01812    return 0;
01813 }
01814 
01815 static char mandescr_getvar[] =
01816 "Description: Get the value of a global or local channel variable.\n"
01817 "Variables: (Names marked with * are required)\n"
01818 "  Channel: Channel to read variable from\n"
01819 "  *Variable: Variable name\n"
01820 "  ActionID: Optional Action id for message matching.\n";
01821 
01822 static int action_getvar(struct mansession *s, const struct message *m)
01823 {
01824    struct ast_channel *c = NULL;
01825    const char *name = astman_get_header(m, "Channel");
01826    const char *varname = astman_get_header(m, "Variable");
01827    char *varval;
01828    char workspace[1024] = "";
01829 
01830    if (ast_strlen_zero(varname)) {
01831       astman_send_error(s, m, "No variable specified");
01832       return 0;
01833    }
01834 
01835    if (!ast_strlen_zero(name)) {
01836       c = ast_get_channel_by_name_locked(name);
01837       if (!c) {
01838          astman_send_error(s, m, "No such channel");
01839          return 0;
01840       }
01841    }
01842 
01843    if (varname[strlen(varname) - 1] == ')') {
01844       if (!c) {
01845          c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01846          if (c) {
01847             ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01848             ast_channel_free(c);
01849             c = NULL;
01850          } else
01851             ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
01852       } else
01853          ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
01854       varval = workspace;
01855    } else {
01856       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01857    }
01858 
01859    if (c)
01860       ast_channel_unlock(c);
01861    astman_start_ack(s, m);
01862    astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
01863 
01864    return 0;
01865 }
01866 
01867 static char mandescr_status[] = 
01868 "Description: Lists channel status along with requested channel vars.\n"
01869 "Variables: (Names marked with * are required)\n"
01870 "  *Channel: Name of the channel to query for status\n"
01871 "  Variables: Comma ',' separated list of variables to include\n"
01872 "  ActionID: Optional ID for this transaction\n"
01873 "Will return the status information of each channel along with the\n"
01874 "value for the specified channel variables.\n";
01875  
01876 
01877 /*! \brief Manager "status" command to show channels */
01878 /* Needs documentation... */
01879 static int action_status(struct mansession *s, const struct message *m)
01880 {
01881    const char *name = astman_get_header(m, "Channel");
01882    const char *cvariables = astman_get_header(m, "Variables");
01883    char *variables = ast_strdupa(S_OR(cvariables, ""));
01884    struct ast_channel *c;
01885    char bridge[256];
01886    struct timeval now = ast_tvnow();
01887    long elapsed_seconds = 0;
01888    int channels = 0;
01889    int all = ast_strlen_zero(name); /* set if we want all channels */
01890    const char *id = astman_get_header(m, "ActionID");
01891    char idText[256];
01892    AST_DECLARE_APP_ARGS(vars,
01893       AST_APP_ARG(name)[100];
01894    );
01895    struct ast_str *str = ast_str_create(1000);
01896 
01897    if (!ast_strlen_zero(id))
01898       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01899    else
01900       idText[0] = '\0';
01901 
01902    if (all)
01903       c = ast_channel_walk_locked(NULL);
01904    else {
01905       c = ast_get_channel_by_name_locked(name);
01906       if (!c) {
01907          astman_send_error(s, m, "No such channel");
01908          ast_free(str);
01909          return 0;
01910       }
01911    }
01912    astman_send_ack(s, m, "Channel status will follow");
01913 
01914    if (!ast_strlen_zero(cvariables)) {
01915       AST_STANDARD_APP_ARGS(vars, variables);
01916    }
01917 
01918    /* if we look by name, we break after the first iteration */
01919    while (c) {
01920       if (!ast_strlen_zero(cvariables)) {
01921          int i;
01922          ast_str_reset(str);
01923          for (i = 0; i < vars.argc; i++) {
01924             char valbuf[512], *ret = NULL;
01925 
01926             if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
01927                if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
01928                   valbuf[0] = '\0';
01929                }
01930                ret = valbuf;
01931             } else {
01932                pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
01933             }
01934 
01935             ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
01936          }
01937       }
01938 
01939       channels++;
01940       if (c->_bridge)
01941          snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
01942       else
01943          bridge[0] = '\0';
01944       if (c->pbx) {
01945          if (c->cdr) {
01946             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01947          }
01948          astman_append(s,
01949          "Event: Status\r\n"
01950          "Privilege: Call\r\n"
01951          "Channel: %s\r\n"
01952          "CallerIDNum: %s\r\n"
01953          "CallerIDName: %s\r\n"
01954          "Accountcode: %s\r\n"
01955          "ChannelState: %d\r\n"
01956          "ChannelStateDesc: %s\r\n"
01957          "Context: %s\r\n"
01958          "Extension: %s\r\n"
01959          "Priority: %d\r\n"
01960          "Seconds: %ld\r\n"
01961          "%s"
01962          "Uniqueid: %s\r\n"
01963          "%s"
01964          "%s"
01965          "\r\n",
01966          c->name,
01967          S_OR(c->cid.cid_num, ""),
01968          S_OR(c->cid.cid_name, ""),
01969          c->accountcode,
01970          c->_state,
01971          ast_state2str(c->_state), c->context,
01972          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
01973       } else {
01974          astman_append(s,
01975          "Event: Status\r\n"
01976          "Privilege: Call\r\n"
01977          "Channel: %s\r\n"
01978          "CallerIDNum: %s\r\n"
01979          "CallerIDName: %s\r\n"
01980          "Account: %s\r\n"
01981          "State: %s\r\n"
01982          "%s"
01983          "Uniqueid: %s\r\n"
01984          "%s"
01985          "%s"
01986          "\r\n",
01987          c->name,
01988          S_OR(c->cid.cid_num, "<unknown>"),
01989          S_OR(c->cid.cid_name, "<unknown>"),
01990          c->accountcode,
01991          ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
01992       }
01993       ast_channel_unlock(c);
01994       if (!all)
01995          break;
01996       c = ast_channel_walk_locked(c);
01997    }
01998    astman_append(s,
01999    "Event: StatusComplete\r\n"
02000    "%s"
02001    "Items: %d\r\n"
02002    "\r\n", idText, channels);
02003    ast_free(str);
02004    return 0;
02005 }
02006 
02007 static char mandescr_sendtext[] =
02008 "Description: Sends A Text Message while in a call.\n"
02009 "Variables: (Names marked with * are required)\n"
02010 "       *Channel: Channel to send message to\n"
02011 "       *Message: Message to send\n"
02012 "       ActionID: Optional Action id for message matching.\n";
02013 
02014 static int action_sendtext(struct mansession *s, const struct message *m)
02015 {
02016    struct ast_channel *c = NULL;
02017    const char *name = astman_get_header(m, "Channel");
02018    const char *textmsg = astman_get_header(m, "Message");
02019    int res = 0;
02020 
02021    if (ast_strlen_zero(name)) {
02022       astman_send_error(s, m, "No channel specified");
02023       return 0;
02024    }
02025 
02026    if (ast_strlen_zero(textmsg)) {
02027       astman_send_error(s, m, "No Message specified");
02028       return 0;
02029    }
02030 
02031    c = ast_get_channel_by_name_locked(name);
02032    if (!c) {
02033       astman_send_error(s, m, "No such channel");
02034       return 0;
02035    }
02036 
02037    res = ast_sendtext(c, textmsg);
02038    ast_channel_unlock(c);
02039    
02040    if (res > 0)
02041       astman_send_ack(s, m, "Success");
02042    else
02043       astman_send_error(s, m, "Failure");
02044    
02045    return res;
02046 }
02047 
02048 static char mandescr_redirect[] =
02049 "Description: Redirect (transfer) a call.\n"
02050 "Variables: (Names marked with * are required)\n"
02051 "  *Channel: Channel to redirect\n"
02052 "  ExtraChannel: Second call leg to transfer (optional)\n"
02053 "  *Exten: Extension to transfer to\n"
02054 "  *Context: Context to transfer to\n"
02055 "  *Priority: Priority to transfer to\n"
02056 "  ActionID: Optional Action id for message matching.\n";
02057 
02058 /*! \brief  action_redirect: The redirect manager command */
02059 static int action_redirect(struct mansession *s, const struct message *m)
02060 {
02061    const char *name = astman_get_header(m, "Channel");
02062    const char *name2 = astman_get_header(m, "ExtraChannel");
02063    const char *exten = astman_get_header(m, "Exten");
02064    const char *context = astman_get_header(m, "Context");
02065    const char *priority = astman_get_header(m, "Priority");
02066    struct ast_channel *chan, *chan2 = NULL;
02067    int pi = 0;
02068    int res;
02069 
02070    if (ast_strlen_zero(name)) {
02071       astman_send_error(s, m, "Channel not specified");
02072       return 0;
02073    }
02074    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02075       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02076          astman_send_error(s, m, "Invalid priority");
02077          return 0;
02078       }
02079    }
02080    /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
02081    chan = ast_get_channel_by_name_locked(name);
02082    if (!chan) {
02083       char buf[256];
02084       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
02085       astman_send_error(s, m, buf);
02086       return 0;
02087    }
02088    if (ast_check_hangup(chan)) {
02089       astman_send_error(s, m, "Redirect failed, channel not up.");
02090       ast_channel_unlock(chan);
02091       return 0;
02092    }
02093    if (!ast_strlen_zero(name2))
02094       chan2 = ast_get_channel_by_name_locked(name2);
02095    if (chan2 && ast_check_hangup(chan2)) {
02096       astman_send_error(s, m, "Redirect failed, extra channel not up.");
02097       ast_channel_unlock(chan);
02098       ast_channel_unlock(chan2);
02099       return 0;
02100    }
02101    if (chan->pbx) {
02102       ast_channel_lock(chan);
02103       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02104       ast_channel_unlock(chan);
02105    }
02106    res = ast_async_goto(chan, context, exten, pi);
02107    if (!res) {
02108       if (!ast_strlen_zero(name2)) {
02109          if (chan2) {
02110             if (chan2->pbx) {
02111                ast_channel_lock(chan2);
02112                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
02113                ast_channel_unlock(chan2);
02114             }
02115             res = ast_async_goto(chan2, context, exten, pi);
02116          } else {
02117             res = -1;
02118          }
02119          if (!res)
02120             astman_send_ack(s, m, "Dual Redirect successful");
02121          else
02122             astman_send_error(s, m, "Secondary redirect failed");
02123       } else
02124          astman_send_ack(s, m, "Redirect successful");
02125    } else
02126       astman_send_error(s, m, "Redirect failed");
02127    if (chan)
02128       ast_channel_unlock(chan);
02129    if (chan2)
02130       ast_channel_unlock(chan2);
02131    return 0;
02132 }
02133 
02134 static char mandescr_atxfer[] =
02135 "Description: Attended transfer.\n"
02136 "Variables: (Names marked with * are required)\n"
02137 "  *Channel: Transferer's channel\n"
02138 "  *Exten: Extension to transfer to\n"
02139 "  *Context: Context to transfer to\n"
02140 "  *Priority: Priority to transfer to\n"
02141 "  ActionID: Optional Action id for message matching.\n";
02142 
02143 static int action_atxfer(struct mansession *s, const struct message *m)
02144 {
02145    const char *name = astman_get_header(m, "Channel");
02146    const char *exten = astman_get_header(m, "Exten");
02147    const char *context = astman_get_header(m, "Context");
02148    struct ast_channel *chan = NULL;
02149    struct ast_call_feature *atxfer_feature = NULL;
02150    char *feature_code = NULL;
02151 
02152    if (ast_strlen_zero(name)) { 
02153       astman_send_error(s, m, "No channel specified");
02154       return 0;
02155    }
02156    if (ast_strlen_zero(exten)) {
02157       astman_send_error(s, m, "No extension specified");
02158       return 0;
02159    }
02160 
02161    if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
02162       astman_send_error(s, m, "No attended transfer feature found");
02163       return 0;
02164    }
02165 
02166    if (!(chan = ast_get_channel_by_name_locked(name))) {
02167       astman_send_error(s, m, "Channel specified does not exist");
02168       return 0;
02169    }
02170 
02171    if (!ast_strlen_zero(context)) {
02172       pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
02173    }
02174 
02175    for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
02176       struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02177       ast_queue_frame(chan, &f);
02178    }
02179 
02180    for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
02181       struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
02182       ast_queue_frame(chan, &f);
02183    }
02184 
02185    astman_send_ack(s, m, "Atxfer successfully queued");
02186    ast_channel_unlock(chan);
02187 
02188    return 0;
02189 }
02190 
02191 static int check_blacklist(const char *cmd)
02192 {
02193    char *cmd_copy, *cur_cmd;
02194    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
02195    int i;
02196 
02197    cmd_copy = ast_strdupa(cmd);
02198    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
02199       cur_cmd = ast_strip(cur_cmd);
02200       if (ast_strlen_zero(cur_cmd)) {
02201          i--;
02202          continue;
02203       }
02204 
02205       cmd_words[i] = cur_cmd;
02206    }
02207 
02208    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
02209       int j, match = 1;
02210 
02211       for (j = 0; command_blacklist[i].words[j]; j++) {
02212          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
02213             match = 0;
02214             break;
02215          }
02216       }
02217 
02218       if (match) {
02219          return 1;
02220       }
02221    }
02222 
02223    return 0;
02224 }
02225 
02226 static char mandescr_command[] =
02227 "Description: Run a CLI command.\n"
02228 "Variables: (Names marked with * are required)\n"
02229 "  *Command: Asterisk CLI command to run\n"
02230 "  ActionID: Optional Action id for message matching.\n";
02231 
02232 /*! \brief  Manager command "command" - execute CLI command */
02233 static int action_command(struct mansession *s, const struct message *m)
02234 {
02235    const char *cmd = astman_get_header(m, "Command");
02236    const char *id = astman_get_header(m, "ActionID");
02237    char *buf, *final_buf;
02238    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
02239    int fd;
02240    off_t l;
02241 
02242    if (ast_strlen_zero(cmd)) {
02243       astman_send_error(s, m, "No command provided");
02244       return 0;
02245    }
02246 
02247    if (check_blacklist(cmd)) {
02248       astman_send_error(s, m, "Command blacklisted");
02249       return 0;
02250    }
02251 
02252    fd = mkstemp(template);
02253 
02254    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
02255    if (!ast_strlen_zero(id))
02256       astman_append(s, "ActionID: %s\r\n", id);
02257    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
02258    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
02259    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
02260 
02261    /* This has a potential to overflow the stack.  Hence, use the heap. */
02262    buf = ast_calloc(1, l + 1);
02263    final_buf = ast_calloc(1, l + 1);
02264    if (buf) {
02265       lseek(fd, 0, SEEK_SET);
02266       if (read(fd, buf, l) < 0) {
02267          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
02268       }
02269       buf[l] = '\0';
02270       if (final_buf) {
02271          term_strip(final_buf, buf, l);
02272          final_buf[l] = '\0';
02273       }
02274       astman_append(s, "%s", S_OR(final_buf, buf));
02275       ast_free(buf);
02276    }
02277    close(fd);
02278    unlink(template);
02279    astman_append(s, "--END COMMAND--\r\n\r\n");
02280    if (final_buf)
02281       ast_free(final_buf);
02282    return 0;
02283 }
02284 
02285 /*! \brief helper function for originate */
02286 struct fast_originate_helper {
02287    char tech[AST_MAX_EXTENSION];
02288    /*! data can contain a channel name, extension number, username, password, etc. */
02289    char data[512];
02290    int timeout;
02291    int format;          /*!< Codecs used for a call */
02292    char app[AST_MAX_APP];
02293    char appdata[AST_MAX_EXTENSION];
02294    char cid_name[AST_MAX_EXTENSION];
02295    char cid_num[AST_MAX_EXTENSION];
02296    char context[AST_MAX_CONTEXT];
02297    char exten[AST_MAX_EXTENSION];
02298    char idtext[AST_MAX_EXTENSION];
02299    char account[AST_MAX_ACCOUNT_CODE];
02300    int priority;
02301    struct ast_variable *vars;
02302 };
02303 
02304 static void *fast_originate(void *data)
02305 {
02306    struct fast_originate_helper *in = data;
02307    int res;
02308    int reason = 0;
02309    struct ast_channel *chan = NULL;
02310    char requested_channel[AST_CHANNEL_NAME];
02311 
02312    if (!ast_strlen_zero(in->app)) {
02313       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
02314          S_OR(in->cid_num, NULL),
02315          S_OR(in->cid_name, NULL),
02316          in->vars, in->account, &chan);
02317    } else {
02318       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
02319          S_OR(in->cid_num, NULL),
02320          S_OR(in->cid_name, NULL),
02321          in->vars, in->account, &chan);
02322    }
02323 
02324    if (!chan)
02325       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
02326    /* Tell the manager what happened with the channel */
02327    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
02328       "%s%s"
02329       "Response: %s\r\n"
02330       "Channel: %s\r\n"
02331       "Context: %s\r\n"
02332       "Exten: %s\r\n"
02333       "Reason: %d\r\n"
02334       "Uniqueid: %s\r\n"
02335       "CallerIDNum: %s\r\n"
02336       "CallerIDName: %s\r\n",
02337       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", 
02338       chan ? chan->name : requested_channel, in->context, in->exten, reason, 
02339       chan ? chan->uniqueid : "<null>",
02340       S_OR(in->cid_num, "<unknown>"),
02341       S_OR(in->cid_name, "<unknown>")
02342       );
02343 
02344    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
02345    if (chan)
02346       ast_channel_unlock(chan);
02347    ast_free(in);
02348    return NULL;
02349 }
02350 
02351 static char mandescr_originate[] =
02352 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
02353 "  Application/Data\n"
02354 "Variables: (Names marked with * are required)\n"
02355 "  *Channel: Channel name to call\n"
02356 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
02357 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
02358 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
02359 "  Application: Application to use\n"
02360 "  Data: Data to use (requires 'Application')\n"
02361 "  Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
02362 "  CallerID: Caller ID to be set on the outgoing channel\n"
02363 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
02364 "  Account: Account code\n"
02365 "  Async: Set to 'true' for fast origination\n";
02366 
02367 static int action_originate(struct mansession *s, const struct message *m)
02368 {
02369    const char *name = astman_get_header(m, "Channel");
02370    const char *exten = astman_get_header(m, "Exten");
02371    const char *context = astman_get_header(m, "Context");
02372    const char *priority = astman_get_header(m, "Priority");
02373    const char *timeout = astman_get_header(m, "Timeout");
02374    const char *callerid = astman_get_header(m, "CallerID");
02375    const char *account = astman_get_header(m, "Account");
02376    const char *app = astman_get_header(m, "Application");
02377    const char *appdata = astman_get_header(m, "Data");
02378    const char *async = astman_get_header(m, "Async");
02379    const char *id = astman_get_header(m, "ActionID");
02380    const char *codecs = astman_get_header(m, "Codecs");
02381    struct ast_variable *vars = astman_get_variables(m);
02382    char *tech, *data;
02383    char *l = NULL, *n = NULL;
02384    int pi = 0;
02385    int res;
02386    int to = 30000;
02387    int reason = 0;
02388    char tmp[256];
02389    char tmp2[256];
02390    int format = AST_FORMAT_SLINEAR;
02391 
02392    pthread_t th;
02393    if (ast_strlen_zero(name)) {
02394       astman_send_error(s, m, "Channel not specified");
02395       return 0;
02396    }
02397    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02398       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02399          astman_send_error(s, m, "Invalid priority");
02400          return 0;
02401       }
02402    }
02403    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02404       astman_send_error(s, m, "Invalid timeout");
02405       return 0;
02406    }
02407    ast_copy_string(tmp, name, sizeof(tmp));
02408    tech = tmp;
02409    data = strchr(tmp, '/');
02410    if (!data) {
02411       astman_send_error(s, m, "Invalid channel");
02412       return 0;
02413    }
02414    *data++ = '\0';
02415    ast_copy_string(tmp2, callerid, sizeof(tmp2));
02416    ast_callerid_parse(tmp2, &n, &l);
02417    if (n) {
02418       if (ast_strlen_zero(n))
02419          n = NULL;
02420    }
02421    if (l) {
02422       ast_shrink_phone_number(l);
02423       if (ast_strlen_zero(l))
02424          l = NULL;
02425    }
02426    if (!ast_strlen_zero(codecs)) {
02427       format = 0;
02428       ast_parse_allow_disallow(NULL, &format, codecs, 1);
02429    }
02430    if (ast_true(async)) {
02431       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02432       if (!fast) {
02433          res = -1;
02434       } else {
02435          if (!ast_strlen_zero(id))
02436             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02437          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02438             ast_copy_string(fast->data, data, sizeof(fast->data));
02439          ast_copy_string(fast->app, app, sizeof(fast->app));
02440          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02441          if (l)
02442             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02443          if (n)
02444             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02445          fast->vars = vars;
02446          ast_copy_string(fast->context, context, sizeof(fast->context));
02447          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02448          ast_copy_string(fast->account, account, sizeof(fast->account));
02449          fast->format = format;
02450          fast->timeout = to;
02451          fast->priority = pi;
02452          if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
02453             ast_free(fast);
02454             res = -1;
02455          } else {
02456             res = 0;
02457          }
02458       }
02459    } else if (!ast_strlen_zero(app)) {
02460       /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
02461       if (!(s->session->writeperm & EVENT_FLAG_SYSTEM)
02462          && (
02463             strcasestr(app, "system") == 0 || /* System(rm -rf /)
02464                                                  TrySystem(rm -rf /)       */
02465             strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
02466                                                  TryExec(System(rm -rf /)) */
02467             strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
02468                                                  EAGI(/bin/rm,-rf /)       */
02469             strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
02470             strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
02471             )) {
02472          astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
02473          return 0;
02474       }
02475       res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02476    } else {
02477       if (exten && context && pi)
02478          res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02479       else {
02480          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02481          return 0;
02482       }
02483    }
02484    if (!res)
02485       astman_send_ack(s, m, "Originate successfully queued");
02486    else
02487       astman_send_error(s, m, "Originate failed");
02488    return 0;
02489 }
02490 
02491 /*! \brief Help text for manager command mailboxstatus
02492  */
02493 static char mandescr_mailboxstatus[] =
02494 "Description: Checks a voicemail account for status.\n"
02495 "Variables: (Names marked with * are required)\n"
02496 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02497 "  ActionID: Optional ActionID for message matching.\n"
02498 "Returns number of messages.\n"
02499 "  Message: Mailbox Status\n"
02500 "  Mailbox: <mailboxid>\n"
02501 "  Waiting: <count>\n"
02502 "\n";
02503 
02504 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02505 {
02506    const char *mailbox = astman_get_header(m, "Mailbox");
02507    int ret;
02508 
02509    if (ast_strlen_zero(mailbox)) {
02510       astman_send_error(s, m, "Mailbox not specified");
02511       return 0;
02512    }
02513    ret = ast_app_has_voicemail(mailbox, NULL);
02514    astman_start_ack(s, m);
02515    astman_append(s, "Message: Mailbox Status\r\n"
02516           "Mailbox: %s\r\n"
02517           "Waiting: %d\r\n\r\n", mailbox, ret);
02518    return 0;
02519 }
02520 
02521 static char mandescr_mailboxcount[] =
02522 "Description: Checks a voicemail account for new messages.\n"
02523 "Variables: (Names marked with * are required)\n"
02524 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02525 "  ActionID: Optional ActionID for message matching.\n"
02526 "Returns number of urgent, new and old messages.\n"
02527 "  Message: Mailbox Message Count\n"
02528 "  Mailbox: <mailboxid>\n"
02529 "  UrgentMessages: <count>\n"
02530 "  NewMessages: <count>\n"
02531 "  OldMessages: <count>\n"
02532 "\n";
02533 static int action_mailboxcount(struct mansession *s, const struct message *m)
02534 {
02535    const char *mailbox = astman_get_header(m, "Mailbox");
02536    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
02537 
02538    if (ast_strlen_zero(mailbox)) {
02539       astman_send_error(s, m, "Mailbox not specified");
02540       return 0;
02541    }
02542    ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
02543    astman_start_ack(s, m);
02544    astman_append(s,   "Message: Mailbox Message Count\r\n"
02545             "Mailbox: %s\r\n"
02546             "UrgMessages: %d\r\n"
02547             "NewMessages: %d\r\n"
02548             "OldMessages: %d\r\n"
02549             "\r\n",
02550             mailbox, urgentmsgs, newmsgs, oldmsgs);
02551    return 0;
02552 }
02553 
02554 static char mandescr_extensionstate[] =
02555 "Description: Report the extension state for given extension.\n"
02556 "  If the extension has a hint, will use devicestate to check\n"
02557 "  the status of the device connected to the extension.\n"
02558 "Variables: (Names marked with * are required)\n"
02559 "  *Exten: Extension to check state on\n"
02560 "  *Context: Context for extension\n"
02561 "  ActionId: Optional ID for this transaction\n"
02562 "Will return an \"Extension Status\" message.\n"
02563 "The response will include the hint for the extension and the status.\n";
02564 
02565 static int action_extensionstate(struct mansession *s, const struct message *m)
02566 {
02567    const char *exten = astman_get_header(m, "Exten");
02568    const char *context = astman_get_header(m, "Context");
02569    char hint[256] = "";
02570    int status;
02571    if (ast_strlen_zero(exten)) {
02572       astman_send_error(s, m, "Extension not specified");
02573       return 0;
02574    }
02575    if (ast_strlen_zero(context))
02576       context = "default";
02577    status = ast_extension_state(NULL, context, exten);
02578    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02579    astman_start_ack(s, m);
02580    astman_append(s,   "Message: Extension Status\r\n"
02581             "Exten: %s\r\n"
02582             "Context: %s\r\n"
02583             "Hint: %s\r\n"
02584             "Status: %d\r\n\r\n",
02585             exten, context, hint, status);
02586    return 0;
02587 }
02588 
02589 static char mandescr_timeout[] =
02590 "Description: Hangup a channel after a certain time.\n"
02591 "Variables: (Names marked with * are required)\n"
02592 "  *Channel: Channel name to hangup\n"
02593 "  *Timeout: Maximum duration of the call (sec)\n"
02594 "Acknowledges set time with 'Timeout Set' message\n";
02595 
02596 static int action_timeout(struct mansession *s, const struct message *m)
02597 {
02598    struct ast_channel *c;
02599    const char *name = astman_get_header(m, "Channel");
02600    double timeout = atof(astman_get_header(m, "Timeout"));
02601    struct timeval when = { timeout, 0 };
02602 
02603    if (ast_strlen_zero(name)) {
02604       astman_send_error(s, m, "No channel specified");
02605       return 0;
02606    }
02607    if (!timeout || timeout < 0) {
02608       astman_send_error(s, m, "No timeout specified");
02609       return 0;
02610    }
02611    c = ast_get_channel_by_name_locked(name);
02612    if (!c) {
02613       astman_send_error(s, m, "No such channel");
02614       return 0;
02615    }
02616 
02617    when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
02618    ast_channel_setwhentohangup_tv(c, when);
02619    ast_channel_unlock(c);
02620    astman_send_ack(s, m, "Timeout Set");
02621    return 0;
02622 }
02623 
02624 /*!
02625  * Send any applicable events to the client listening on this socket.
02626  * Wait only for a finite time on each event, and drop all events whether
02627  * they are successfully sent or not.
02628  */
02629 static int process_events(struct mansession *s)
02630 {
02631    int ret = 0;
02632 
02633    ast_mutex_lock(&s->session->__lock);
02634    if (s->session->f != NULL) {
02635       struct eventqent *eqe;
02636 
02637       while ( (eqe = NEW_EVENT(s)) ) {
02638          ref_event(eqe);
02639          if (!ret && s->session->authenticated &&
02640              (s->session->readperm & eqe->category) == eqe->category &&
02641              (s->session->send_events & eqe->category) == eqe->category) {
02642             if (send_string(s, eqe->eventdata) < 0)
02643                ret = -1;   /* don't send more */
02644          }
02645          s->session->last_ev = unref_event(s->session->last_ev);
02646       }
02647    }
02648    ast_mutex_unlock(&s->session->__lock);
02649    return ret;
02650 }
02651 
02652 static char mandescr_userevent[] =
02653 "Description: Send an event to manager sessions.\n"
02654 "Variables: (Names marked with * are required)\n"
02655 "       *UserEvent: EventStringToSend\n"
02656 "       Header1: Content1\n"
02657 "       HeaderN: ContentN\n";
02658 
02659 static int action_userevent(struct mansession *s, const struct message *m)
02660 {
02661    const char *event = astman_get_header(m, "UserEvent");
02662    struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
02663    int x;
02664 
02665    ast_str_reset(body);
02666 
02667    for (x = 0; x < m->hdrcount; x++) {
02668       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02669          ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
02670       }
02671    }
02672 
02673    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body->str);
02674    astman_send_ack(s, m, "Event Sent");   
02675    return 0;
02676 }
02677 
02678 static char mandescr_coresettings[] =
02679 "Description: Query for Core PBX settings.\n"
02680 "Variables: (Names marked with * are optional)\n"
02681 "       *ActionID: ActionID of this transaction\n";
02682 
02683 /*! \brief Show PBX core settings information */
02684 static int action_coresettings(struct mansession *s, const struct message *m)
02685 {
02686    const char *actionid = astman_get_header(m, "ActionID");
02687    char idText[150];
02688 
02689    if (!ast_strlen_zero(actionid))
02690       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02691    else
02692       idText[0] = '\0';
02693 
02694    astman_append(s, "Response: Success\r\n"
02695          "%s"
02696          "AMIversion: %s\r\n"
02697          "AsteriskVersion: %s\r\n"
02698          "SystemName: %s\r\n"
02699          "CoreMaxCalls: %d\r\n"
02700          "CoreMaxLoadAvg: %f\r\n"
02701          "CoreRunUser: %s\r\n"
02702          "CoreRunGroup: %s\r\n"
02703          "CoreMaxFilehandles: %d\r\n" 
02704          "CoreRealTimeEnabled: %s\r\n"
02705          "CoreCDRenabled: %s\r\n"
02706          "CoreHTTPenabled: %s\r\n"
02707          "\r\n",
02708          idText,
02709          AMI_VERSION,
02710          ast_get_version(), 
02711          ast_config_AST_SYSTEM_NAME,
02712          option_maxcalls,
02713          option_maxload,
02714          ast_config_AST_RUN_USER,
02715          ast_config_AST_RUN_GROUP,
02716          option_maxfiles,
02717          ast_realtime_enabled() ? "Yes" : "No",
02718          check_cdr_enabled() ? "Yes" : "No",
02719          check_webmanager_enabled() ? "Yes" : "No"
02720          );
02721    return 0;
02722 }
02723 
02724 static char mandescr_corestatus[] =
02725 "Description: Query for Core PBX status.\n"
02726 "Variables: (Names marked with * are optional)\n"
02727 "       *ActionID: ActionID of this transaction\n";
02728 
02729 /*! \brief Show PBX core status information */
02730 static int action_corestatus(struct mansession *s, const struct message *m)
02731 {
02732    const char *actionid = astman_get_header(m, "ActionID");
02733    char idText[150];
02734    char startuptime[150];
02735    char reloadtime[150];
02736    struct ast_tm tm;
02737 
02738    if (!ast_strlen_zero(actionid))
02739       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02740    else
02741       idText[0] = '\0';
02742 
02743    ast_localtime(&ast_startuptime, &tm, NULL);
02744    ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02745    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02746    ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02747 
02748    astman_append(s, "Response: Success\r\n"
02749          "%s"
02750          "CoreStartupTime: %s\r\n"
02751          "CoreReloadTime: %s\r\n"
02752          "CoreCurrentCalls: %d\r\n"
02753          "\r\n",
02754          idText,
02755          startuptime,
02756          reloadtime,
02757          ast_active_channels()
02758          );
02759    return 0;
02760 }
02761 
02762 static char mandescr_reload[] =
02763 "Description: Send a reload event.\n"
02764 "Variables: (Names marked with * are optional)\n"
02765 "       *ActionID: ActionID of this transaction\n"
02766 "       *Module: Name of the module to reload\n";
02767 
02768 /*! \brief Send a reload event */
02769 static int action_reload(struct mansession *s, const struct message *m)
02770 {
02771    const char *module = astman_get_header(m, "Module");
02772    int res = ast_module_reload(S_OR(module, NULL));
02773 
02774    if (res == 2)
02775       astman_send_ack(s, m, "Module Reloaded");
02776    else
02777       astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
02778    return 0;
02779 }
02780 
02781 static char mandescr_coreshowchannels[] =
02782 "Description: List currently defined channels and some information\n"
02783 "             about them.\n"
02784 "Variables:\n"
02785 "          ActionID: Optional Action id for message matching.\n";
02786 
02787 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels 
02788  *          and some information about them. */
02789 static int action_coreshowchannels(struct mansession *s, const struct message *m)
02790 {
02791    const char *actionid = astman_get_header(m, "ActionID");
02792    char actionidtext[256];
02793    struct ast_channel *c = NULL;
02794    int numchans = 0;
02795    int duration, durh, durm, durs;
02796 
02797    if (!ast_strlen_zero(actionid))
02798       snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
02799    else
02800       actionidtext[0] = '\0';
02801 
02802    astman_send_listack(s, m, "Channels will follow", "start"); 
02803 
02804    while ((c = ast_channel_walk_locked(c)) != NULL) {
02805       struct ast_channel *bc = ast_bridged_channel(c);
02806       char durbuf[10] = "";
02807 
02808       if (c->cdr && !ast_tvzero(c->cdr->start)) {
02809          duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
02810          durh = duration / 3600;
02811          durm = (duration % 3600) / 60;
02812          durs = duration % 60;
02813          snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
02814       }
02815 
02816       astman_append(s,
02817          "Event: CoreShowChannel\r\n"
02818          "Channel: %s\r\n"
02819          "UniqueID: %s\r\n"
02820          "Context: %s\r\n"
02821          "Extension: %s\r\n"
02822          "Priority: %d\r\n"
02823          "ChannelState: %d\r\n"
02824          "ChannelStateDesc: %s\r\n"
02825          "Application: %s\r\n"
02826          "ApplicationData: %s\r\n"
02827          "CallerIDnum: %s\r\n"
02828          "Duration: %s\r\n"
02829          "AccountCode: %s\r\n"
02830          "BridgedChannel: %s\r\n"
02831          "BridgedUniqueID: %s\r\n"
02832          "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
02833          c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
02834          S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
02835       ast_channel_unlock(c);
02836       numchans++;
02837    }
02838 
02839    astman_append(s,
02840       "Event: CoreShowChannelsComplete\r\n"
02841       "EventList: Complete\r\n"
02842       "ListItems: %d\r\n"
02843       "%s"
02844       "\r\n", numchans, actionidtext);
02845 
02846    return 0;
02847 }
02848 
02849 static char mandescr_modulecheck[] = 
02850 "Description: Checks if Asterisk module is loaded\n"
02851 "Variables: \n"
02852 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
02853 "  Module: <name>          Asterisk module name (not including extension)\n"
02854 "\n"
02855 "Will return Success/Failure\n"
02856 "For success returns, the module revision number is included.\n";
02857 
02858 /* Manager function to check if module is loaded */
02859 static int manager_modulecheck(struct mansession *s, const struct message *m)
02860 {
02861    int res;
02862    const char *module = astman_get_header(m, "Module");
02863    const char *id = astman_get_header(m, "ActionID");
02864    char idText[256];
02865 #if !defined(LOW_MEMORY)
02866    const char *version;
02867 #endif
02868    char filename[PATH_MAX];
02869    char *cut;
02870 
02871    ast_copy_string(filename, module, sizeof(filename));
02872    if ((cut = strchr(filename, '.'))) {
02873       *cut = '\0';
02874    } else {
02875       cut = filename + strlen(filename);
02876    }
02877    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
02878    ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
02879    res = ast_module_check(filename);
02880    if (!res) {
02881       astman_send_error(s, m, "Module not loaded");
02882       return 0;
02883    }
02884    snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
02885    ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
02886 #if !defined(LOW_MEMORY)
02887    version = ast_file_version_find(filename);
02888 #endif
02889 
02890    if (!ast_strlen_zero(id))
02891       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02892    else
02893       idText[0] = '\0';
02894    astman_append(s, "Response: Success\r\n%s", idText);
02895 #if !defined(LOW_MEMORY)
02896    astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
02897 #endif
02898    return 0;
02899 }
02900 
02901 static char mandescr_moduleload[] = 
02902 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
02903 "Variables: \n"
02904 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
02905 "  Module: <name>          Asterisk module name (including .so extension)\n"
02906 "                          or subsystem identifier:\n"
02907 "           cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
02908 "  LoadType: load | unload | reload\n"
02909 "                          The operation to be done on module\n"
02910 " If no module is specified for a reload loadtype, all modules are reloaded";
02911 
02912 static int manager_moduleload(struct mansession *s, const struct message *m)
02913 {
02914    int res;
02915    const char *module = astman_get_header(m, "Module");
02916    const char *loadtype = astman_get_header(m, "LoadType");
02917 
02918    if (!loadtype || strlen(loadtype) == 0)
02919       astman_send_error(s, m, "Incomplete ModuleLoad action.");
02920    if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
02921       astman_send_error(s, m, "Need module name");
02922 
02923    if (!strcasecmp(loadtype, "load")) {
02924       res = ast_load_resource(module);
02925       if (res)
02926          astman_send_error(s, m, "Could not load module.");
02927       else
02928          astman_send_ack(s, m, "Module loaded.");
02929    } else if (!strcasecmp(loadtype, "unload")) {
02930       res = ast_unload_resource(module, AST_FORCE_SOFT);
02931       if (res)
02932          astman_send_error(s, m, "Could not unload module.");
02933       else
02934          astman_send_ack(s, m, "Module unloaded.");
02935    } else if (!strcasecmp(loadtype, "reload")) {
02936       if (module != NULL) {
02937          res = ast_module_reload(module);
02938          if (res == 0)
02939             astman_send_error(s, m, "No such module.");
02940          else if (res == 1)
02941             astman_send_error(s, m, "Module does not support reload action.");
02942          else
02943             astman_send_ack(s, m, "Module reloaded.");
02944       } else {
02945          ast_module_reload(NULL);   /* Reload all modules */
02946          astman_send_ack(s, m, "All modules reloaded");
02947       }
02948    } else 
02949       astman_send_error(s, m, "Incomplete ModuleLoad action.");
02950    return 0;
02951 }
02952 
02953 /*
02954  * Done with the action handlers here, we start with the code in charge
02955  * of accepting connections and serving them.
02956  * accept_thread() forks a new thread for each connection, session_do(),
02957  * which in turn calls get_input() repeatedly until a full message has
02958  * been accumulated, and then invokes process_message() to pass it to
02959  * the appropriate handler.
02960  */
02961 
02962 /*
02963  * Process an AMI message, performing desired action.
02964  * Return 0 on success, -1 on error that require the session to be destroyed.
02965  */
02966 static int process_message(struct mansession *s, const struct message *m)
02967 {
02968    char action[80] = "";
02969    int ret = 0;
02970    struct manager_action *tmp;
02971    const char *user = astman_get_header(m, "Username");
02972 
02973    ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
02974    ast_debug(1, "Manager received command '%s'\n", action);
02975 
02976    if (ast_strlen_zero(action)) {
02977       ast_mutex_lock(&s->session->__lock);
02978       astman_send_error(s, m, "Missing action in request");
02979       ast_mutex_unlock(&s->session->__lock);
02980       return 0;
02981    }
02982 
02983    if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
02984       ast_mutex_lock(&s->session->__lock);
02985       astman_send_error(s, m, "Permission denied");
02986       ast_mutex_unlock(&s->session->__lock);
02987       return 0;
02988    }
02989 
02990    if (!allowmultiplelogin && !s->session->authenticated && user &&
02991       (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
02992       if (check_manager_session_inuse(user)) {
02993          sleep(1);
02994          ast_mutex_lock(&s->session->__lock);
02995          astman_send_error(s, m, "Login Already In Use");
02996          ast_mutex_unlock(&s->session->__lock);
02997          return -1;
02998       }
02999    }
03000 
03001    AST_RWLIST_RDLOCK(&actions);
03002    AST_RWLIST_TRAVERSE(&actions, tmp, list) {
03003       if (strcasecmp(action, tmp->action))
03004          continue;
03005       if (s->session->writeperm & tmp->authority || tmp->authority == 0)
03006          ret = tmp->func(s, m);
03007       else
03008          astman_send_error(s, m, "Permission denied");
03009       break;
03010    }
03011    AST_RWLIST_UNLOCK(&actions);
03012 
03013    if (!tmp) {
03014       char buf[512];
03015       snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
03016       ast_mutex_lock(&s->session->__lock);
03017       astman_send_error(s, m, buf);
03018       ast_mutex_unlock(&s->session->__lock);
03019    }
03020    if (ret)
03021       return ret;
03022    /* Once done with our message, deliver any pending events */
03023    return process_events(s);
03024 }
03025 
03026 /*!
03027  * Read one full line (including crlf) from the manager socket.
03028  * \note \verbatim
03029  * \r\n is the only valid terminator for the line.
03030  * (Note that, later, '\0' will be considered as the end-of-line marker,
03031  * so everything between the '\0' and the '\r\n' will not be used).
03032  * Also note that we assume output to have at least "maxlen" space.
03033  * \endverbatim
03034  */
03035 static int get_input(struct mansession *s, char *output)
03036 {
03037    int res, x;
03038    int maxlen = sizeof(s->session->inbuf) - 1;
03039    char *src = s->session->inbuf;
03040 
03041    /*
03042     * Look for \r\n within the buffer. If found, copy to the output
03043     * buffer and return, trimming the \r\n (not used afterwards).
03044     */
03045    for (x = 0; x < s->session->inlen; x++) {
03046       int cr;  /* set if we have \r */
03047       if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n')
03048          cr = 2;  /* Found. Update length to include \r\n */
03049       else if (src[x] == '\n')
03050          cr = 1;  /* also accept \n only */
03051       else
03052          continue;
03053       memmove(output, src, x);   /*... but trim \r\n */
03054       output[x] = '\0';    /* terminate the string */
03055       x += cr;       /* number of bytes used */
03056       s->session->inlen -= x;       /* remaining size */
03057       memmove(src, src + x, s->session->inlen); /* remove used bytes */
03058       return 1;
03059    }
03060    if (s->session->inlen >= maxlen) {
03061       /* no crlf found, and buffer full - sorry, too long for us */
03062       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->session->sin.sin_addr), src);
03063       s->session->inlen = 0;
03064    }
03065    res = 0;
03066    while (res == 0) {
03067       /* XXX do we really need this locking ? */
03068       ast_mutex_lock(&s->session->__lock);
03069       if (s->session->pending_event) {
03070          s->session->pending_event = 0;
03071          ast_mutex_unlock(&s->session->__lock);
03072          return 0;
03073       }
03074       s->session->waiting_thread = pthread_self();
03075       ast_mutex_unlock(&s->session->__lock);
03076 
03077       res = ast_wait_for_input(s->session->fd, -1);   /* return 0 on timeout ? */
03078 
03079       ast_mutex_lock(&s->session->__lock);
03080       s->session->waiting_thread = AST_PTHREADT_NULL;
03081       ast_mutex_unlock(&s->session->__lock);
03082    }
03083    if (res < 0) {
03084       /* If we get a signal from some other thread (typically because
03085        * there are new events queued), return 0 to notify the caller.
03086        */
03087       if (errno == EINTR || errno == EAGAIN)
03088          return 0;
03089       ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
03090       return -1;
03091    }
03092    ast_mutex_lock(&s->session->__lock);
03093    res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f);
03094    if (res < 1)
03095       res = -1;   /* error return */
03096    else {
03097       s->session->inlen += res;
03098       src[s->session->inlen] = '\0';
03099       res = 0;
03100    }
03101    ast_mutex_unlock(&s->session->__lock);
03102    return res;
03103 }
03104 
03105 static int do_message(struct mansession *s)
03106 {
03107    struct message m = { 0 };
03108    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
03109    int res;
03110 
03111    for (;;) {
03112       /* Check if any events are pending and do them if needed */
03113       if (process_events(s))
03114          return -1;
03115       res = get_input(s, header_buf);
03116       if (res == 0) {
03117          continue;
03118       } else if (res > 0) {
03119          if (ast_strlen_zero(header_buf))
03120             return process_message(s, &m) ? -1 : 0;
03121          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
03122             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
03123       } else {
03124          return res;
03125       }
03126    }
03127 }
03128 
03129 /*! \brief The body of the individual manager session.
03130  * Call get_input() to read one line at a time
03131  * (or be woken up on new events), collect the lines in a
03132  * message until found an empty line, and execute the request.
03133  * In any case, deliver events asynchronously through process_events()
03134  * (called from here if no line is available, or at the end of
03135  * process_message(). )
03136  */
03137 static void *session_do(void *data)
03138 {
03139    struct ast_tcptls_session_instance *ser = data;
03140    struct mansession_session *session = ast_calloc(1, sizeof(*session));
03141    struct mansession s = {.session = NULL, };
03142    int flags;
03143    int res;
03144 
03145    if (session == NULL)
03146       goto done;
03147 
03148    session->writetimeout = 100;
03149    session->waiting_thread = AST_PTHREADT_NULL;
03150 
03151    flags = fcntl(ser->fd, F_GETFL);
03152    if (!block_sockets) /* make sure socket is non-blocking */
03153       flags |= O_NONBLOCK;
03154    else
03155       flags &= ~O_NONBLOCK;
03156    fcntl(ser->fd, F_SETFL, flags);
03157 
03158    ast_mutex_init(&session->__lock);
03159    session->send_events = -1;
03160    /* Hook to the tail of the event queue */
03161    session->last_ev = grab_last();
03162 
03163    /* these fields duplicate those in the 'ser' structure */
03164    session->fd = ser->fd;
03165    session->f = ser->f;
03166    session->sin = ser->remote_address;
03167    s.session = session;
03168 
03169    AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03170 
03171    AST_LIST_LOCK(&sessions);
03172    AST_LIST_INSERT_HEAD(&sessions, session, list);
03173    ast_atomic_fetchadd_int(&num_sessions, 1);
03174    AST_LIST_UNLOCK(&sessions);
03175 
03176    astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);   /* welcome prompt */
03177    for (;;) {
03178       if ((res = do_message(&s)) < 0)
03179          break;
03180    }
03181    /* session is over, explain why and terminate */
03182    if (session->authenticated) {
03183          if (manager_displayconnects(session))
03184          ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03185       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03186    } else {
03187          if (displayconnects)
03188          ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03189       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03190    }
03191 
03192    /* It is possible under certain circumstances for this session thread
03193       to complete its work and exit *before* the thread that created it
03194       has finished executing the ast_pthread_create_background() function.
03195       If this occurs, some versions of glibc appear to act in a buggy
03196       fashion and attempt to write data into memory that it thinks belongs
03197       to the thread but is in fact not owned by the thread (or may have
03198       been freed completely).
03199 
03200       Causing this thread to yield to other threads at least one time
03201       appears to work around this bug.
03202    */
03203    usleep(1);
03204 
03205    destroy_session(session);
03206 
03207 done:
03208    ao2_ref(ser, -1);
03209    ser = NULL;
03210    return NULL;
03211 }
03212 
03213 /*! \brief remove at most n_max stale session from the list. */
03214 static void purge_sessions(int n_max)
03215 {
03216    struct mansession_session *session;
03217    time_t now = time(NULL);
03218 
03219    AST_LIST_LOCK(&sessions);
03220    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) {
03221       if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
03222          AST_LIST_REMOVE_CURRENT(list);
03223          ast_atomic_fetchadd_int(&num_sessions, -1);
03224          if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) {
03225             ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
03226                session->username, ast_inet_ntoa(session->sin.sin_addr));
03227          }
03228          free_session(session);  /* XXX outside ? */
03229          if (--n_max <= 0)
03230             break;
03231       }
03232    }
03233    AST_LIST_TRAVERSE_SAFE_END;
03234    AST_LIST_UNLOCK(&sessions);
03235 }
03236 
03237 /*
03238  * events are appended to a queue from where they
03239  * can be dispatched to clients.
03240  */
03241 static int append_event(const char *str, int category)
03242 {
03243    struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
03244    static int seq;   /* sequence number */
03245 
03246    if (!tmp)
03247       return -1;
03248 
03249    /* need to init all fields, because ast_malloc() does not */
03250    tmp->usecount = 0;
03251    tmp->category = category;
03252    tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
03253    AST_LIST_NEXT(tmp, eq_next) = NULL;
03254    strcpy(tmp->eventdata, str);
03255 
03256    AST_LIST_LOCK(&all_events);
03257    AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
03258    AST_LIST_UNLOCK(&all_events);
03259 
03260    return 0;
03261 }
03262 
03263 /* XXX see if can be moved inside the function */
03264 AST_THREADSTORAGE(manager_event_buf);
03265 #define MANAGER_EVENT_BUF_INITSIZE   256
03266 
03267 /*! \brief  manager_event: Send AMI event to client */
03268 int __manager_event(int category, const char *event,
03269    const char *file, int line, const char *func, const char *fmt, ...)
03270 {
03271    struct mansession_session *session;
03272    struct manager_custom_hook *hook;
03273    struct ast_str *auth = ast_str_alloca(80);
03274    const char *cat_str;
03275    va_list ap;
03276    struct timeval now;
03277    struct ast_str *buf;
03278 
03279    /* Abort if there aren't any manager sessions */
03280    if (!num_sessions)
03281       return 0;
03282 
03283    if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
03284       return -1;
03285 
03286    cat_str = authority_to_str(category, &auth);
03287    ast_str_set(&buf, 0,
03288          "Event: %s\r\nPrivilege: %s\r\n",
03289           event, cat_str);
03290 
03291    if (timestampevents) {
03292       now = ast_tvnow();
03293       ast_str_append(&buf, 0,
03294             "Timestamp: %ld.%06lu\r\n",
03295              (long)now.tv_sec, (unsigned long) now.tv_usec);
03296    }
03297    if (manager_debug) {
03298       static int seq;
03299       ast_str_append(&buf, 0,
03300             "SequenceNumber: %d\r\n",
03301              ast_atomic_fetchadd_int(&seq, 1));
03302       ast_str_append(&buf, 0,
03303             "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
03304    }
03305 
03306    va_start(ap, fmt);
03307    ast_str_append_va(&buf, 0, fmt, ap);
03308    va_end(ap);
03309 
03310    ast_str_append(&buf, 0, "\r\n");
03311 
03312    append_event(buf->str, category);
03313 
03314    /* Wake up any sleeping sessions */
03315    AST_LIST_LOCK(&sessions);
03316    AST_LIST_TRAVERSE(&sessions, session, list) {
03317       ast_mutex_lock(&session->__lock);
03318       if (session->waiting_thread != AST_PTHREADT_NULL)
03319          pthread_kill(session->waiting_thread, SIGURG);
03320       else
03321          /* We have an event to process, but the mansession is
03322           * not waiting for it. We still need to indicate that there
03323           * is an event waiting so that get_input processes the pending
03324           * event instead of polling.
03325           */
03326          session->pending_event = 1;
03327       ast_mutex_unlock(&session->__lock);
03328    }
03329    AST_LIST_UNLOCK(&sessions);
03330 
03331    AST_RWLIST_RDLOCK(&manager_hooks);
03332    AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
03333       hook->helper(category, event, buf->str);
03334    }
03335    AST_RWLIST_UNLOCK(&manager_hooks);
03336 
03337    return 0;
03338 }
03339 
03340 /*
03341  * support functions to register/unregister AMI action handlers,
03342  */
03343 int ast_manager_unregister(char *action)
03344 {
03345    struct manager_action *cur;
03346    struct timespec tv = { 5, };
03347 
03348    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03349       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03350       return -1;
03351    }
03352    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
03353       if (!strcasecmp(action, cur->action)) {
03354          AST_RWLIST_REMOVE_CURRENT(list);
03355          ast_free(cur);
03356          ast_verb(2, "Manager unregistered action %s\n", action);
03357          break;
03358       }
03359    }
03360    AST_RWLIST_TRAVERSE_SAFE_END;
03361    AST_RWLIST_UNLOCK(&actions);
03362 
03363    return 0;
03364 }
03365 
03366 static int manager_state_cb(char *context, char *exten, int state, void *data)
03367 {
03368    /* Notify managers of change */
03369    char hint[512];
03370    ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
03371 
03372    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
03373    return 0;
03374 }
03375 
03376 static int ast_manager_register_struct(struct manager_action *act)
03377 {
03378    struct manager_action *cur, *prev = NULL;
03379    struct timespec tv = { 5, };
03380 
03381    if (AST_RWLIST_TIMEDWRLOCK(&actions, &tv)) {
03382       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
03383       return -1;
03384    }
03385    AST_RWLIST_TRAVERSE(&actions, cur, list) {
03386       int ret = strcasecmp(cur->action, act->action);
03387       if (ret == 0) {
03388          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
03389          AST_RWLIST_UNLOCK(&actions);
03390          return -1;
03391       }
03392       if (ret > 0) { /* Insert these alphabetically */
03393          prev = cur;
03394          break;
03395       }
03396    }
03397 
03398    if (prev)
03399       AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
03400    else
03401       AST_RWLIST_INSERT_HEAD(&actions, act, list);
03402 
03403    ast_verb(2, "Manager registered action %s\n", act->action);
03404 
03405    AST_RWLIST_UNLOCK(&actions);
03406 
03407    return 0;
03408 }
03409 
03410 /*! \brief register a new command with manager, including online help. This is
03411    the preferred way to register a manager command */
03412 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
03413 {
03414    struct manager_action *cur = NULL;
03415 
03416    if (!(cur = ast_calloc(1, sizeof(*cur))))
03417       return -1;
03418 
03419    cur->action = action;
03420    cur->authority = auth;
03421    cur->func = func;
03422    cur->synopsis = synopsis;
03423    cur->description = description;
03424 
03425    if (ast_manager_register_struct(cur)) {
03426       ast_free(cur);
03427       return -1;
03428    }
03429 
03430    return 0;
03431 }
03432 /*! @}
03433  END Doxygen group */
03434 
03435 /*
03436  * The following are support functions for AMI-over-http.
03437  * The common entry point is generic_http_callback(),
03438  * which extracts HTTP header and URI fields and reformats
03439  * them into AMI messages, locates a proper session
03440  * (using the mansession_id Cookie or GET variable),
03441  * and calls process_message() as for regular AMI clients.
03442  * When done, the output (which goes to a temporary file)
03443  * is read back into a buffer and reformatted as desired,
03444  * then fed back to the client over the original socket.
03445  */
03446 
03447 enum output_format {
03448    FORMAT_RAW,
03449    FORMAT_HTML,
03450    FORMAT_XML,
03451 };
03452 
03453 static char *contenttype[] = {
03454    [FORMAT_RAW] = "plain",
03455    [FORMAT_HTML] = "html",
03456    [FORMAT_XML] =  "xml",
03457 };
03458 
03459 /*!
03460  * locate an http session in the list. The search key (ident) is
03461  * the value of the mansession_id cookie (0 is not valid and means
03462  * a session on the AMI socket).
03463  */
03464 static struct mansession_session *find_session(uint32_t ident, int incinuse)
03465 {
03466    struct mansession_session *session;
03467 
03468    if (ident == 0)
03469       return NULL;
03470 
03471    AST_LIST_LOCK(&sessions);
03472    AST_LIST_TRAVERSE(&sessions, session, list) {
03473       ast_mutex_lock(&session->__lock);
03474       if (session->managerid == ident && !session->needdestroy) {
03475          ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
03476          break;
03477       }
03478       ast_mutex_unlock(&session->__lock);
03479    }
03480    AST_LIST_UNLOCK(&sessions);
03481 
03482    return session;
03483 }
03484 
03485 int astman_is_authed(uint32_t ident) 
03486 {
03487    int authed;
03488    struct mansession_session *session;
03489 
03490    if (!(session = find_session(ident, 0)))
03491       return 0;
03492 
03493    authed = (session->authenticated != 0);
03494 
03495    ast_mutex_unlock(&session->__lock);
03496 
03497    return authed;
03498 }
03499 
03500 int astman_verify_session_readpermissions(uint32_t ident, int perm)
03501 {
03502    int result = 0;
03503    struct mansession_session *session;
03504 
03505    AST_LIST_LOCK(&sessions);
03506    AST_LIST_TRAVERSE(&sessions, session, list) {
03507       ast_mutex_lock(&session->__lock);
03508       if ((session->managerid == ident) && (session->readperm & perm)) {
03509          result = 1;
03510          ast_mutex_unlock(&session->__lock);
03511          break;
03512       }
03513       ast_mutex_unlock(&session->__lock);
03514    }
03515    AST_LIST_UNLOCK(&sessions);
03516    return result;
03517 }
03518 
03519 int astman_verify_session_writepermissions(uint32_t ident, int perm)
03520 {
03521    int result = 0;
03522    struct mansession_session *session;
03523 
03524    AST_LIST_LOCK(&sessions);
03525    AST_LIST_TRAVERSE(&sessions, session, list) {
03526       ast_mutex_lock(&session->__lock);
03527       if ((session->managerid == ident) && (session->writeperm & perm)) {
03528          result = 1;
03529          ast_mutex_unlock(&session->__lock);
03530          break;
03531       }
03532       ast_mutex_unlock(&session->__lock);
03533    }
03534    AST_LIST_UNLOCK(&sessions);
03535    return result;
03536 }
03537 
03538 /*
03539  * convert to xml with various conversion:
03540  * mode & 1 -> lowercase;
03541  * mode & 2 -> replace non-alphanumeric chars with underscore
03542  */
03543 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
03544 {
03545    /* store in a local buffer to avoid calling ast_str_append too often */
03546    char buf[256];
03547    char *dst = buf;
03548    int space = sizeof(buf);
03549    /* repeat until done and nothing to flush */
03550    for ( ; *src || dst != buf ; src++) {
03551       if (*src == '\0' || space < 10) {   /* flush */
03552          *dst++ = '\0';
03553          ast_str_append(out, 0, "%s", buf);
03554          dst = buf;
03555          space = sizeof(buf);
03556          if (*src == '\0')
03557             break;
03558       }
03559          
03560       if ( (mode & 2) && !isalnum(*src)) {
03561          *dst++ = '_';
03562          space--;
03563          continue;
03564       }
03565       switch (*src) {
03566       case '<':
03567          strcpy(dst, "&lt;");
03568          dst += 4;
03569          space -= 4;
03570          break;
03571       case '>':
03572          strcpy(dst, "&gt;");
03573          dst += 4;
03574          space -= 4;
03575          break;
03576       case '\"':
03577          strcpy(dst, "&quot;");
03578          dst += 6;
03579          space -= 6;
03580          break;
03581       case '\'':
03582          strcpy(dst, "&apos;");
03583          dst += 6;
03584          space -= 6;
03585          break;
03586       case '&':
03587          strcpy(dst, "&amp;");
03588          dst += 5;
03589          space -= 5;
03590          break;
03591 
03592       default:
03593          *dst++ = mode ? tolower(*src) : *src;
03594          space--;
03595       }
03596    }
03597 }
03598 
03599 struct variable_count {
03600    char *varname;
03601    int count;
03602 };
03603 
03604 static int compress_char(char c)
03605 {
03606    c &= 0x7f;
03607    if (c < 32)
03608       return 0;
03609    else if (c >= 'a' && c <= 'z')
03610       return c - 64;
03611    else if (c > 'z')
03612       return '_';
03613    else
03614       return c - 32;
03615 }
03616 
03617 static int variable_count_hash_fn(const void *vvc, const int flags)
03618 {
03619    const struct variable_count *vc = vvc;
03620    int res = 0, i;
03621    for (i = 0; i < 5; i++) {
03622       if (vc->varname[i] == '\0')
03623          break;
03624       res += compress_char(vc->varname[i]) << (i * 6);
03625    }
03626    return res;
03627 }
03628 
03629 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
03630 {
03631    /* Due to the simplicity of struct variable_count, it makes no difference
03632     * if you pass in objects or strings, the same operation applies. This is
03633     * due to the fact that the hash occurs on the first element, which means
03634     * the address of both the struct and the string are exactly the same. */
03635    struct variable_count *vc = obj;
03636    char *str = vstr;
03637    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
03638 }
03639 
03640 /*! \brief Convert the input into XML or HTML.
03641  * The input is supposed to be a sequence of lines of the form
03642  * Name: value
03643  * optionally followed by a blob of unformatted text.
03644  * A blank line is a section separator. Basically, this is a
03645  * mixture of the format of Manager Interface and CLI commands.
03646  * The unformatted text is considered as a single value of a field
03647  * named 'Opaque-data'.
03648  *
03649  * At the moment the output format is the following (but it may
03650  * change depending on future requirements so don't count too
03651  * much on it when writing applications):
03652  *
03653  * General: the unformatted text is used as a value of
03654  * XML output:  to be completed
03655  * 
03656  * \verbatim
03657  *   Each section is within <response type="object" id="xxx">
03658  *   where xxx is taken from ajaxdest variable or defaults to unknown
03659  *   Each row is reported as an attribute Name="value" of an XML
03660  *   entity named from the variable ajaxobjtype, default to "generic"
03661  * \endverbatim
03662  *
03663  * HTML output:
03664  *   each Name-value pair is output as a single row of a two-column table.
03665  *   Sections (blank lines in the input) are separated by a <HR>
03666  *
03667  */
03668 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
03669 {
03670    struct ast_variable *v;
03671    const char *dest = NULL;
03672    char *var, *val;
03673    const char *objtype = NULL;
03674    int in_data = 0;  /* parsing data */
03675    int inobj = 0;
03676    int xml = (format == FORMAT_XML);
03677    struct variable_count *vc = NULL;
03678    struct ao2_container *vco = NULL;
03679 
03680    for (v = vars; v; v = v->next) {
03681       if (!dest && !strcasecmp(v->name, "ajaxdest"))
03682          dest = v->value;
03683       else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
03684          objtype = v->value;
03685    }
03686    if (!dest)
03687       dest = "unknown";
03688    if (!objtype)
03689       objtype = "generic";
03690 
03691    /* we want to stop when we find an empty line */
03692    while (in && *in) {
03693       val = strsep(&in, "\r\n"); /* mark start and end of line */
03694       if (in && *in == '\n')     /* remove trailing \n if any */
03695          in++;
03696       ast_trim_blanks(val);
03697       ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
03698       if (ast_strlen_zero(val)) {
03699          if (in_data) { /* close data */
03700             ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03701             in_data = 0;
03702          }
03703          if (inobj) {
03704             ast_str_append(out, 0, xml ? " /></response>\n" :
03705                "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03706             inobj = 0;
03707             ao2_ref(vco, -1);
03708             vco = NULL;
03709          }
03710          continue;
03711       }
03712 
03713       /* we expect Name: value lines */
03714       if (in_data) {
03715          var = NULL;
03716       } else {
03717          var = strsep(&val, ":");
03718          if (val) {  /* found the field name */
03719             val = ast_skip_blanks(val);
03720             ast_trim_blanks(var);
03721          } else {    /* field name not found, move to opaque mode */
03722             val = var;
03723             var = "Opaque-data";
03724          }
03725       }
03726 
03727       if (!inobj) {
03728          if (xml)
03729             ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
03730          else
03731             ast_str_append(out, 0, "<body>\n");
03732          vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
03733          inobj = 1;
03734       }
03735 
03736       if (!in_data) {   /* build appropriate line start */
03737          ast_str_append(out, 0, xml ? " " : "<tr><td>");
03738          if ((vc = ao2_find(vco, var, 0)))
03739             vc->count++;
03740          else {
03741             /* Create a new entry for this one */
03742             vc = ao2_alloc(sizeof(*vc), NULL);
03743             vc->varname = var;
03744             vc->count = 1;
03745             ao2_link(vco, vc);
03746          }
03747          xml_copy_escape(out, var, xml ? 1 | 2 : 0);
03748          if (vc->count > 1)
03749             ast_str_append(out, 0, "-%d", vc->count);
03750          ao2_ref(vc, -1);
03751          ast_str_append(out, 0, xml ? "='" : "</td><td>");
03752          if (!strcmp(var, "Opaque-data"))
03753             in_data = 1;
03754       }
03755       xml_copy_escape(out, val, 0); /* data field */
03756       if (!in_data)
03757          ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
03758       else
03759          ast_str_append(out, 0, xml ? "\n" : "<br>\n");
03760    }
03761    if (inobj) {
03762       ast_str_append(out, 0, xml ? " /></response>\n" :
03763          "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
03764       ao2_ref(vco, -1);
03765    }
03766 }
03767 
03768 static struct ast_str *generic_http_callback(enum output_format format,
03769                     struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method,
03770                     struct ast_variable *params, int *status,
03771                     char **title, int *contentlength)
03772 {
03773    struct mansession s = {.session = NULL, };
03774    struct mansession_session *session = NULL;
03775    uint32_t ident = 0;
03776    int blastaway = 0;
03777    struct ast_variable *v;
03778    char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
03779    struct ast_str *out = NULL;
03780    struct message m = { 0 };
03781    unsigned int x;
03782    size_t hdrlen;
03783 
03784    for (v = params; v; v = v->next) {
03785       if (!strcasecmp(v->name, "mansession_id")) {
03786          sscanf(v->value, "%30x", &ident);
03787          break;
03788       }
03789    }
03790 
03791    if (!(session = find_session(ident, 1))) {
03792       /* Create new session.
03793        * While it is not in the list we don't need any locking
03794        */
03795       if (!(session = ast_calloc(1, sizeof(*session)))) {
03796          *status = 500;
03797          goto generic_callback_out;
03798       }
03799       session->sin = *remote_address;
03800       session->fd = -1;
03801       session->waiting_thread = AST_PTHREADT_NULL;
03802       session->send_events = 0;
03803       ast_mutex_init(&session->__lock);
03804       ast_mutex_lock(&session->__lock);
03805       session->inuse = 1;
03806       /*!\note There is approximately a 1 in 1.8E19 chance that the following
03807        * calculation will produce 0, which is an invalid ID, but due to the
03808        * properties of the rand() function (and the constantcy of s), that
03809        * won't happen twice in a row.
03810        */
03811       while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
03812       session->last_ev = grab_last();
03813       AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
03814       AST_LIST_LOCK(&sessions);
03815       AST_LIST_INSERT_HEAD(&sessions, session, list);
03816       ast_atomic_fetchadd_int(&num_sessions, 1);
03817       AST_LIST_UNLOCK(&sessions);
03818    }
03819 
03820    s.session = session;
03821 
03822    ast_mutex_unlock(&session->__lock);
03823 
03824    if (!(out = ast_str_create(1024))) {
03825       *status = 500;
03826       goto generic_callback_out;
03827    }
03828 
03829    s.fd = mkstemp(template);  /* create a temporary file for command output */
03830    unlink(template);
03831    s.f = fdopen(s.fd, "w+");
03832 
03833    for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
03834       hdrlen = strlen(v->name) + strlen(v->value) + 3;
03835       m.headers[m.hdrcount] = alloca(hdrlen);
03836       snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
03837       ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
03838       m.hdrcount = x + 1;
03839    }
03840 
03841    if (process_message(&s, &m)) {
03842       if (session->authenticated) {
03843          if (manager_displayconnects(session)) {
03844             ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03845          }
03846          ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
03847       } else {
03848          if (displayconnects) {
03849             ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
03850          }
03851          ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
03852       }
03853       session->needdestroy = 1;
03854    }
03855 
03856    ast_str_append(&out, 0,
03857              "Content-type: text/%s\r\n"
03858              "Cache-Control: no-cache;\r\n"
03859              "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n"
03860              "\r\n",
03861          contenttype[format],
03862          session->managerid, httptimeout);
03863 
03864    if (format == FORMAT_XML) {
03865       ast_str_append(&out, 0, "<ajax-response>\n");
03866    } else if (format == FORMAT_HTML) {
03867       /*
03868        * When handling AMI-over-HTTP in HTML format, we provide a simple form for
03869        * debugging purposes. This HTML code should not be here, we
03870        * should read from some config file...
03871        */
03872 
03873 #define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
03874 #define TEST_STRING \
03875    "<form action=\"manager\">\n\
03876    Action: <select name=\"action\">\n\
03877       <option value=\"\">-----&gt;</option>\n\
03878       <option value=\"login\">login</option>\n\
03879       <option value=\"command\">Command</option>\n\
03880       <option value=\"waitevent\">waitevent</option>\n\
03881       <option value=\"listcommands\">listcommands</option>\n\
03882    </select>\n\
03883    or <input name=\"action\"><br/>\n\
03884    CLI Command <input name=\"command\"><br>\n\
03885    user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
03886    <input type=\"submit\">\n</form>\n"
03887 
03888       ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
03889       ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03890       ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
03891       ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
03892    }
03893 
03894    if (s.f != NULL) {   /* have temporary output */
03895       char *buf;
03896       size_t l;
03897       
03898       /* Ensure buffer is NULL-terminated */
03899       fprintf(s.f, "%c", 0);
03900 
03901       if ((l = ftell(s.f))) {
03902          if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) {
03903             ast_log(LOG_WARNING, "mmap failed.  Manager output was not processed\n");
03904          } else {
03905             if (format == FORMAT_XML || format == FORMAT_HTML)
03906                xml_translate(&out, buf, params, format);
03907             else
03908                ast_str_append(&out, 0, "%s", buf);
03909             munmap(buf, l);
03910          }
03911       } else if (format == FORMAT_XML || format == FORMAT_HTML) {
03912          xml_translate(&out, "", params, format);
03913       }
03914       fclose(s.f);
03915       s.f = NULL;
03916       s.fd = -1;
03917    }
03918 
03919    if (format == FORMAT_XML) {
03920       ast_str_append(&out, 0, "</ajax-response>\n");
03921    } else if (format == FORMAT_HTML)
03922       ast_str_append(&out, 0, "</table></body>\r\n");
03923 
03924    ast_mutex_lock(&session->__lock);
03925    /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
03926    session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
03927 
03928    if (session->needdestroy) {
03929       if (session->inuse == 1) {
03930          ast_debug(1, "Need destroy, doing it now!\n");
03931          blastaway = 1;
03932       } else {
03933          ast_debug(1, "Need destroy, but can't do it yet!\n");
03934          if (session->waiting_thread != AST_PTHREADT_NULL)
03935             pthread_kill(session->waiting_thread, SIGURG);
03936          session->inuse--;
03937       }
03938    } else
03939       session->inuse--;
03940    ast_mutex_unlock(&session->__lock);
03941 
03942    if (blastaway)
03943       destroy_session(session);
03944 generic_callback_out:
03945    if (*status != 200)
03946       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
03947    return out;
03948 }
03949 
03950 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)
03951 {
03952    return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength);
03953 }
03954 
03955 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)
03956 {
03957    return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength);
03958 }
03959 
03960 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)
03961 {
03962    return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength);
03963 }
03964 
03965 struct ast_http_uri rawmanuri = {
03966    .description = "Raw HTTP Manager Event Interface",
03967    .uri = "rawman",
03968    .callback = rawman_http_callback,
03969    .supports_get = 1,
03970    .data = NULL,
03971    .key = __FILE__,
03972 };
03973 
03974 struct ast_http_uri manageruri = {
03975    .description = "HTML Manager Event Interface",
03976    .uri = "manager",
03977    .callback = manager_http_callback,
03978    .supports_get = 1,
03979    .data = NULL,
03980    .key = __FILE__,
03981 };
03982 
03983 struct ast_http_uri managerxmluri = {
03984    .description = "XML Manager Event Interface",
03985    .uri = "mxml",
03986    .callback = mxml_http_callback,
03987    .supports_get = 1,
03988    .data = NULL,
03989    .key = __FILE__,
03990 };
03991 
03992 static int registered = 0;
03993 static int webregged = 0;
03994 
03995 /*! \brief cleanup code called at each iteration of server_root,
03996  * guaranteed to happen every 5 seconds at most
03997  */
03998 static void purge_old_stuff(void *data)
03999 {
04000    purge_sessions(1);
04001    purge_events();
04002 }
04003 
04004 struct ast_tls_config ami_tls_cfg;
04005 static struct ast_tcptls_session_args ami_desc = {
04006    .accept_fd = -1,
04007    .master = AST_PTHREADT_NULL,
04008    .tls_cfg = NULL, 
04009    .poll_timeout = 5000,   /* wake up every 5 seconds */
04010    .periodic_fn = purge_old_stuff,
04011    .name = "AMI server",
04012    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
04013    .worker_fn = session_do,   /* thread handling the session */
04014 };
04015 
04016 static struct ast_tcptls_session_args amis_desc = {
04017    .accept_fd = -1,
04018    .master = AST_PTHREADT_NULL,
04019    .tls_cfg = &ami_tls_cfg, 
04020    .poll_timeout = -1,  /* the other does the periodic cleanup */
04021    .name = "AMI TLS server",
04022    .accept_fn = ast_tcptls_server_root,   /* thread doing the accept() */
04023    .worker_fn = session_do,   /* thread handling the session */
04024 };
04025 
04026 static int __init_manager(int reload)
04027 {
04028    struct ast_config *ucfg = NULL, *cfg = NULL;
04029    const char *val;
04030    char *cat = NULL;
04031    int newhttptimeout = 60;
04032    int have_sslbindaddr = 0;
04033    struct hostent *hp;
04034    struct ast_hostent ahp;
04035    struct ast_manager_user *user = NULL;
04036    struct ast_variable *var;
04037    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
04038 
04039    manager_enabled = 0;
04040 
04041    if (!registered) {
04042       /* Register default actions */
04043       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
04044       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
04045       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
04046       ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
04047       ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
04048       ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
04049       ast_manager_register2("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status", mandescr_status);
04050       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar);
04051       ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar);
04052       ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
04053       ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
04054       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
04055       ast_manager_register2("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig, "Creates an empty file in the configuration directory", mandescr_createconfig);
04056       ast_manager_register2("ListCategories", EVENT_FLAG_CONFIG, action_listcategories, "List categories in configuration file", mandescr_listcategories);
04057       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
04058       ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer);
04059       ast_manager_register2("Originate", EVENT_FLAG_ORIGINATE, action_originate, "Originate Call", mandescr_originate);
04060       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
04061       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
04062       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
04063       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
04064       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
04065       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
04066       ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
04067       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
04068       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
04069       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
04070       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
04071       ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
04072       ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
04073       ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
04074       ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
04075 
04076       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
04077       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
04078       registered = 1;
04079       /* Append placeholder event so master_eventq never runs dry */
04080       append_event("Event: Placeholder\r\n\r\n", 0);
04081    }
04082    if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
04083       return 0;
04084 
04085    displayconnects = 1;
04086    if (!cfg) {
04087       ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf. Asterisk management interface (AMI) disabled.\n");
04088       return 0;
04089    }
04090 
04091    /* default values */
04092    memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
04093    memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
04094    amis_desc.local_address.sin_port = htons(5039);
04095    ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT);
04096 
04097    ami_tls_cfg.enabled = 0;
04098    if (ami_tls_cfg.certfile)
04099       ast_free(ami_tls_cfg.certfile);
04100    ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
04101    if (ami_tls_cfg.cipher)
04102       ast_free(ami_tls_cfg.cipher);
04103    ami_tls_cfg.cipher = ast_strdup("");
04104 
04105    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
04106       val = var->value;
04107       if (!strcasecmp(var->name, "sslenable"))
04108          ami_tls_cfg.enabled = ast_true(val);
04109       else if (!strcasecmp(var->name, "sslbindport"))
04110          amis_desc.local_address.sin_port = htons(atoi(val));
04111       else if (!strcasecmp(var->name, "sslbindaddr")) {
04112          if ((hp = ast_gethostbyname(val, &ahp))) {
04113             memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr));
04114             have_sslbindaddr = 1;
04115          } else {
04116             ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
04117          }
04118       } else if (!strcasecmp(var->name, "sslcert")) {
04119          ast_free(ami_tls_cfg.certfile);
04120          ami_tls_cfg.certfile = ast_strdup(val);
04121       } else if (!strcasecmp(var->name, "sslcipher")) {
04122          ast_free(ami_tls_cfg.cipher);
04123          ami_tls_cfg.cipher = ast_strdup(val);
04124       } else if (!strcasecmp(var->name, "enabled")) {
04125          manager_enabled = ast_true(val);
04126       } else if (!strcasecmp(var->name, "block-sockets")) {
04127          block_sockets = ast_true(val);
04128       } else if (!strcasecmp(var->name, "webenabled")) {
04129          webmanager_enabled = ast_true(val);
04130       } else if (!strcasecmp(var->name, "port")) {
04131          ami_desc.local_address.sin_port = htons(atoi(val));
04132       } else if (!strcasecmp(var->name, "bindaddr")) {
04133          if (!inet_aton(val, &ami_desc.local_address.sin_addr)) {
04134             ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
04135             memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr));
04136          }
04137       } else if (!strcasecmp(var->name, "allowmultiplelogin")) { 
04138          allowmultiplelogin = ast_true(val);
04139       } else if (!strcasecmp(var->name, "displayconnects")) {
04140          displayconnects = ast_true(val);
04141       } else if (!strcasecmp(var->name, "timestampevents")) {
04142          timestampevents = ast_true(val);
04143       } else if (!strcasecmp(var->name, "debug")) {
04144          manager_debug = ast_true(val);
04145       } else if (!strcasecmp(var->name, "httptimeout")) {
04146          newhttptimeout = atoi(val);
04147       } else {
04148          ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
04149             var->name, val);
04150       }  
04151    }
04152 
04153    if (manager_enabled)
04154       ami_desc.local_address.sin_family = AF_INET;
04155    if (!have_sslbindaddr)
04156       amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr;
04157    if (ami_tls_cfg.enabled)
04158       amis_desc.local_address.sin_family = AF_INET;
04159 
04160    
04161    AST_RWLIST_WRLOCK(&users);
04162 
04163    /* First, get users from users.conf */
04164    ucfg = ast_config_load2("users.conf", "manager", config_flags);
04165    if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED)) {
04166       const char *hasmanager;
04167       int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
04168 
04169       while ((cat = ast_category_browse(ucfg, cat))) {
04170          if (!strcasecmp(cat, "general"))
04171             continue;
04172          
04173          hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
04174          if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
04175             const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
04176             const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
04177             const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
04178             const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
04179             const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
04180             
04181             /* Look for an existing entry,
04182              * if none found - create one and add it to the list
04183              */
04184             if (!(user = get_manager_by_name_locked(cat))) {
04185                if (!(user = ast_calloc(1, sizeof(*user))))
04186                   break;
04187 
04188                /* Copy name over */
04189                ast_copy_string(user->username, cat, sizeof(user->username));
04190                /* Insert into list */
04191                AST_LIST_INSERT_TAIL(&users, user, list);
04192                user->ha = NULL;
04193                user->keep = 1;
04194                user->readperm = -1;
04195                user->writeperm = -1;
04196                /* Default displayconnect from [general] */
04197                user->displayconnects = displayconnects;
04198                user->writetimeout = 100;
04199             }
04200 
04201             if (!user_secret)
04202                user_secret = ast_variable_retrieve(ucfg, "general", "secret");
04203             if (!user_read)
04204                user_read = ast_variable_retrieve(ucfg, "general", "read");
04205             if (!user_write)
04206                user_write = ast_variable_retrieve(ucfg, "general", "write");
04207             if (!user_displayconnects)
04208                user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
04209             if (!user_writetimeout)
04210                user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
04211 
04212             if (!ast_strlen_zero(user_secret)) {
04213                if (user->secret)
04214                   ast_free(user->secret);
04215                user->secret = ast_strdup(user_secret);
04216             }
04217 
04218             if (user_read)
04219                user->readperm = get_perm(user_read);
04220             if (user_write)
04221                user->writeperm = get_perm(user_write);
04222             if (user_displayconnects)
04223                user->displayconnects = ast_true(user_displayconnects);
04224 
04225             if (user_writetimeout) {
04226                int value = atoi(user_writetimeout);
04227                if (value < 100)
04228                   ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
04229                else
04230                   user->writetimeout = value;
04231             }
04232          }
04233       }
04234       ast_config_destroy(ucfg);
04235    }
04236 
04237    /* cat is NULL here in any case */
04238 
04239    while ((cat = ast_category_browse(cfg, cat))) {
04240       struct ast_ha *oldha;
04241 
04242       if (!strcasecmp(cat, "general"))
04243          continue;
04244 
04245       /* Look for an existing entry, if none found - create one and add it to the list */
04246       if (!(user = get_manager_by_name_locked(cat))) {
04247          if (!(user = ast_calloc(1, sizeof(*user))))
04248             break;
04249          /* Copy name over */
04250          ast_copy_string(user->username, cat, sizeof(user->username));
04251 
04252          user->ha = NULL;
04253          user->readperm = 0;
04254          user->writeperm = 0;
04255          /* Default displayconnect from [general] */
04256          user->displayconnects = displayconnects;
04257          user->writetimeout = 100;
04258 
04259          /* Insert into list */
04260          AST_RWLIST_INSERT_TAIL(&users, user, list);
04261       }
04262 
04263       /* Make sure we keep this user and don't destroy it during cleanup */
04264       user->keep = 1;
04265       oldha = user->ha;
04266       user->ha = NULL;
04267 
04268       var = ast_variable_browse(cfg, cat);
04269       for (; var; var = var->next) {
04270          if (!strcasecmp(var->name, "secret")) {
04271             if (user->secret)
04272                ast_free(user->secret);
04273             user->secret = ast_strdup(var->value);
04274          } else if (!strcasecmp(var->name, "deny") ||
04275                    !strcasecmp(var->name, "permit")) {
04276             user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
04277          }  else if (!strcasecmp(var->name, "read") ) {
04278             user->readperm = get_perm(var->value);
04279          }  else if (!strcasecmp(var->name, "write") ) {
04280             user->writeperm = get_perm(var->value);
04281          }  else if (!strcasecmp(var->name, "displayconnects") ) {
04282             user->displayconnects = ast_true(var->value);
04283          } else if (!strcasecmp(var->name, "writetimeout")) {
04284             int value = atoi(var->value);
04285             if (value < 100)
04286                ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
04287             else
04288                user->writetimeout = value;
04289          } else
04290             ast_debug(1, "%s is an unknown option.\n", var->name);
04291       }
04292       ast_free_ha(oldha);
04293    }
04294    ast_config_destroy(cfg);
04295 
04296    /* Perform cleanup - essentially prune out old users that no longer exist */
04297    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
04298       if (user->keep) { /* valid record. clear flag for the next round */
04299          user->keep = 0;
04300          continue;
04301       }
04302       /* We do not need to keep this user so take them out of the list */
04303       AST_RWLIST_REMOVE_CURRENT(list);
04304       /* Free their memory now */
04305       if (user->secret)
04306          ast_free(user->secret);
04307       ast_free_ha(user->ha);
04308       ast_free(user);
04309    }
04310    AST_RWLIST_TRAVERSE_SAFE_END;
04311 
04312    AST_RWLIST_UNLOCK(&users);
04313 
04314    if (webmanager_enabled && manager_enabled) {
04315       if (!webregged) {
04316          ast_http_uri_link(&rawmanuri);
04317          ast_http_uri_link(&manageruri);
04318          ast_http_uri_link(&managerxmluri);
04319          webregged = 1;
04320       }
04321    } else {
04322       if (webregged) {
04323          ast_http_uri_unlink(&rawmanuri);
04324          ast_http_uri_unlink(&manageruri);
04325          ast_http_uri_unlink(&managerxmluri);
04326          webregged = 0;
04327       }
04328    }
04329 
04330    if (newhttptimeout > 0)
04331       httptimeout = newhttptimeout;
04332 
04333    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
04334 
04335    ast_tcptls_server_start(&ami_desc);
04336    if (ast_ssl_setup(amis_desc.tls_cfg))
04337       ast_tcptls_server_start(&amis_desc);
04338    return 0;
04339 }
04340 
04341 int init_manager(void)
04342 {
04343    return __init_manager(0);
04344 }
04345 
04346 int reload_manager(void)
04347 {
04348    return __init_manager(1);
04349 }
04350 
04351 int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
04352 {
04353    AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
04354 
04355    return 0;
04356 }
04357 
04358 int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
04359 {
04360    return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
04361 }
04362 
04363 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
04364 {
04365    struct ast_datastore *datastore = NULL;
04366    
04367    if (info == NULL)
04368       return NULL;
04369 
04370    AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
04371       if (datastore->info != info) {
04372          continue;
04373       }
04374 
04375       if (uid == NULL) {
04376          /* matched by type only */
04377          break;
04378       }
04379 
04380       if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
04381          /* Matched by type AND uid */
04382          break;
04383       }
04384    }
04385    AST_LIST_TRAVERSE_SAFE_END;
04386 
04387    return datastore;
04388 }

Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1