Thu Jul 9 13:40:36 2009

Asterisk developer's documentation


manager.c

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

Generated on Thu Jul 9 13:40:37 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7