Wed Aug 18 22:33:53 2010

Asterisk developer's documentation


manager.c

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

Generated on Wed Aug 18 22:33:53 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7