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

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