Fri Jul 15 2011 11:58:21

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