Thu Apr 28 2011 16:56:47

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