Fri Jun 19 12:09:47 2009

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

Generated on Fri Jun 19 12:09:47 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7