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