Thu Oct 11 06:47:18 2012

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

Generated on Thu Oct 11 06:47:18 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6