Sat Aug 6 00:39:30 2011

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  * Channel Management and more
00026  * 
00027  * \ref amiconf
00028  */
00029 
00030 /*! \addtogroup Group_AMI AMI functions 
00031 */
00032 /*! @{ 
00033  Doxygen group */
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 323559 $")
00038 
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include <sys/time.h>
00044 #include <sys/types.h>
00045 #include <netdb.h>
00046 #include <sys/socket.h>
00047 #include <netinet/in.h>
00048 #include <netinet/tcp.h>
00049 #include <arpa/inet.h>
00050 #include <signal.h>
00051 #include <errno.h>
00052 #include <unistd.h>
00053 #include <sys/mman.h>
00054 
00055 #include "asterisk/version.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/file.h"
00058 #include "asterisk/manager.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/callerid.h"
00061 #include "asterisk/lock.h"
00062 #include "asterisk/logger.h"
00063 #include "asterisk/options.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/pbx.h"
00067 #include "asterisk/md5.h"
00068 #include "asterisk/acl.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/http.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/linkedlists.h"
00073 #include "asterisk/term.h"
00074 #include "asterisk/astobj2.h"
00075 
00076 struct fast_originate_helper {
00077    char tech[AST_MAX_EXTENSION];
00078    /*! data can contain a channel name, extension number, username, password, etc. */
00079    char data[512];
00080    int timeout;
00081    int format;
00082    char app[AST_MAX_APP];
00083    char appdata[AST_MAX_EXTENSION];
00084    char cid_name[AST_MAX_EXTENSION];
00085    char cid_num[AST_MAX_EXTENSION];
00086    char context[AST_MAX_CONTEXT];
00087    char exten[AST_MAX_EXTENSION];
00088    char idtext[AST_MAX_EXTENSION];
00089    char account[AST_MAX_ACCOUNT_CODE];
00090    int priority;
00091    struct ast_variable *vars;
00092 };
00093 
00094 struct eventqent {
00095    int usecount;
00096    int category;
00097    struct eventqent *next;
00098    char eventdata[1];
00099 };
00100 
00101 static const int DEFAULT_ENABLED       = 0;  /*!< Default setting for manager to be enabled */
00102 static const int DEFAULT_WEBENABLED       = 0;  /*!< Default setting for the web interface to be enabled */
00103 static const int DEFAULT_BLOCKSOCKETS     = 0;  /*!< Default setting for block-sockets */
00104 static const int DEFAULT_DISPLAYCONNECTS  = 1;  /*!< Default setting for displaying manager connections */
00105 static const int DEFAULT_TIMESTAMPEVENTS  = 0;  /*!< Default setting for timestampevents */  
00106 static const int DEFAULT_HTTPTIMEOUT      = 60; /*!< Default manager http timeout */
00107 static const int DEFAULT_BROKENEVENTSACTION  = 0;  /*!< Default setting for brokeneventsaction */
00108 static const int DEFAULT_AUTHTIMEOUT      = 30; /*!< Default setting for authtimeout */
00109 static const int DEFAULT_AUTHLIMIT     = 50; /*!< Default setting for authlimit */
00110 
00111 
00112 static int manager_enabled = 0;
00113 static int webmanager_enabled = 0;
00114 
00115 static int portno = DEFAULT_MANAGER_PORT;
00116 static int asock = -1;
00117 static int displayconnects;
00118 static int timestampevents;
00119 static int httptimeout;
00120 static int broken_events_action;
00121 static int authtimeout;
00122 static int authlimit;
00123 
00124 static pthread_t t;
00125 static int block_sockets;
00126 static int num_sessions;
00127 static int unauth_sessions = 0;
00128 
00129 /* Protected by the sessions list lock */
00130 struct eventqent *master_eventq = NULL;
00131 
00132 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
00133 #define MANAGER_EVENT_BUF_INITSIZE   256
00134 
00135 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
00136 #define ASTMAN_APPEND_BUF_INITSIZE   256
00137 
00138 static struct permalias {
00139    int num;
00140    char *label;
00141 } perms[] = {
00142    { EVENT_FLAG_SYSTEM, "system" },
00143    { EVENT_FLAG_CALL, "call" },
00144    { EVENT_FLAG_LOG, "log" },
00145    { EVENT_FLAG_VERBOSE, "verbose" },
00146    { EVENT_FLAG_COMMAND, "command" },
00147    { EVENT_FLAG_AGENT, "agent" },
00148    { EVENT_FLAG_USER, "user" },
00149    { EVENT_FLAG_CONFIG, "config" },
00150    { INT_MAX, "all" },
00151    { 0, "none" },
00152 };
00153 
00154 #define MAX_BLACKLIST_CMD_LEN 2
00155 static struct {
00156    char *words[AST_MAX_CMD_LEN];
00157 } command_blacklist[] = {
00158    {{ "module", "load", NULL }},
00159    {{ "module", "unload", NULL }},
00160    {{ "restart", "gracefully", NULL }},
00161 };
00162 
00163 /* In order to understand what the heck is going on with the
00164  * mansession_session and mansession structs, we need to have a bit of a history
00165  * lesson.
00166  *
00167  * In the beginning, there was the mansession. The mansession contained data that was
00168  * intrinsic to a manager session, such as the time that it started, the name of the logged-in
00169  * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
00170  * sessions, these were used to represent the TCP socket over which the AMI session was taking
00171  * place. It makes perfect sense for these fields to be a part of the session-specific data since
00172  * the session actually defines this information.
00173  *
00174  * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
00175  * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
00176  * but rather to the action that is being executed. Because a single session may execute many commands
00177  * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
00178  * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
00179  * has had a chance to properly close its handles.
00180  *
00181  * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
00182  * from being run at the same time in a single session. Some manager actions may block for a long time, thus
00183  * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
00184  * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
00185  * part of the action instead.
00186  *
00187  * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
00188  * contain the action-specific information, such as which file to write to. In order to maintain expectations
00189  * of action handlers and not have to change the public API of the manager code, we would need to name this
00190  * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
00191  * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
00192  * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
00193  * data.
00194  */
00195 struct mansession_session {
00196    /*! Thread lock -- don't use in action callbacks, it's already taken care of  */
00197    ast_mutex_t __lock;
00198    /*! socket address */
00199    struct sockaddr_in sin;
00200    /*! TCP socket */
00201    int fd;
00202    /*! Whether an HTTP manager is in use */
00203    int inuse;
00204    /*! Whether an HTTP session should be destroyed */
00205    int needdestroy;
00206    /*! Whether an HTTP session has someone waiting on events */
00207    pthread_t waiting_thread;
00208    /*! Unique manager identifer */
00209    uint32_t managerid;
00210    /*! Session timeout if HTTP */
00211    time_t sessiontimeout;
00212    /*! Output from manager interface */
00213    struct ast_dynamic_str *outputstr;
00214    /*! Logged in username */
00215    char username[80];
00216    /*! Authentication challenge */
00217    char challenge[10];
00218    /*! Authentication status */
00219    int authenticated;
00220    /*! Authorization for reading */
00221    int readperm;
00222    /*! Authorization for writing */
00223    int writeperm;
00224    /*! Buffer */
00225    char inbuf[1024];
00226    int inlen;
00227    int send_events;
00228    int displaysystemname;     /*!< Add system name to manager responses and events */
00229    /* Queued events that we've not had the ability to send yet */
00230    struct eventqent *eventq;
00231    /* Timeout for ast_carefulwrite() */
00232    int writetimeout;
00233    time_t authstart;
00234    int pending_event;         /*!< Pending events indicator in case when waiting_thread is NULL */
00235    AST_LIST_ENTRY(mansession_session) list;
00236 };
00237 
00238 /* In case you didn't read that giant block of text above the mansession_session struct, the
00239  * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
00240  * represents data that is different from Manager action to Manager action. The mansession_session pointer
00241  * contained within points to session-specific data.
00242  */
00243 struct mansession {
00244    FILE *f;
00245    struct mansession_session *session;
00246    int fd;
00247 };
00248 
00249 static AST_LIST_HEAD_STATIC(sessions, mansession_session);
00250 
00251 struct ast_manager_user {
00252    char username[80];
00253    char *secret;
00254    char *deny;
00255    char *permit;
00256    char *read;
00257    char *write;
00258    unsigned int displayconnects:1;
00259    int keep;
00260    AST_LIST_ENTRY(ast_manager_user) list;
00261 };
00262 
00263 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
00264 
00265 static struct manager_action *first_action;
00266 AST_RWLOCK_DEFINE_STATIC(actionlock);
00267 
00268 int check_manager_enabled()
00269 {
00270    return manager_enabled;
00271 }
00272 
00273 int check_webmanager_enabled()
00274 {
00275    return (webmanager_enabled && manager_enabled);
00276 }
00277 
00278 /*! \brief Convert authority code to string with serveral options */
00279 static char *authority_to_str(int authority, char *res, int reslen)
00280 {
00281    int running_total = 0, i;
00282 
00283    memset(res, 0, reslen);
00284    for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
00285       if (authority & perms[i].num) {
00286          if (*res) {
00287             strncat(res, ",", (reslen > running_total) ? reslen - running_total - 1 : 0);
00288             running_total++;
00289          }
00290          strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total - 1 : 0);
00291          running_total += strlen(perms[i].label);
00292       }
00293    }
00294 
00295    if (ast_strlen_zero(res))
00296       ast_copy_string(res, "<none>", reslen);
00297    
00298    return res;
00299 }
00300 
00301 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
00302 {
00303    struct manager_action *cur;
00304    int which = 0;
00305    char *ret = NULL;
00306 
00307    ast_rwlock_rdlock(&actionlock);
00308    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00309       if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
00310          ret = ast_strdup(cur->action);
00311          break;   /* make sure we exit even if ast_strdup() returns NULL */
00312       }
00313    }
00314    ast_rwlock_unlock(&actionlock);
00315 
00316    return ret;
00317 }
00318 
00319 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
00320 {
00321    while (*src && (*maxlen > 6)) {
00322       switch (*src) {
00323       case '<':
00324          strcpy(*dst, "&lt;");
00325          (*dst) += 4;
00326          *maxlen -= 4;
00327          break;
00328       case '>':
00329          strcpy(*dst, "&gt;");
00330          (*dst) += 4;
00331          *maxlen -= 4;
00332          break;
00333       case '\"':
00334          strcpy(*dst, "&quot;");
00335          (*dst) += 6;
00336          *maxlen -= 6;
00337          break;
00338       case '\'':
00339          strcpy(*dst, "&apos;");
00340          (*dst) += 6;
00341          *maxlen -= 6;
00342          break;
00343       case '&':
00344          strcpy(*dst, "&amp;");
00345          (*dst) += 5;
00346          *maxlen -= 5;
00347          break;      
00348       default:
00349          *(*dst)++ = lower ? tolower(*src) : *src;
00350          (*maxlen)--;
00351       }
00352       src++;
00353    }
00354 }
00355 
00356 struct variable_count {
00357    char *varname;
00358    int count;
00359 };
00360 
00361 static int compress_char(char c)
00362 {
00363    c &= 0x7f;
00364    if (c < 32)
00365       return 0;
00366    else if (c >= 'a' && c <= 'z')
00367       return c - 64;
00368    else if (c > 'z')
00369       return '_';
00370    else
00371       return c - 32;
00372 }
00373 
00374 static int variable_count_hash_fn(const void *vvc, const int flags)
00375 {
00376    const struct variable_count *vc = vvc;
00377    int res = 0, i;
00378    for (i = 0; i < 5; i++) {
00379       if (vc->varname[i] == '\0')
00380          break;
00381       res += compress_char(vc->varname[i]) << (i * 6);
00382    }
00383    return res;
00384 }
00385 
00386 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
00387 {
00388    /* Due to the simplicity of struct variable_count, it makes no difference
00389     * if you pass in objects or strings, the same operation applies. This is
00390     * due to the fact that the hash occurs on the first element, which means
00391     * the address of both the struct and the string are exactly the same. */
00392    struct variable_count *vc = obj;
00393    char *str = vstr;
00394    return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
00395 }
00396 
00397 static char *xml_translate(char *in, struct ast_variable *vars)
00398 {
00399    struct ast_variable *v;
00400    char *dest = NULL;
00401    char *out, *tmp, *var, *val;
00402    char *objtype = NULL;
00403    int colons = 0;
00404    int breaks = 0;
00405    size_t len;
00406    int count = 1;
00407    int escaped = 0;
00408    int inobj = 0;
00409    int x;
00410    struct variable_count *vc = NULL;
00411    struct ao2_container *vco = NULL;
00412 
00413    for (v = vars; v; v = v->next) {
00414       if (!dest && !strcasecmp(v->name, "ajaxdest"))
00415          dest = v->value;
00416       else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) 
00417          objtype = v->value;
00418    }
00419    if (!dest)
00420       dest = "unknown";
00421    if (!objtype)
00422       objtype = "generic";
00423    for (x = 0; in[x]; x++) {
00424       if (in[x] == ':')
00425          colons++;
00426       else if (in[x] == '\n')
00427          breaks++;
00428       else if (strchr("&\"<>\'", in[x]))
00429          escaped++;
00430    }
00431    len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
00432    out = ast_malloc(len);
00433    if (!out)
00434       return 0;
00435    tmp = out;
00436    while (*in) {
00437       var = in;
00438       while (*in && (*in >= 32))
00439          in++;
00440       if (*in) {
00441          if ((count > 3) && inobj) {
00442             ast_build_string(&tmp, &len, " /></response>\n");
00443             inobj = 0;
00444 
00445             /* Entity is closed, so close out the name cache */
00446             ao2_ref(vco, -1);
00447             vco = NULL;
00448          }
00449          count = 0;
00450          while (*in && (*in < 32)) {
00451             *in = '\0';
00452             in++;
00453             count++;
00454          }
00455          val = strchr(var, ':');
00456          if (val) {
00457             *val = '\0';
00458             val++;
00459             if (*val == ' ')
00460                val++;
00461             if (!inobj) {
00462                vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
00463                ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
00464                inobj = 1;
00465             }
00466 
00467             /* Check if the var has been used already */
00468             if ((vc = ao2_find(vco, var, 0)))
00469                vc->count++;
00470             else {
00471                /* Create a new entry for this one */
00472                vc = ao2_alloc(sizeof(*vc), NULL);
00473                vc->varname = var;
00474                vc->count = 1;
00475                ao2_link(vco, vc);
00476             }
00477 
00478             ast_build_string(&tmp, &len, " ");
00479             xml_copy_escape(&tmp, &len, var, 1);
00480             if (vc->count > 1)
00481                ast_build_string(&tmp, &len, "-%d", vc->count);
00482             ast_build_string(&tmp, &len, "='");
00483             xml_copy_escape(&tmp, &len, val, 0);
00484             ast_build_string(&tmp, &len, "'");
00485             ao2_ref(vc, -1);
00486          }
00487       }
00488    }
00489    if (inobj)
00490       ast_build_string(&tmp, &len, " /></response>\n");
00491    if (vco)
00492       ao2_ref(vco, -1);
00493    return out;
00494 }
00495 
00496 static char *html_translate(char *in)
00497 {
00498    int x;
00499    int colons = 0;
00500    int breaks = 0;
00501    size_t len;
00502    int count = 1;
00503    char *tmp, *var, *val, *out;
00504 
00505    for (x=0; in[x]; x++) {
00506       if (in[x] == ':')
00507          colons++;
00508       if (in[x] == '\n')
00509          breaks++;
00510    }
00511    len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
00512    out = ast_malloc(len);
00513    if (!out)
00514       return 0;
00515    tmp = out;
00516    while (*in) {
00517       var = in;
00518       while (*in && (*in >= 32))
00519          in++;
00520       if (*in) {
00521          if ((count % 4) == 0){
00522             ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
00523          }
00524          count = 0;
00525          while (*in && (*in < 32)) {
00526             *in = '\0';
00527             in++;
00528             count++;
00529          }
00530          val = strchr(var, ':');
00531          if (val) {
00532             *val = '\0';
00533             val++;
00534             if (*val == ' ')
00535                val++;
00536             ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
00537          }
00538       }
00539    }
00540    return out;
00541 }
00542 
00543 
00544 
00545 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
00546 {
00547    struct ast_manager_user *user = NULL;
00548 
00549    AST_LIST_TRAVERSE(&users, user, list)
00550       if (!strcasecmp(user->username, name))
00551          break;
00552    return user;
00553 }
00554 
00555 void astman_append(struct mansession *s, const char *fmt, ...)
00556 {
00557    va_list ap;
00558    struct ast_dynamic_str *buf;
00559 
00560    ast_mutex_lock(&s->session->__lock);
00561 
00562    if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
00563       ast_mutex_unlock(&s->session->__lock);
00564       return;
00565    }
00566 
00567    va_start(ap, fmt);
00568    ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
00569    va_end(ap);
00570    
00571    if (s->fd > -1)
00572       ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->session->writetimeout);
00573    else {
00574       if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) {
00575          ast_mutex_unlock(&s->session->__lock);
00576          return;
00577       }
00578 
00579       ast_dynamic_str_append(&s->session->outputstr, 0, "%s", buf->str);   
00580    }
00581 
00582    ast_mutex_unlock(&s->session->__lock);
00583 }
00584 
00585 static int handle_showmancmd(int fd, int argc, char *argv[])
00586 {
00587    struct manager_action *cur;
00588    char authority[80];
00589    int num;
00590 
00591    if (argc != 4)
00592       return RESULT_SHOWUSAGE;
00593 
00594    ast_rwlock_rdlock(&actionlock);
00595    for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
00596       for (num = 3; num < argc; num++) {
00597          if (!strcasecmp(cur->action, argv[num])) {
00598             ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
00599          }
00600       }
00601    }
00602    ast_rwlock_unlock(&actionlock);
00603 
00604    return RESULT_SUCCESS;
00605 }
00606 
00607 static int handle_showmanager(int fd, int argc, char *argv[])
00608 {
00609    struct ast_manager_user *user = NULL;
00610 
00611    if (argc != 4)
00612       return RESULT_SHOWUSAGE;
00613 
00614    AST_LIST_LOCK(&users);
00615 
00616    if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
00617       ast_cli(fd, "There is no manager called %s\n", argv[3]);
00618       AST_LIST_UNLOCK(&users);
00619       return -1;
00620    }
00621 
00622    ast_cli(fd,"\n");
00623    ast_cli(fd,
00624       "       username: %s\n"
00625       "         secret: %s\n"
00626       "           deny: %s\n"
00627       "         permit: %s\n"
00628       "           read: %s\n"
00629       "          write: %s\n"
00630       "displayconnects: %s\n",
00631       (user->username ? user->username : "(N/A)"),
00632       (user->secret ? "<Set>" : "(N/A)"),
00633       (user->deny ? user->deny : "(N/A)"),
00634       (user->permit ? user->permit : "(N/A)"),
00635       (user->read ? user->read : "(N/A)"),
00636       (user->write ? user->write : "(N/A)"),
00637       (user->displayconnects ? "yes" : "no"));
00638 
00639    AST_LIST_UNLOCK(&users);
00640 
00641    return RESULT_SUCCESS;
00642 }
00643 
00644 
00645 static int handle_showmanagers(int fd, int argc, char *argv[])
00646 {
00647    struct ast_manager_user *user = NULL;
00648    int count_amu = 0;
00649 
00650    if (argc != 3)
00651       return RESULT_SHOWUSAGE;
00652 
00653    AST_LIST_LOCK(&users);
00654 
00655    /* If there are no users, print out something along those lines */
00656    if (AST_LIST_EMPTY(&users)) {
00657       ast_cli(fd, "There are no manager users.\n");
00658       AST_LIST_UNLOCK(&users);
00659       return RESULT_SUCCESS;
00660    }
00661 
00662    ast_cli(fd, "\nusername\n--------\n");
00663 
00664    AST_LIST_TRAVERSE(&users, user, list) {
00665       ast_cli(fd, "%s\n", user->username);
00666       count_amu++;
00667    }
00668 
00669    AST_LIST_UNLOCK(&users);
00670 
00671    ast_cli(fd,"-------------------\n");
00672    ast_cli(fd,"%d manager users configured.\n", count_amu);
00673 
00674    return RESULT_SUCCESS;
00675 }
00676 
00677 
00678 /*! \brief  CLI command 
00679    Should change to "manager show commands" */
00680 static int handle_showmancmds(int fd, int argc, char *argv[])
00681 {
00682    struct manager_action *cur;
00683    char authority[80];
00684    char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
00685 
00686    ast_cli(fd, format, "Action", "Privilege", "Synopsis");
00687    ast_cli(fd, format, "------", "---------", "--------");
00688    
00689    ast_rwlock_rdlock(&actionlock);
00690    for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
00691       ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
00692    ast_rwlock_unlock(&actionlock);
00693    
00694    return RESULT_SUCCESS;
00695 }
00696 
00697 /*! \brief CLI command show manager connected */
00698 /* Should change to "manager show connected" */
00699 static int handle_showmanconn(int fd, int argc, char *argv[])
00700 {
00701    struct mansession_session *s;
00702    char wtout[32];
00703    char *format = "  %-15.15s  %-15.15s  %-15.15s\n";
00704 
00705    ast_cli(fd, format, "Username", "IP Address", "Timeout");
00706    
00707    AST_LIST_LOCK(&sessions);
00708    AST_LIST_TRAVERSE(&sessions, s, list) {
00709       memset(wtout, 0, sizeof(wtout));
00710       snprintf(wtout, sizeof(wtout)-1, "%d", s->writetimeout);
00711       ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr), wtout);
00712    }
00713    AST_LIST_UNLOCK(&sessions);
00714 
00715    return RESULT_SUCCESS;
00716 }
00717 
00718 /*! \brief CLI command show manager eventq */
00719 /* Should change to "manager show eventq" */
00720 static int handle_showmaneventq(int fd, int argc, char *argv[])
00721 {
00722    struct eventqent *s;
00723 
00724    AST_LIST_LOCK(&sessions);
00725    for (s = master_eventq; s; s = s->next) {
00726       ast_cli(fd, "Usecount: %d\n",s->usecount);
00727       ast_cli(fd, "Category: %d\n", s->category);
00728       ast_cli(fd, "Event:\n%s", s->eventdata);
00729    }
00730    AST_LIST_UNLOCK(&sessions);
00731 
00732    return RESULT_SUCCESS;
00733 }
00734 
00735 static char showmancmd_help[] = 
00736 "Usage: manager show command <actionname>\n"
00737 "  Shows the detailed description for a specific Asterisk manager interface command.\n";
00738 
00739 static char showmancmds_help[] = 
00740 "Usage: manager show commands\n"
00741 "  Prints a listing of all the available Asterisk manager interface commands.\n";
00742 
00743 static char showmanconn_help[] = 
00744 "Usage: manager show connected\n"
00745 "  Prints a listing of the users that are currently connected to the\n"
00746 "Asterisk manager interface.\n";
00747 
00748 static char showmaneventq_help[] = 
00749 "Usage: manager show eventq\n"
00750 "  Prints a listing of all events pending in the Asterisk manger\n"
00751 "event queue.\n";
00752 
00753 static char showmanagers_help[] =
00754 "Usage: manager show users\n"
00755 "       Prints a listing of all managers that are currently configured on that\n"
00756 " system.\n";
00757 
00758 static char showmanager_help[] =
00759 " Usage: manager show user <user>\n"
00760 "        Display all information related to the manager user specified.\n";
00761 
00762 static struct ast_cli_entry cli_show_manager_command_deprecated = {
00763    { "show", "manager", "command", NULL },
00764    handle_showmancmd, NULL,
00765    NULL, complete_show_mancmd };
00766 
00767 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
00768    { "show", "manager", "commands", NULL },
00769    handle_showmancmds, NULL,
00770    NULL };
00771 
00772 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
00773    { "show", "manager", "connected", NULL },
00774    handle_showmanconn, NULL,
00775    NULL };
00776 
00777 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
00778    { "show", "manager", "eventq", NULL },
00779    handle_showmaneventq, NULL,
00780    NULL };
00781 
00782 static struct ast_cli_entry cli_manager[] = {
00783    { { "manager", "show", "command", NULL },
00784    handle_showmancmd, "Show a manager interface command",
00785    showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
00786 
00787    { { "manager", "show", "commands", NULL },
00788    handle_showmancmds, "List manager interface commands",
00789    showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
00790 
00791    { { "manager", "show", "connected", NULL },
00792    handle_showmanconn, "List connected manager interface users",
00793    showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
00794 
00795    { { "manager", "show", "eventq", NULL },
00796    handle_showmaneventq, "List manager interface queued events",
00797    showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
00798 
00799    { { "manager", "show", "users", NULL },
00800    handle_showmanagers, "List configured manager users",
00801    showmanagers_help, NULL, NULL },
00802 
00803    { { "manager", "show", "user", NULL },
00804    handle_showmanager, "Display information on a specific manager user",
00805    showmanager_help, NULL, NULL },
00806 };
00807 
00808 static void unuse_eventqent(struct eventqent *e)
00809 {
00810    struct eventqent *next = e->next;
00811    if (ast_atomic_dec_and_test(&e->usecount) && next)
00812       pthread_kill(t, SIGURG);
00813 }
00814 
00815 static void free_session(struct mansession_session *s)
00816 {
00817    struct eventqent *eqe;
00818    if (s->fd > -1)
00819       close(s->fd);
00820    if (s->outputstr)
00821       free(s->outputstr);
00822    ast_mutex_destroy(&s->__lock);
00823    while (s->eventq) {
00824       eqe = s->eventq;
00825       s->eventq = s->eventq->next;
00826       unuse_eventqent(eqe);
00827    }
00828    free(s);
00829 }
00830 
00831 static void destroy_session(struct mansession_session *s)
00832 {
00833    AST_LIST_LOCK(&sessions);
00834    AST_LIST_REMOVE(&sessions, s, list);
00835    num_sessions--;
00836    free_session(s);
00837    AST_LIST_UNLOCK(&sessions);
00838 }
00839 
00840 const char *astman_get_header(const struct message *m, char *var)
00841 {
00842    char cmp[80];
00843    int x;
00844 
00845    snprintf(cmp, sizeof(cmp), "%s: ", var);
00846 
00847    for (x = 0; x < m->hdrcount; x++) {
00848       if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
00849          return m->headers[x] + strlen(cmp);
00850    }
00851 
00852    return "";
00853 }
00854 
00855 struct ast_variable *astman_get_variables(const struct message *m)
00856 {
00857    int varlen, x, y;
00858    struct ast_variable *head = NULL, *cur;
00859    char *var, *val;
00860 
00861    char *parse;    
00862    AST_DECLARE_APP_ARGS(args,
00863       AST_APP_ARG(vars)[32];
00864    );
00865 
00866    varlen = strlen("Variable: ");   
00867 
00868    for (x = 0; x < m->hdrcount; x++) {
00869       if (strncasecmp("Variable: ", m->headers[x], varlen))
00870          continue;
00871 
00872       parse = ast_strdupa(m->headers[x] + varlen);
00873 
00874       AST_STANDARD_APP_ARGS(args, parse);
00875       if (args.argc) {
00876          for (y = 0; y < args.argc; y++) {
00877             if (!args.vars[y])
00878                continue;
00879             var = val = ast_strdupa(args.vars[y]);
00880             strsep(&val, "=");
00881             if (!val || ast_strlen_zero(var))
00882                continue;
00883             cur = ast_variable_new(var, val);
00884             if (head) {
00885                cur->next = head;
00886                head = cur;
00887             } else
00888                head = cur;
00889          }
00890       }
00891    }
00892 
00893    return head;
00894 }
00895 
00896 /*! \note NOTE:
00897    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
00898    hold the session lock _or_ be running in an action callback (in which case s->busy will
00899    be non-zero). In either of these cases, there is no need to lock-protect the session's
00900    fd, since no other output will be sent (events will be queued), and no input will
00901    be read until either the current action finishes or get_input() obtains the session
00902    lock.
00903  */
00904 void astman_send_error(struct mansession *s, const struct message *m, char *error)
00905 {
00906    const char *id = astman_get_header(m,"ActionID");
00907 
00908    astman_append(s, "Response: Error\r\n");
00909    if (!ast_strlen_zero(id))
00910       astman_append(s, "ActionID: %s\r\n", id);
00911    astman_append(s, "Message: %s\r\n\r\n", error);
00912 }
00913 
00914 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
00915 {
00916    const char *id = astman_get_header(m,"ActionID");
00917 
00918    astman_append(s, "Response: %s\r\n", resp);
00919    if (!ast_strlen_zero(id))
00920       astman_append(s, "ActionID: %s\r\n", id);
00921    if (msg)
00922       astman_append(s, "Message: %s\r\n\r\n", msg);
00923    else
00924       astman_append(s, "\r\n");
00925 }
00926 
00927 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
00928 {
00929    astman_send_response(s, m, "Success", msg);
00930 }
00931 
00932 /*! Tells you if smallstr exists inside bigstr
00933    which is delim by delim and uses no buf or stringsep
00934    ast_instring("this|that|more","this",',') == 1;
00935 
00936    feel free to move this to app.c -anthm */
00937 static int ast_instring(const char *bigstr, const char *smallstr, char delim) 
00938 {
00939    const char *val = bigstr, *next;
00940 
00941    do {
00942       if ((next = strchr(val, delim))) {
00943          if (!strncmp(val, smallstr, (next - val)))
00944             return 1;
00945          else
00946             continue;
00947       } else
00948          return !strcmp(smallstr, val);
00949 
00950    } while (*(val = (next + 1)));
00951 
00952    return 0;
00953 }
00954 
00955 static int get_perm(const char *instr)
00956 {
00957    int x = 0, ret = 0;
00958 
00959    if (!instr)
00960       return 0;
00961 
00962    for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
00963       if (ast_instring(instr, perms[x].label, ','))
00964          ret |= perms[x].num;
00965    }
00966    
00967    return ret;
00968 }
00969 
00970 static int ast_is_number(const char *string) 
00971 {
00972    int ret = 1, x = 0;
00973 
00974    if (!string)
00975       return 0;
00976 
00977    for (x = 0; x < strlen(string); x++) {
00978       if (!(string[x] >= 48 && string[x] <= 57)) {
00979          ret = 0;
00980          break;
00981       }
00982    }
00983    
00984    return ret ? atoi(string) : 0;
00985 }
00986 
00987 static int strings_to_mask(const char *string) 
00988 {
00989    int x, ret = -1;
00990    
00991    x = ast_is_number(string);
00992 
00993    if (x)
00994       ret = x;
00995    else if (ast_strlen_zero(string))
00996       ret = -1;
00997    else if (ast_false(string))
00998       ret = 0;
00999    else if (ast_true(string)) {
01000       ret = 0;
01001       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
01002          ret |= perms[x].num;    
01003    } else {
01004       ret = 0;
01005       for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
01006          if (ast_instring(string, perms[x].label, ',')) 
01007             ret |= perms[x].num;    
01008       }
01009    }
01010 
01011    return ret;
01012 }
01013 
01014 /*! \brief
01015    Rather than braindead on,off this now can also accept a specific int mask value 
01016    or a ',' delim list of mask strings (the same as manager.conf) -anthm
01017 */
01018 static int set_eventmask(struct mansession_session *s, const char *eventmask)
01019 {
01020    int maskint = strings_to_mask(eventmask);
01021 
01022    ast_mutex_lock(&s->__lock);
01023    if (maskint >= 0) 
01024       s->send_events = maskint;
01025    ast_mutex_unlock(&s->__lock);
01026    
01027    return maskint;
01028 }
01029 
01030 static int authenticate(struct mansession *s, const struct message *m)
01031 {
01032    struct ast_config *cfg;
01033    char *cat;
01034    const char *user = astman_get_header(m, "Username");
01035    const char *pass = astman_get_header(m, "Secret");
01036    const char *authtype = astman_get_header(m, "AuthType");
01037    const char *key = astman_get_header(m, "Key");
01038    const char *events = astman_get_header(m, "Events");
01039    
01040    cfg = ast_config_load("manager.conf");
01041    if (!cfg)
01042       return -1;
01043    cat = ast_category_browse(cfg, NULL);
01044    while (cat) {
01045       if (strcasecmp(cat, "general")) {
01046          /* This is a user */
01047          if (!strcasecmp(cat, user)) {
01048             struct ast_variable *v;
01049             struct ast_ha *ha = NULL;
01050             char *password = NULL;
01051 
01052             for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01053                if (!strcasecmp(v->name, "secret")) {
01054                   password = v->value;
01055                } else if (!strcasecmp(v->name, "displaysystemname")) {
01056                   if (ast_true(v->value)) {
01057                      if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
01058                         s->session->displaysystemname = 1;
01059                      } else {
01060                         ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
01061                      }
01062                   }
01063                } else if (!strcasecmp(v->name, "permit") ||
01064                      !strcasecmp(v->name, "deny")) {
01065                   ha = ast_append_ha(v->name, v->value, ha);
01066                } else if (!strcasecmp(v->name, "writetimeout")) {
01067                   int val = atoi(v->value);
01068 
01069                   if (val < 100)
01070                      ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
01071                   else
01072                      s->session->writetimeout = val;
01073                }
01074                      
01075             }
01076             if (ha && !ast_apply_ha(ha, &(s->session->sin))) {
01077                ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01078                ast_free_ha(ha);
01079                ast_config_destroy(cfg);
01080                return -1;
01081             } else if (ha)
01082                ast_free_ha(ha);
01083             if (!strcasecmp(authtype, "MD5")) {
01084                if (!ast_strlen_zero(key) && 
01085                    !ast_strlen_zero(s->session->challenge) && !ast_strlen_zero(password)) {
01086                   int x;
01087                   int len = 0;
01088                   char md5key[256] = "";
01089                   struct MD5Context md5;
01090                   unsigned char digest[16];
01091                   MD5Init(&md5);
01092                   MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
01093                   MD5Update(&md5, (unsigned char *) password, strlen(password));
01094                   MD5Final(digest, &md5);
01095                   for (x=0; x<16; x++)
01096                      len += sprintf(md5key + len, "%2.2x", digest[x]);
01097                   if (!strcmp(md5key, key))
01098                      break;
01099                   else {
01100                      ast_config_destroy(cfg);
01101                      return -1;
01102                   }
01103                } else {
01104                   ast_log(LOG_DEBUG, "MD5 authentication is not possible.  challenge: '%s'\n", 
01105                      S_OR(s->session->challenge, ""));
01106                   ast_config_destroy(cfg);
01107                   return -1;
01108                }
01109             } else if (password && !strcmp(password, pass)) {
01110                break;
01111             } else {
01112                ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01113                ast_config_destroy(cfg);
01114                return -1;
01115             }  
01116          }
01117       }
01118       cat = ast_category_browse(cfg, cat);
01119    }
01120    if (cat) {
01121       ast_copy_string(s->session->username, cat, sizeof(s->session->username));
01122       s->session->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
01123       s->session->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
01124       ast_config_destroy(cfg);
01125       if (events)
01126          set_eventmask(s->session, events);
01127       return 0;
01128    }
01129    ast_config_destroy(cfg);
01130    cfg = ast_config_load("users.conf");
01131    if (!cfg)
01132       return -1;
01133    cat = ast_category_browse(cfg, NULL);
01134    while (cat) {
01135       struct ast_variable *v;
01136       const char *password = NULL;
01137       int hasmanager = 0;
01138       const char *readperms = NULL;
01139       const char *writeperms = NULL;
01140 
01141       if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
01142          cat = ast_category_browse(cfg, cat);
01143          continue;
01144       }
01145       for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01146          if (!strcasecmp(v->name, "secret"))
01147             password = v->value;
01148          else if (!strcasecmp(v->name, "hasmanager"))
01149             hasmanager = ast_true(v->value);
01150          else if (!strcasecmp(v->name, "managerread"))
01151             readperms = v->value;
01152          else if (!strcasecmp(v->name, "managerwrite"))
01153             writeperms = v->value;
01154       }
01155       if (!hasmanager)
01156          break;
01157       if (!password || strcmp(password, pass)) {
01158          ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01159          ast_config_destroy(cfg);
01160          return -1;
01161       }
01162       ast_copy_string(s->session->username, cat, sizeof(s->session->username));
01163       s->session->readperm = readperms ? get_perm(readperms) : -1;
01164       s->session->writeperm = writeperms ? get_perm(writeperms) : -1;
01165       ast_config_destroy(cfg);
01166       if (events)
01167          set_eventmask(s->session, events);
01168       return 0;
01169    }
01170    ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), user);
01171    ast_config_destroy(cfg);
01172    return -1;
01173 }
01174 
01175 /*! \brief Manager PING */
01176 static char mandescr_ping[] = 
01177 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
01178 "  manager connection open.\n"
01179 "Variables: NONE\n";
01180 
01181 static int action_ping(struct mansession *s, const struct message *m)
01182 {
01183    astman_send_response(s, m, "Pong", NULL);
01184    return 0;
01185 }
01186 
01187 static char mandescr_getconfig[] =
01188 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
01189 "file by category and contents.\n"
01190 "Variables:\n"
01191 "   Filename: Configuration filename (e.g. foo.conf)\n";
01192 
01193 static int action_getconfig(struct mansession *s, const struct message *m)
01194 {
01195    struct ast_config *cfg;
01196    const char *fn = astman_get_header(m, "Filename");
01197    int catcount = 0;
01198    int lineno = 0;
01199    char *category=NULL;
01200    struct ast_variable *v;
01201    char idText[256] = "";
01202    const char *id = astman_get_header(m, "ActionID");
01203 
01204    if (!ast_strlen_zero(id))
01205       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01206 
01207    if (ast_strlen_zero(fn)) {
01208       astman_send_error(s, m, "Filename not specified");
01209       return 0;
01210    }
01211    if (!(cfg = ast_config_load_with_comments(fn))) {
01212       astman_send_error(s, m, "Config file not found");
01213       return 0;
01214    }
01215    astman_append(s, "Response: Success\r\n%s", idText);
01216    while ((category = ast_category_browse(cfg, category))) {
01217       lineno = 0;
01218       astman_append(s, "Category-%06d: %s\r\n", catcount, category);
01219       for (v = ast_variable_browse(cfg, category); v; v = v->next)
01220          astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
01221       catcount++;
01222    }
01223    ast_config_destroy(cfg);
01224    astman_append(s, "\r\n");
01225 
01226    return 0;
01227 }
01228 
01229 
01230 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
01231 {
01232    int x;
01233    char hdr[40];
01234    const char *action, *cat, *var, *value, *match;
01235    struct ast_category *category;
01236    struct ast_variable *v;
01237    
01238    for (x=0;x<100000;x++) {
01239       unsigned int object = 0;
01240 
01241       snprintf(hdr, sizeof(hdr), "Action-%06d", x);
01242       action = astman_get_header(m, hdr);
01243       if (ast_strlen_zero(action))
01244          break;
01245       snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
01246       cat = astman_get_header(m, hdr);
01247       snprintf(hdr, sizeof(hdr), "Var-%06d", x);
01248       var = astman_get_header(m, hdr);
01249       snprintf(hdr, sizeof(hdr), "Value-%06d", x);
01250       value = astman_get_header(m, hdr);
01251       if (!ast_strlen_zero(value) && *value == '>') {
01252          object = 1;
01253          value++;
01254       }
01255       snprintf(hdr, sizeof(hdr), "Match-%06d", x);
01256       match = astman_get_header(m, hdr);
01257       if (!strcasecmp(action, "newcat")) {
01258          if (!ast_strlen_zero(cat)) {
01259             category = ast_category_new(cat);
01260             if (category) {
01261                ast_category_append(cfg, category);
01262             }
01263          }
01264       } else if (!strcasecmp(action, "renamecat")) {
01265          if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
01266             category = ast_category_get(cfg, cat);
01267             if (category) 
01268                ast_category_rename(category, value);
01269          }
01270       } else if (!strcasecmp(action, "delcat")) {
01271          if (!ast_strlen_zero(cat))
01272             ast_category_delete(cfg, (char *) cat);
01273       } else if (!strcasecmp(action, "update")) {
01274          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01275             ast_variable_update(category, var, value, match, object);
01276       } else if (!strcasecmp(action, "delete")) {
01277          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
01278             ast_variable_delete(category, (char *) var, (char *) match);
01279       } else if (!strcasecmp(action, "append")) {
01280          if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && 
01281             (category = ast_category_get(cfg, cat)) && 
01282             (v = ast_variable_new(var, value))){
01283             if (object || (match && !strcasecmp(match, "object")))
01284                v->object = 1;
01285             ast_variable_append(category, v);
01286          }
01287       }
01288    }
01289 }
01290 
01291 static char mandescr_updateconfig[] =
01292 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
01293 "configuration elements in Asterisk configuration files.\n"
01294 "Variables (X's represent 6 digit number beginning with 000000):\n"
01295 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
01296 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
01297 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
01298 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
01299 "   Cat-XXXXXX:    Category to operate on\n"
01300 "   Var-XXXXXX:    Variable to work on\n"
01301 "   Value-XXXXXX:  Value to work on\n"
01302 "   Match-XXXXXX:  Extra match required to match line\n";
01303 
01304 static int action_updateconfig(struct mansession *s, const struct message *m)
01305 {
01306    struct ast_config *cfg;
01307    const char *sfn = astman_get_header(m, "SrcFilename");
01308    const char *dfn = astman_get_header(m, "DstFilename");
01309    int res;
01310    char idText[256] = "";
01311    const char *id = astman_get_header(m, "ActionID");
01312    const char *rld = astman_get_header(m, "Reload");
01313 
01314    if (!ast_strlen_zero(id))
01315       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01316 
01317    if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
01318       astman_send_error(s, m, "Filename not specified");
01319       return 0;
01320    }
01321    if (!(cfg = ast_config_load_with_comments(sfn))) {
01322       astman_send_error(s, m, "Config file not found");
01323       return 0;
01324    }
01325    handle_updates(s, m, cfg);
01326    res = config_text_file_save(dfn, cfg, "Manager");
01327    ast_config_destroy(cfg);
01328    if (res) {
01329       astman_send_error(s, m, "Save of config failed");
01330       return 0;
01331    }
01332    astman_append(s, "Response: Success\r\n%s\r\n", idText);
01333    if (!ast_strlen_zero(rld)) {
01334       if (ast_true(rld))
01335          rld = NULL;
01336       ast_module_reload(rld); 
01337    }
01338    return 0;
01339 }
01340 
01341 /*! \brief Manager WAITEVENT */
01342 static char mandescr_waitevent[] = 
01343 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
01344 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
01345 "session, events will be generated and queued.\n"
01346 "Variables: \n"
01347 "   Timeout: Maximum time to wait for events\n";
01348 
01349 static int action_waitevent(struct mansession *s, const struct message *m)
01350 {
01351    const char *timeouts = astman_get_header(m, "Timeout");
01352    int timeout = -1, max;
01353    int x;
01354    int needexit = 0;
01355    time_t now;
01356    struct eventqent *eqe;
01357    const char *id = astman_get_header(m,"ActionID");
01358    char idText[256] = "";
01359 
01360    if (!ast_strlen_zero(id))
01361       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01362 
01363    if (!ast_strlen_zero(timeouts)) {
01364       sscanf(timeouts, "%30i", &timeout);
01365    }
01366    
01367    ast_mutex_lock(&s->session->__lock);
01368    if (s->session->waiting_thread != AST_PTHREADT_NULL) {
01369       pthread_kill(s->session->waiting_thread, SIGURG);
01370    }
01371    if (s->session->sessiontimeout) {
01372       time(&now);
01373       max = s->session->sessiontimeout - now - 10;
01374       if (max < 0)
01375          max = 0;
01376       if ((timeout < 0) || (timeout > max))
01377          timeout = max;
01378       if (!s->session->send_events)
01379          s->session->send_events = -1;
01380       /* Once waitevent is called, always queue events from now on */
01381    }
01382    ast_mutex_unlock(&s->session->__lock);
01383    s->session->waiting_thread = pthread_self();
01384    if (option_debug)
01385       ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
01386    for (x=0; ((x < timeout) || (timeout < 0)); x++) {
01387       ast_mutex_lock(&s->session->__lock);
01388       if (s->session->eventq && s->session->eventq->next)
01389          needexit = 1;
01390       if (s->session->waiting_thread != pthread_self())
01391          needexit = 1;
01392       if (s->session->needdestroy)
01393          needexit = 1;
01394       ast_mutex_unlock(&s->session->__lock);
01395       if (needexit)
01396          break;
01397       if (s->session->fd > 0) {
01398          if (ast_wait_for_input(s->session->fd, 1000))
01399             break;
01400       } else {
01401          sleep(1);
01402       }
01403    }
01404    if (option_debug)
01405       ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
01406    ast_mutex_lock(&s->session->__lock);
01407    if (s->session->waiting_thread == pthread_self()) {
01408       astman_send_response(s, m, "Success", "Waiting for Event...");
01409       /* Only show events if we're the most recent waiter */
01410       while(s->session->eventq->next) {
01411          eqe = s->session->eventq->next;
01412          if (((s->session->readperm & eqe->category) == eqe->category) &&
01413              ((s->session->send_events & eqe->category) == eqe->category)) {
01414             astman_append(s, "%s", eqe->eventdata);
01415          }
01416          unuse_eventqent(s->session->eventq);
01417          s->session->eventq = eqe;
01418       }
01419       astman_append(s,
01420          "Event: WaitEventComplete\r\n"
01421          "%s"
01422          "\r\n", idText);
01423       s->session->waiting_thread = AST_PTHREADT_NULL;
01424    } else {
01425       ast_log(LOG_DEBUG, "Abandoning event request!\n");
01426    }
01427    ast_mutex_unlock(&s->session->__lock);
01428    return 0;
01429 }
01430 
01431 static char mandescr_listcommands[] = 
01432 "Description: Returns the action name and synopsis for every\n"
01433 "  action that is available to the user\n"
01434 "Variables: NONE\n";
01435 
01436 /*! \note The actionlock is read-locked by the caller of this function */
01437 static int action_listcommands(struct mansession *s, const struct message *m)
01438 {
01439    struct manager_action *cur;
01440    char idText[256] = "";
01441    char temp[BUFSIZ];
01442    const char *id = astman_get_header(m,"ActionID");
01443 
01444    if (!ast_strlen_zero(id))
01445       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01446    astman_append(s, "Response: Success\r\n%s", idText);
01447    for (cur = first_action; cur; cur = cur->next) {
01448       if ((s->session->writeperm & cur->authority) == cur->authority)
01449          astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
01450    }
01451    astman_append(s, "\r\n");
01452 
01453    return 0;
01454 }
01455 
01456 static char mandescr_events[] = 
01457 "Description: Enable/Disable sending of events to this manager\n"
01458 "  client.\n"
01459 "Variables:\n"
01460 "  EventMask: 'on' if all events should be sent,\n"
01461 "     'off' if no events should be sent,\n"
01462 "     'system,call,log' to select which flags events should have to be sent.\n";
01463 
01464 static int action_events(struct mansession *s, const struct message *m)
01465 {
01466    const char *mask = astman_get_header(m, "EventMask");
01467    int res, x;
01468 
01469    res = set_eventmask(s->session, mask);
01470    if (broken_events_action) {
01471       /* if this option is set we should not return a response on
01472        * error, or when all events are set */
01473 
01474       if (res > 0) {
01475          for (x = 0; x < ARRAY_LEN(perms); x++) {
01476             if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
01477                return 0;
01478             }
01479          }
01480          astman_send_response(s, m, "Events On", NULL);
01481       } else if (res == 0)
01482          astman_send_response(s, m, "Events Off", NULL);
01483       return 0;
01484    }
01485 
01486    if (res > 0)
01487       astman_send_response(s, m, "Events On", NULL);
01488    else if (res == 0)
01489       astman_send_response(s, m, "Events Off", NULL);
01490    else
01491       astman_send_error(s, m, "Invalid event mask");
01492 
01493    return 0;
01494 }
01495 
01496 static char mandescr_logoff[] = 
01497 "Description: Logoff this manager session\n"
01498 "Variables: NONE\n";
01499 
01500 static int action_logoff(struct mansession *s, const struct message *m)
01501 {
01502    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
01503    return -1;
01504 }
01505 
01506 static char mandescr_hangup[] = 
01507 "Description: Hangup a channel\n"
01508 "Variables: \n"
01509 "  Channel: The channel name to be hungup\n";
01510 
01511 static int action_hangup(struct mansession *s, const struct message *m)
01512 {
01513    struct ast_channel *c = NULL;
01514    const char *name = astman_get_header(m, "Channel");
01515    if (ast_strlen_zero(name)) {
01516       astman_send_error(s, m, "No channel specified");
01517       return 0;
01518    }
01519    c = ast_get_channel_by_name_locked(name);
01520    if (!c) {
01521       astman_send_error(s, m, "No such channel");
01522       return 0;
01523    }
01524    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01525    ast_channel_unlock(c);
01526    astman_send_ack(s, m, "Channel Hungup");
01527    return 0;
01528 }
01529 
01530 static char mandescr_setvar[] = 
01531 "Description: Set a global or local channel variable.\n"
01532 "Variables: (Names marked with * are required)\n"
01533 "  Channel: Channel to set variable for\n"
01534 "  *Variable: Variable name\n"
01535 "  *Value: Value\n";
01536 
01537 static int action_setvar(struct mansession *s, const struct message *m)
01538 {
01539         struct ast_channel *c = NULL;
01540    const char *name = astman_get_header(m, "Channel");
01541    const char *varname = astman_get_header(m, "Variable");
01542    const char *varval = astman_get_header(m, "Value");
01543    int res = 0;
01544    
01545    if (ast_strlen_zero(varname)) {
01546       astman_send_error(s, m, "No variable specified");
01547       return 0;
01548    }
01549    
01550    if (!ast_strlen_zero(name)) {
01551       c = ast_get_channel_by_name_locked(name);
01552       if (!c) {
01553          astman_send_error(s, m, "No such channel");
01554          return 0;
01555       }
01556    }
01557    if (varname[strlen(varname)-1] == ')') {
01558       char *function = ast_strdupa(varname);
01559       res = ast_func_write(c, function, varval);
01560    } else {
01561       pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
01562    }
01563      
01564    if (c) {
01565       ast_channel_unlock(c);
01566    }
01567 
01568    if (res == 0) {
01569       astman_send_ack(s, m, "Variable Set"); 
01570    } else {
01571       astman_send_error(s, m, "Variable not set");
01572    }
01573 
01574    return 0;
01575 }
01576 
01577 static char mandescr_getvar[] = 
01578 "Description: Get the value of a global or local channel variable.\n"
01579 "Variables: (Names marked with * are required)\n"
01580 "  Channel: Channel to read variable from\n"
01581 "  *Variable: Variable name\n"
01582 "  ActionID: Optional Action id for message matching.\n";
01583 
01584 static int action_getvar(struct mansession *s, const struct message *m)
01585 {
01586    struct ast_channel *c = NULL;
01587    const char *name = astman_get_header(m, "Channel");
01588    const char *varname = astman_get_header(m, "Variable");
01589    const char *id = astman_get_header(m,"ActionID");
01590    char *varval;
01591    char workspace[1024] = "";
01592 
01593    if (ast_strlen_zero(varname)) {
01594       astman_send_error(s, m, "No variable specified");
01595       return 0;
01596    }
01597 
01598    if (!ast_strlen_zero(name)) {
01599       c = ast_get_channel_by_name_locked(name);
01600       if (!c) {
01601          astman_send_error(s, m, "No such channel");
01602          return 0;
01603       }
01604    }
01605 
01606    if (varname[strlen(varname) - 1] == ')') {
01607       char *copy = ast_strdupa(varname);
01608       if (!c) {
01609          c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
01610          if (c) {
01611             ast_func_read(c, copy, workspace, sizeof(workspace));
01612             ast_channel_free(c);
01613             c = NULL;
01614          } else
01615             ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
01616       } else
01617          ast_func_read(c, copy, workspace, sizeof(workspace));
01618       varval = workspace;
01619    } else {
01620       pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
01621    }
01622 
01623    if (c)
01624       ast_channel_unlock(c);
01625    astman_append(s, "Response: Success\r\n"
01626       "Variable: %s\r\nValue: %s\r\n", varname, S_OR(varval, ""));
01627    if (!ast_strlen_zero(id))
01628       astman_append(s, "ActionID: %s\r\n",id);
01629    astman_append(s, "\r\n");
01630 
01631    return 0;
01632 }
01633 
01634 
01635 /*! \brief Manager "status" command to show channels */
01636 /* Needs documentation... */
01637 static int action_status(struct mansession *s, const struct message *m)
01638 {
01639    const char *id = astman_get_header(m,"ActionID");
01640    const char *name = astman_get_header(m,"Channel");
01641    char idText[256] = "";
01642    struct ast_channel *c;
01643    char bridge[256];
01644    struct timeval now = ast_tvnow();
01645    long elapsed_seconds = 0;
01646    int all = ast_strlen_zero(name); /* set if we want all channels */
01647 
01648    if (!ast_strlen_zero(id))
01649       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
01650    if (all)
01651       c = ast_channel_walk_locked(NULL);
01652    else {
01653       c = ast_get_channel_by_name_locked(name);
01654       if (!c) {
01655          astman_send_error(s, m, "No such channel");
01656          return 0;
01657       }
01658    }
01659    astman_send_ack(s, m, "Channel status will follow");
01660    /* if we look by name, we break after the first iteration */
01661    while (c) {
01662       if (c->_bridge)
01663          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
01664       else
01665          bridge[0] = '\0';
01666       if (c->pbx) {
01667          if (c->cdr) {
01668             elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01669          }
01670          astman_append(s,
01671          "Event: Status\r\n"
01672          "Privilege: Call\r\n"
01673          "Channel: %s\r\n"
01674          "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01675          "CallerIDNum: %s\r\n"
01676          "CallerIDName: %s\r\n"
01677          "Account: %s\r\n"
01678          "State: %s\r\n"
01679          "Context: %s\r\n"
01680          "Extension: %s\r\n"
01681          "Priority: %d\r\n"
01682          "Seconds: %ld\r\n"
01683          "%s"
01684          "Uniqueid: %s\r\n"
01685          "%s"
01686          "\r\n",
01687          c->name, 
01688          S_OR(c->cid.cid_num, "<unknown>"), 
01689          S_OR(c->cid.cid_num, "<unknown>"), 
01690          S_OR(c->cid.cid_name, "<unknown>"), 
01691          c->accountcode,
01692          ast_state2str(c->_state), c->context,
01693          c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
01694       } else {
01695          astman_append(s,
01696          "Event: Status\r\n"
01697          "Privilege: Call\r\n"
01698          "Channel: %s\r\n"
01699          "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01700          "CallerIDNum: %s\r\n"
01701          "CallerIDName: %s\r\n"
01702          "Account: %s\r\n"
01703          "State: %s\r\n"
01704          "%s"
01705          "Uniqueid: %s\r\n"
01706          "%s"
01707          "\r\n",
01708          c->name, 
01709          S_OR(c->cid.cid_num, "<unknown>"), 
01710          S_OR(c->cid.cid_num, "<unknown>"), 
01711          S_OR(c->cid.cid_name, "<unknown>"), 
01712          c->accountcode,
01713          ast_state2str(c->_state), bridge, c->uniqueid, idText);
01714       }
01715       ast_channel_unlock(c);
01716       if (!all)
01717          break;
01718       c = ast_channel_walk_locked(c);
01719    }
01720    astman_append(s,
01721    "Event: StatusComplete\r\n"
01722    "%s"
01723    "\r\n",idText);
01724    return 0;
01725 }
01726 
01727 static char mandescr_redirect[] = 
01728 "Description: Redirect (transfer) a call.\n"
01729 "Variables: (Names marked with * are required)\n"
01730 "  *Channel: Channel to redirect\n"
01731 "  ExtraChannel: Second call leg to transfer (optional)\n"
01732 "  *Exten: Extension to transfer to\n"
01733 "  *Context: Context to transfer to\n"
01734 "  *Priority: Priority to transfer to\n"
01735 "  ActionID: Optional Action id for message matching.\n";
01736 
01737 /*! \brief  action_redirect: The redirect manager command */
01738 static int action_redirect(struct mansession *s, const struct message *m)
01739 {
01740    const char *name = astman_get_header(m, "Channel");
01741    const char *name2 = astman_get_header(m, "ExtraChannel");
01742    const char *exten = astman_get_header(m, "Exten");
01743    const char *context = astman_get_header(m, "Context");
01744    const char *priority = astman_get_header(m, "Priority");
01745    struct ast_channel *chan, *chan2 = NULL;
01746    int pi = 0;
01747    int res;
01748 
01749    if (ast_strlen_zero(name)) {
01750       astman_send_error(s, m, "Channel not specified");
01751       return 0;
01752    }
01753    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
01754       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
01755          astman_send_error(s, m, "Invalid priority");
01756          return 0;
01757       }
01758    }
01759    /* XXX watch out, possible deadlock!!! */
01760    chan = ast_get_channel_by_name_locked(name);
01761    if (!chan) {
01762       char buf[BUFSIZ];
01763       snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
01764       astman_send_error(s, m, buf);
01765       return 0;
01766    }
01767    if (ast_check_hangup(chan)) {
01768       astman_send_error(s, m, "Redirect failed, channel not up.");
01769       ast_channel_unlock(chan);
01770       return 0;
01771    }
01772    if (!ast_strlen_zero(name2))
01773       chan2 = ast_get_channel_by_name_locked(name2);
01774    if (chan2 && ast_check_hangup(chan2)) {
01775       astman_send_error(s, m, "Redirect failed, extra channel not up.");
01776       ast_channel_unlock(chan);
01777       ast_channel_unlock(chan2);
01778       return 0;
01779    }
01780    if (chan->pbx) {
01781       ast_channel_lock(chan);
01782       ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01783       ast_channel_unlock(chan);
01784    }
01785    res = ast_async_goto(chan, context, exten, pi);
01786    if (!res) {
01787       if (!ast_strlen_zero(name2)) {
01788          if (chan2) {
01789             if (chan2->pbx) {
01790                ast_channel_lock(chan2);
01791                ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
01792                ast_channel_unlock(chan2);
01793             }
01794             res = ast_async_goto(chan2, context, exten, pi);
01795          } else {
01796             res = -1;
01797          }
01798          if (!res)
01799             astman_send_ack(s, m, "Dual Redirect successful");
01800          else
01801             astman_send_error(s, m, "Secondary redirect failed");
01802       } else
01803          astman_send_ack(s, m, "Redirect successful");
01804    } else
01805       astman_send_error(s, m, "Redirect failed");
01806    if (chan)
01807       ast_channel_unlock(chan);
01808    if (chan2)
01809       ast_channel_unlock(chan2);
01810    return 0;
01811 }
01812 
01813 static int check_blacklist(const char *cmd)
01814 {
01815    char *cmd_copy, *cur_cmd;
01816    char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
01817    int i;
01818 
01819    cmd_copy = ast_strdupa(cmd);
01820    for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
01821       cur_cmd = ast_strip(cur_cmd);
01822       if (ast_strlen_zero(cur_cmd)) {
01823          i--;
01824          continue;
01825       }
01826 
01827       cmd_words[i] = cur_cmd;
01828    }
01829 
01830    for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
01831       int j, match = 1;
01832 
01833       for (j = 0; command_blacklist[i].words[j]; j++) {
01834          if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
01835             match = 0;
01836             break;
01837          }
01838       }
01839 
01840       if (match) {
01841          return 1;
01842       }
01843    }
01844 
01845    return 0;
01846 }
01847 
01848 static char mandescr_command[] = 
01849 "Description: Run a CLI command.\n"
01850 "Variables: (Names marked with * are required)\n"
01851 "  *Command: Asterisk CLI command to run\n"
01852 "  ActionID: Optional Action id for message matching.\n";
01853 
01854 /*! \brief  action_command: Manager command "command" - execute CLI command */
01855 static int action_command(struct mansession *s, const struct message *m)
01856 {
01857    const char *cmd = astman_get_header(m, "Command");
01858    const char *id = astman_get_header(m, "ActionID");
01859    char *buf, *final_buf;
01860    char template[] = "/tmp/ast-ami-XXXXXX";  /* template for temporary file */
01861    int fd;
01862    off_t l;
01863 
01864    if (ast_strlen_zero(cmd)) {
01865       astman_send_error(s, m, "No command provided");
01866       return 0;
01867    }
01868 
01869    if (check_blacklist(cmd)) {
01870       astman_send_error(s, m, "Command blacklisted");
01871       return 0;
01872    }
01873 
01874    fd = mkstemp(template);
01875 
01876    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01877    if (!ast_strlen_zero(id))
01878       astman_append(s, "ActionID: %s\r\n", id);
01879    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
01880    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
01881    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
01882 
01883    /* This has a potential to overflow the stack.  Hence, use the heap. */
01884    buf = ast_calloc(1, l + 1);
01885    final_buf = ast_calloc(1, l + 1);
01886    if (buf) {
01887       lseek(fd, 0, SEEK_SET);
01888       if (read(fd, buf, l) < 0) {
01889          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01890       }
01891       buf[l] = '\0';
01892       if (final_buf) {
01893          term_strip(final_buf, buf, l);
01894          final_buf[l] = '\0';
01895       }
01896       astman_append(s, "%s", S_OR(final_buf, buf));
01897       ast_free(buf);
01898    }
01899    close(fd);
01900    unlink(template);
01901    astman_append(s, "--END COMMAND--\r\n\r\n");
01902    if (final_buf)
01903       ast_free(final_buf);
01904    return 0;
01905 }
01906 
01907 static void *fast_originate(void *data)
01908 {
01909    struct fast_originate_helper *in = data;
01910    int res;
01911    int reason = 0;
01912    struct ast_channel *chan = NULL;
01913    char requested_channel[AST_CHANNEL_NAME];
01914 
01915    if (!ast_strlen_zero(in->app)) {
01916       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
01917          S_OR(in->cid_num, NULL), 
01918          S_OR(in->cid_name, NULL),
01919          in->vars, in->account, &chan);
01920    } else {
01921       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
01922          S_OR(in->cid_num, NULL), 
01923          S_OR(in->cid_name, NULL),
01924          in->vars, in->account, &chan);
01925    }
01926 
01927    if (!chan)
01928       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
01929    /* Tell the manager what happened with the channel */
01930    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01931       "%s%s"
01932       "Response: %s\r\n"
01933       "Channel: %s\r\n"
01934       "Context: %s\r\n"
01935       "Exten: %s\r\n"
01936       "Reason: %d\r\n"
01937       "Uniqueid: %s\r\n"
01938       "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01939       "CallerIDNum: %s\r\n"
01940       "CallerIDName: %s\r\n",
01941       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", 
01942       chan ? chan->name : requested_channel, in->context, in->exten, reason, 
01943       chan ? chan->uniqueid : "<null>",
01944       S_OR(in->cid_num, "<unknown>"),
01945       S_OR(in->cid_num, "<unknown>"),
01946       S_OR(in->cid_name, "<unknown>")
01947       );
01948 
01949    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
01950    if (chan)
01951       ast_channel_unlock(chan);
01952    free(in);
01953    return NULL;
01954 }
01955 
01956 static char mandescr_originate[] = 
01957 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01958 "  Application/Data\n"
01959 "Variables: (Names marked with * are required)\n"
01960 "  *Channel: Channel name to call\n"
01961 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
01962 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
01963 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
01964 "  Application: Application to use\n"
01965 "  Data: Data to use (requires 'Application')\n"
01966 "  Timeout: How long to wait for call to be answered (in ms. Default: 30000)\n"
01967 "  CallerID: Caller ID to be set on the outgoing channel\n"
01968 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01969 "  Codecs: Comma-separated list of codecs to use for the new channels\n"
01970 "  Account: Account code\n"
01971 "  Async: Set to 'true' for fast origination\n";
01972 
01973 static int action_originate(struct mansession *s, const struct message *m)
01974 {
01975    const char *name = astman_get_header(m, "Channel");
01976    const char *exten = astman_get_header(m, "Exten");
01977    const char *context = astman_get_header(m, "Context");
01978    const char *priority = astman_get_header(m, "Priority");
01979    const char *timeout = astman_get_header(m, "Timeout");
01980    const char *callerid = astman_get_header(m, "CallerID");
01981    const char *account = astman_get_header(m, "Account");
01982    const char *app = astman_get_header(m, "Application");
01983    const char *appdata = astman_get_header(m, "Data");
01984    const char *async = astman_get_header(m, "Async");
01985    const char *id = astman_get_header(m, "ActionID");
01986    const char *codecs = astman_get_header(m, "Codecs");
01987    struct ast_variable *vars;
01988    char *tech, *data;
01989    char *l = NULL, *n = NULL;
01990    int pi = 0;
01991    int res;
01992    int to = 30000;
01993    int reason = 0;
01994    char tmp[256];
01995    char tmp2[256];
01996    int format = AST_FORMAT_SLINEAR;
01997    
01998    pthread_t th;
01999    pthread_attr_t attr;
02000    if (ast_strlen_zero(name)) {
02001       astman_send_error(s, m, "Channel not specified");
02002       return 0;
02003    }
02004    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02005       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02006          astman_send_error(s, m, "Invalid priority");
02007          return 0;
02008       }
02009    }
02010    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02011       astman_send_error(s, m, "Invalid timeout");
02012       return 0;
02013    }
02014    ast_copy_string(tmp, name, sizeof(tmp));
02015    tech = tmp;
02016    data = strchr(tmp, '/');
02017    if (!data) {
02018       astman_send_error(s, m, "Invalid channel");
02019       return 0;
02020    }
02021    *data++ = '\0';
02022    ast_copy_string(tmp2, callerid, sizeof(tmp2));
02023    ast_callerid_parse(tmp2, &n, &l);
02024    if (n) {
02025       if (ast_strlen_zero(n))
02026          n = NULL;
02027    }
02028    if (l) {
02029       ast_shrink_phone_number(l);
02030       if (ast_strlen_zero(l))
02031          l = NULL;
02032    }
02033    if (!ast_strlen_zero(codecs)) {
02034       format = 0;
02035       ast_parse_allow_disallow(NULL, &format, codecs, 1);
02036    }
02037    /* Allocate requested channel variables */
02038    vars = astman_get_variables(m);
02039 
02040    if (ast_true(async)) {
02041       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02042       if (!fast) {
02043          res = -1;
02044       } else {
02045          if (!ast_strlen_zero(id))
02046             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02047          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02048             ast_copy_string(fast->data, data, sizeof(fast->data));
02049          ast_copy_string(fast->app, app, sizeof(fast->app));
02050          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02051          if (l)
02052             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02053          if (n)
02054             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02055          fast->vars = vars;   
02056          ast_copy_string(fast->context, context, sizeof(fast->context));
02057          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02058          ast_copy_string(fast->account, account, sizeof(fast->account));
02059          fast->format = format;
02060          fast->timeout = to;
02061          fast->priority = pi;
02062          pthread_attr_init(&attr);
02063          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02064          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
02065             ast_free(fast);
02066             res = -1;
02067          } else {
02068             res = 0;
02069          }
02070          pthread_attr_destroy(&attr);
02071       }
02072    } else if (!ast_strlen_zero(app)) {
02073          res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02074       } else {
02075       if (exten && context && pi)
02076             res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02077       else {
02078          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02079          if (vars) {
02080             ast_variables_destroy(vars);
02081          }
02082          return 0;
02083       }
02084    }   
02085    if (!res)
02086       astman_send_ack(s, m, "Originate successfully queued");
02087    else
02088       astman_send_error(s, m, "Originate failed");
02089    return 0;
02090 }
02091 
02092 /*! \brief Help text for manager command mailboxstatus
02093  */
02094 static char mandescr_mailboxstatus[] = 
02095 "Description: Checks a voicemail account for status.\n"
02096 "Variables: (Names marked with * are required)\n"
02097 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02098 "  ActionID: Optional ActionID for message matching.\n"
02099 "Returns number of messages.\n"
02100 "  Message: Mailbox Status\n"
02101 "  Mailbox: <mailboxid>\n"
02102 "  Waiting: <count>\n"
02103 "\n";
02104 
02105 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02106 {
02107    const char *mailbox = astman_get_header(m, "Mailbox");
02108    const char *id = astman_get_header(m,"ActionID");
02109    char idText[256] = "";
02110    int ret;
02111    if (ast_strlen_zero(mailbox)) {
02112       astman_send_error(s, m, "Mailbox not specified");
02113       return 0;
02114    }
02115         if (!ast_strlen_zero(id))
02116                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02117    ret = ast_app_has_voicemail(mailbox, NULL);
02118    astman_append(s, "Response: Success\r\n"
02119                "%s"
02120                "Message: Mailbox Status\r\n"
02121                "Mailbox: %s\r\n"
02122                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
02123    return 0;
02124 }
02125 
02126 static char mandescr_mailboxcount[] = 
02127 "Description: Checks a voicemail account for new messages.\n"
02128 "Variables: (Names marked with * are required)\n"
02129 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02130 "  ActionID: Optional ActionID for message matching.\n"
02131 "Returns number of new and old messages.\n"
02132 "  Message: Mailbox Message Count\n"
02133 "  Mailbox: <mailboxid>\n"
02134 "  NewMessages: <count>\n"
02135 "  OldMessages: <count>\n"
02136 "\n";
02137 static int action_mailboxcount(struct mansession *s, const struct message *m)
02138 {
02139    const char *mailbox = astman_get_header(m, "Mailbox");
02140    const char *id = astman_get_header(m,"ActionID");
02141    char idText[256] = "";
02142    int newmsgs = 0, oldmsgs = 0;
02143    if (ast_strlen_zero(mailbox)) {
02144       astman_send_error(s, m, "Mailbox not specified");
02145       return 0;
02146    }
02147    ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
02148    if (!ast_strlen_zero(id)) {
02149       snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
02150    }
02151    astman_append(s, "Response: Success\r\n"
02152                "%s"
02153                "Message: Mailbox Message Count\r\n"
02154                "Mailbox: %s\r\n"
02155                "NewMessages: %d\r\n"
02156                "OldMessages: %d\r\n" 
02157                "\r\n",
02158                 idText,mailbox, newmsgs, oldmsgs);
02159    return 0;
02160 }
02161 
02162 static char mandescr_extensionstate[] = 
02163 "Description: Report the extension state for given extension.\n"
02164 "  If the extension has a hint, will use devicestate to check\n"
02165 "  the status of the device connected to the extension.\n"
02166 "Variables: (Names marked with * are required)\n"
02167 "  *Exten: Extension to check state on\n"
02168 "  *Context: Context for extension\n"
02169 "  ActionId: Optional ID for this transaction\n"
02170 "Will return an \"Extension Status\" message.\n"
02171 "The response will include the hint for the extension and the status.\n";
02172 
02173 static int action_extensionstate(struct mansession *s, const struct message *m)
02174 {
02175    const char *exten = astman_get_header(m, "Exten");
02176    const char *context = astman_get_header(m, "Context");
02177    const char *id = astman_get_header(m,"ActionID");
02178    char idText[256] = "";
02179    char hint[256] = "";
02180    int status;
02181    if (ast_strlen_zero(exten)) {
02182       astman_send_error(s, m, "Extension not specified");
02183       return 0;
02184    }
02185    if (ast_strlen_zero(context))
02186       context = "default";
02187    status = ast_extension_state(NULL, context, exten);
02188    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02189         if (!ast_strlen_zero(id)) {
02190                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02191         }
02192    astman_append(s, "Response: Success\r\n"
02193                     "%s"
02194                "Message: Extension Status\r\n"
02195                "Exten: %s\r\n"
02196                "Context: %s\r\n"
02197                "Hint: %s\r\n"
02198                "Status: %d\r\n\r\n",
02199                idText,exten, context, hint, status);
02200    return 0;
02201 }
02202 
02203 static char mandescr_timeout[] = 
02204 "Description: Hangup a channel after a certain time.\n"
02205 "Variables: (Names marked with * are required)\n"
02206 "  *Channel: Channel name to hangup\n"
02207 "  *Timeout: Maximum duration of the call (sec)\n"
02208 "Acknowledges set time with 'Timeout Set' message\n";
02209 
02210 static int action_timeout(struct mansession *s, const struct message *m)
02211 {
02212    struct ast_channel *c = NULL;
02213    const char *name = astman_get_header(m, "Channel");
02214    int timeout = atoi(astman_get_header(m, "Timeout"));
02215    if (ast_strlen_zero(name)) {
02216       astman_send_error(s, m, "No channel specified");
02217       return 0;
02218    }
02219    if (!timeout) {
02220       astman_send_error(s, m, "No timeout specified");
02221       return 0;
02222    }
02223    c = ast_get_channel_by_name_locked(name);
02224    if (!c) {
02225       astman_send_error(s, m, "No such channel");
02226       return 0;
02227    }
02228    ast_channel_setwhentohangup(c, timeout);
02229    ast_channel_unlock(c);
02230    astman_send_ack(s, m, "Timeout Set");
02231    return 0;
02232 }
02233 
02234 static int process_events(struct mansession *s)
02235 {
02236    struct eventqent *eqe;
02237    int ret = 0;
02238    ast_mutex_lock(&s->session->__lock);
02239    if (!s->session->eventq)
02240       s->session->eventq = master_eventq;
02241    while(s->session->eventq->next) {
02242       eqe = s->session->eventq->next;
02243       if ((s->session->authenticated && (s->session->readperm & eqe->category) == eqe->category) &&
02244                ((s->session->send_events & eqe->category) == eqe->category)) {
02245          if (s->fd > -1) {
02246             if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->session->writetimeout) < 0)
02247                ret = -1;
02248          } else if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) 
02249             ret = -1;
02250          else 
02251             ast_dynamic_str_append(&s->session->outputstr, 0, "%s", eqe->eventdata);
02252       }
02253       unuse_eventqent(s->session->eventq);
02254       s->session->eventq = eqe;
02255    }
02256    ast_mutex_unlock(&s->session->__lock);
02257    return ret;
02258 }
02259 
02260 static char mandescr_userevent[] =
02261 "Description: Send an event to manager sessions.\n"
02262 "Variables: (Names marked with * are required)\n"
02263 "       *UserEvent: EventStringToSend\n"
02264 "       Header1: Content1\n"
02265 "       HeaderN: ContentN\n";
02266 
02267 static int action_userevent(struct mansession *s, const struct message *m)
02268 {
02269    const char *event = astman_get_header(m, "UserEvent");
02270    char body[2048] = "";
02271    int x, bodylen = 0, xlen;
02272    for (x = 0; x < m->hdrcount; x++) {
02273       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02274          if (sizeof(body) < bodylen + (xlen = strlen(m->headers[x])) + 3) {
02275             ast_log(LOG_WARNING, "UserEvent exceeds our buffer length.  Truncating.\n");
02276             break;
02277          }
02278          ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02279          bodylen += xlen;
02280          ast_copy_string(body + bodylen, "\r\n", 3);
02281          bodylen += 2;
02282       }
02283    }
02284 
02285    astman_send_ack(s, m, "Event Sent");   
02286    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02287    return 0;
02288 }
02289 
02290 static char mandescr_coresettings[] =
02291 "Description: Query for Core PBX settings.\n"
02292 "Variables: (Names marked with * are optional)\n"
02293 "       *ActionID: ActionID of this transaction\n";
02294 
02295 /*! \brief Show PBX core settings information */
02296 static int action_coresettings(struct mansession *s, const struct message *m)
02297 {
02298    const char *actionid = astman_get_header(m, "ActionID");
02299    char idText[150] = "";
02300 
02301    if (!ast_strlen_zero(actionid))
02302       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02303 
02304    astman_append(s, "Response: Success\r\n"
02305          "%s"
02306          "AMIversion: %s\r\n"
02307          "AsteriskVersion: %s\r\n"
02308          "SystemName: %s\r\n"
02309          "CoreMaxCalls: %d\r\n"
02310          "CoreMaxLoadAvg: %f\r\n"
02311          "CoreRunUser: %s\r\n"
02312          "CoreRunGroup: %s\r\n"
02313          "CoreMaxFilehandles: %d\r\n" 
02314          "CoreRealTimeEnabled: %s\r\n"
02315          "CoreCDRenabled: %s\r\n"
02316          "CoreHTTPenabled: %s\r\n"
02317          "\r\n",
02318          idText,
02319          AMI_VERSION,
02320          ASTERISK_VERSION, 
02321          ast_config_AST_SYSTEM_NAME,
02322          option_maxcalls,
02323          option_maxload,
02324          ast_config_AST_RUN_USER,
02325          ast_config_AST_RUN_GROUP,
02326          option_maxfiles,
02327          ast_realtime_enabled() ? "Yes" : "No",
02328          check_cdr_enabled() ? "Yes" : "No",
02329          check_webmanager_enabled() ? "Yes" : "No"
02330          );
02331    return 0;
02332 }
02333 
02334 static char mandescr_corestatus[] =
02335 "Description: Query for Core PBX status.\n"
02336 "Variables: (Names marked with * are optional)\n"
02337 "       *ActionID: ActionID of this transaction\n";
02338 
02339 /*! \brief Show PBX core status information */
02340 static int action_corestatus(struct mansession *s, const struct message *m)
02341 {
02342    const char *actionid = astman_get_header(m, "ActionID");
02343    char idText[150];
02344    char startuptime[150];
02345    char reloadtime[150];
02346    struct tm tm;
02347 
02348    if (!ast_strlen_zero(actionid))
02349       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02350 
02351    ast_localtime(&ast_startuptime, &tm, NULL);
02352    strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02353    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02354    strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02355 
02356    astman_append(s, "Response: Success\r\n"
02357          "%s"
02358          "CoreStartupTime: %s\r\n"
02359          "CoreReloadTime: %s\r\n"
02360          "CoreCurrentCalls: %d\r\n"
02361          "\r\n",
02362          idText,
02363          startuptime,
02364          reloadtime,
02365          ast_active_channels()
02366          );
02367    return 0;
02368 }
02369 
02370 static int process_message(struct mansession *s, const struct message *m)
02371 {
02372    char action[80] = "";
02373    struct manager_action *tmp;
02374    const char *id = astman_get_header(m,"ActionID");
02375    char idText[256] = "";
02376    int ret = 0;
02377 
02378    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02379    if (option_debug)
02380       ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
02381 
02382    if (ast_strlen_zero(action)) {
02383       astman_send_error(s, m, "Missing action in request");
02384       return 0;
02385    }
02386    if (!ast_strlen_zero(id)) {
02387       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02388    }
02389    if (!s->session->authenticated) {
02390       if (!strcasecmp(action, "Challenge")) {
02391          const char *authtype = astman_get_header(m, "AuthType");
02392 
02393          if (!strcasecmp(authtype, "MD5")) {
02394             if (ast_strlen_zero(s->session->challenge))
02395                snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
02396             astman_append(s, "Response: Success\r\n"
02397                   "%s"
02398                   "Challenge: %s\r\n\r\n",
02399                   idText, s->session->challenge);
02400             return 0;
02401          } else {
02402             astman_send_error(s, m, "Must specify AuthType");
02403             return 0;
02404          }
02405       } else if (!strcasecmp(action, "Login")) {
02406          if (authenticate(s, m)) {
02407             sleep(1);
02408             astman_send_error(s, m, "Authentication failed");
02409             return -1;
02410          } else {
02411             s->session->authenticated = 1;
02412             ast_atomic_fetchadd_int(&unauth_sessions, -1);
02413             if (option_verbose > 1) {
02414                if (displayconnects) {
02415                   ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", 
02416                      (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02417                }
02418             }
02419             ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", 
02420                (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02421             astman_send_ack(s, m, "Authentication accepted");
02422             if (ast_opt_send_fullybooted && ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
02423                char auth[80];
02424                   authority_to_str(EVENT_FLAG_SYSTEM, auth, sizeof(auth));
02425                astman_append(s, "Event: FullyBooted\r\n"
02426                   "Privilege: %s\r\n"
02427                   "Status: Fully Booted\r\n\r\n",
02428                   auth);
02429             }
02430          }
02431       } else if (!strcasecmp(action, "Logoff")) {
02432          astman_send_ack(s, m, "See ya");
02433          return -1;
02434       } else
02435          astman_send_error(s, m, "Authentication Required");
02436    } else {
02437       if (!strcasecmp(action, "Login"))
02438          astman_send_ack(s, m, "Already logged in");
02439       else {
02440          ast_rwlock_rdlock(&actionlock);
02441          for (tmp = first_action; tmp; tmp = tmp->next) {      
02442             if (strcasecmp(action, tmp->action))
02443                continue;
02444             if ((s->session->writeperm & tmp->authority) == tmp->authority) {
02445                if (tmp->func(s, m))
02446                   ret = -1;
02447             } else
02448                astman_send_error(s, m, "Permission denied");
02449             break;
02450          }
02451          ast_rwlock_unlock(&actionlock);
02452          if (!tmp)
02453             astman_send_error(s, m, "Invalid/unknown command");
02454       }
02455    }
02456    if (ret)
02457       return ret;
02458    return process_events(s);
02459 }
02460 
02461 static int get_input(struct mansession_session *s, char *output)
02462 {
02463    /* output must have at least sizeof(s->inbuf) space */
02464    int res;
02465    int x;
02466    struct pollfd fds[1];
02467    int timeout = -1;
02468    time_t now;
02469    for (x = 1; x < s->inlen; x++) {
02470       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
02471          /* Copy output data up to and including \r\n */
02472          memcpy(output, s->inbuf, x + 1);
02473          /* Add trailing \0 */
02474          output[x+1] = '\0';
02475          /* Move remaining data back to the front */
02476          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
02477          s->inlen -= (x + 1);
02478          return 1;
02479       }
02480    } 
02481    if (s->inlen >= sizeof(s->inbuf) - 1) {
02482       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
02483       s->inlen = 0;
02484    }
02485    fds[0].fd = s->fd;
02486    fds[0].events = POLLIN;
02487 
02488    do {
02489       /* calculate a timeout if we are not authenticated */
02490       if (!s->authenticated) {
02491          if(time(&now) == -1) {
02492             ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
02493             return -1;
02494          }
02495 
02496          timeout = (authtimeout - (now - s->authstart)) * 1000;
02497          if (timeout < 0) {
02498             /* we have timed out */
02499             return 0;
02500          }
02501       }
02502 
02503       ast_mutex_lock(&s->__lock);
02504       if (s->pending_event) {
02505          s->pending_event = 0;
02506          ast_mutex_unlock(&s->__lock);
02507          return 0;
02508       }
02509       s->waiting_thread = pthread_self();
02510       ast_mutex_unlock(&s->__lock);
02511 
02512       res = ast_poll(fds, 1, timeout);
02513 
02514       ast_mutex_lock(&s->__lock);
02515       s->waiting_thread = AST_PTHREADT_NULL;
02516       ast_mutex_unlock(&s->__lock);
02517       if (res < 0) {
02518          if (errno == EINTR || errno == EAGAIN) {
02519             return 0;
02520          }
02521          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02522          return -1;
02523       } else if (res > 0) {
02524          ast_mutex_lock(&s->__lock);
02525          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
02526          ast_mutex_unlock(&s->__lock);
02527          if (res < 1)
02528             return -1;
02529          break;
02530       } else {
02531          /* timeout */
02532          return 0;
02533       }
02534    } while(1);
02535    s->inlen += res;
02536    s->inbuf[s->inlen] = '\0';
02537    return 0;
02538 }
02539 
02540 static int do_message(struct mansession *s)
02541 {
02542    struct message m = { 0 };
02543    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
02544    int res;
02545    time_t now;
02546 
02547    for (;;) {
02548       /* Check if any events are pending and do them if needed */
02549       if (s->session->eventq->next) {
02550          if (process_events(s))
02551             return -1;
02552       }
02553       res = get_input(s->session, header_buf);
02554       if (res == 0) {
02555          if (!s->session->authenticated) {
02556             if(time(&now) == -1) {
02557                ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
02558                return -1;
02559             }
02560 
02561             if (now - s->session->authstart > authtimeout) {
02562                ast_log(LOG_EVENT, "Client from %s, failed to authenticate in %d seconds\n", ast_inet_ntoa(s->session->sin.sin_addr), authtimeout);
02563                return -1;
02564             }
02565          }
02566          continue;
02567       } else if (res > 0) {
02568          /* Strip trailing \r\n */
02569          if (strlen(header_buf) < 2)
02570             continue;
02571          header_buf[strlen(header_buf) - 2] = '\0';
02572          if (ast_strlen_zero(header_buf))
02573             return process_message(s, &m) ? -1 : 0;
02574          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02575             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02576       } else {
02577          return res;
02578       }
02579    }
02580 }
02581 
02582 static void *session_do(void *data)
02583 {
02584    struct mansession_session *session = data;
02585    int res;
02586    struct mansession s = { .session = session, .fd = session->fd };
02587 
02588    astman_append(&s, "Asterisk Call Manager/1.0\r\n");
02589    for (;;) {
02590       if ((res = do_message(&s)) < 0)
02591          break;
02592    }
02593    if (session->authenticated) {
02594       if (option_verbose > 1) {
02595          if (displayconnects) 
02596             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02597       }
02598       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02599    } else {
02600       ast_atomic_fetchadd_int(&unauth_sessions, -1);
02601       if (option_verbose > 1) {
02602          if (displayconnects)
02603             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
02604       }
02605       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
02606    }
02607 
02608    /* At one point there was a usleep(1) here intended to allow the call
02609     * to ast_pthread_create_background() to complete before this thread
02610     * exited. This should no longer be necessary as the thread id is no
02611     * longer stored in the mansessions_session.
02612     */
02613 
02614    destroy_session(session);
02615    return NULL;
02616 }
02617 
02618 static void *accept_thread(void *ignore)
02619 {
02620    int as;
02621    struct sockaddr_in sin;
02622    socklen_t sinlen;
02623    struct eventqent *eqe;
02624    struct mansession_session *s;
02625    struct protoent *p;
02626    int arg = 1;
02627    int flags;
02628    pthread_t t;
02629    pthread_attr_t attr;
02630    time_t now;
02631    struct pollfd pfds[1];
02632 
02633    pthread_attr_init(&attr);
02634    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02635 
02636    for (;;) {
02637       time(&now);
02638       AST_LIST_LOCK(&sessions);
02639       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02640          if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02641             AST_LIST_REMOVE_CURRENT(&sessions, list);
02642             num_sessions--;
02643             if (s->authenticated && (option_verbose > 1) && displayconnects) {
02644                ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02645                   s->username, ast_inet_ntoa(s->sin.sin_addr));
02646             }
02647             free_session(s);
02648             break;   
02649          }
02650       }
02651       AST_LIST_TRAVERSE_SAFE_END
02652       /* Purge master event queue of old, unused events, but make sure we
02653          always keep at least one in the queue */
02654       eqe = master_eventq;
02655       while (master_eventq->next && !master_eventq->usecount) {
02656          eqe = master_eventq;
02657          master_eventq = master_eventq->next;
02658          free(eqe);
02659       }
02660       AST_LIST_UNLOCK(&sessions);
02661 
02662       sinlen = sizeof(sin);
02663       pfds[0].fd = asock;
02664       pfds[0].events = POLLIN;
02665       /* Wait for something to happen, but timeout every few seconds so
02666          we can ditch any old manager sessions */
02667       if (ast_poll(pfds, 1, 5000) < 1)
02668          continue;
02669       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
02670       if (as < 0) {
02671          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02672          continue;
02673       }
02674 
02675       if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
02676          close(as);
02677          ast_atomic_fetchadd_int(&unauth_sessions, -1);
02678          ast_log(LOG_WARNING, "manager connection rejected, too many unauthenticated sessions.\n");
02679          continue;
02680       }
02681 
02682       p = getprotobyname("tcp");
02683       if (p) {
02684          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02685             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02686          }
02687       }
02688       if (!(s = ast_calloc(1, sizeof(*s)))) {
02689          close(as);
02690          ast_atomic_fetchadd_int(&unauth_sessions, -1);
02691          continue;
02692       }
02693 
02694       memcpy(&s->sin, &sin, sizeof(sin));
02695       s->writetimeout = 100;
02696       s->waiting_thread = AST_PTHREADT_NULL;
02697 
02698       if (!block_sockets) {
02699          /* For safety, make sure socket is non-blocking */
02700          flags = fcntl(as, F_GETFL);
02701          fcntl(as, F_SETFL, flags | O_NONBLOCK);
02702       } else {
02703          flags = fcntl(as, F_GETFL);
02704          fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
02705       }
02706       ast_mutex_init(&s->__lock);
02707       s->fd = as;
02708       s->send_events = -1;
02709       AST_LIST_LOCK(&sessions);
02710       AST_LIST_INSERT_HEAD(&sessions, s, list);
02711       num_sessions++;
02712       /* Find the last place in the master event queue and hook ourselves
02713          in there */
02714       s->eventq = master_eventq;
02715       while(s->eventq->next)
02716          s->eventq = s->eventq->next;
02717       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02718       AST_LIST_UNLOCK(&sessions);
02719       if(time(&s->authstart) == -1) {
02720          ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
02721          ast_atomic_fetchadd_int(&unauth_sessions, -1);
02722          destroy_session(s);
02723          continue;
02724       }
02725       if (ast_pthread_create_background(&t, &attr, session_do, s)) {
02726          ast_atomic_fetchadd_int(&unauth_sessions, -1);
02727          destroy_session(s);
02728       }
02729    }
02730    pthread_attr_destroy(&attr);
02731    return NULL;
02732 }
02733 
02734 static int append_event(const char *str, int category)
02735 {
02736    struct eventqent *tmp, *prev = NULL;
02737    tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02738 
02739    if (!tmp)
02740       return -1;
02741 
02742    tmp->next = NULL;
02743    tmp->category = category;
02744    strcpy(tmp->eventdata, str);
02745    
02746    if (master_eventq) {
02747       prev = master_eventq;
02748       while (prev->next) 
02749          prev = prev->next;
02750       prev->next = tmp;
02751    } else {
02752       master_eventq = tmp;
02753    }
02754    
02755    tmp->usecount = num_sessions;
02756    
02757    return 0;
02758 }
02759 
02760 /*! \brief  manager_event: Send AMI event to client */
02761 int manager_event(int category, const char *event, const char *fmt, ...)
02762 {
02763    struct mansession_session *s;
02764    char auth[80];
02765    va_list ap;
02766    struct timeval now;
02767    struct ast_dynamic_str *buf;
02768 
02769    /* Abort if there aren't any manager sessions */
02770    if (!num_sessions)
02771       return 0;
02772 
02773    if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02774       return -1;
02775 
02776    ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
02777          "Event: %s\r\nPrivilege: %s\r\n",
02778           event, authority_to_str(category, auth, sizeof(auth)));
02779 
02780    if (timestampevents) {
02781       now = ast_tvnow();
02782       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
02783             "Timestamp: %ld.%06lu\r\n",
02784              (long) now.tv_sec, (unsigned long) now.tv_usec);
02785    }
02786 
02787    va_start(ap, fmt);
02788    ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
02789    va_end(ap);
02790    
02791    ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");  
02792    
02793    /* Append event to master list and wake up any sleeping sessions */
02794    AST_LIST_LOCK(&sessions);
02795    append_event(buf->str, category);
02796    AST_LIST_TRAVERSE(&sessions, s, list) {
02797       ast_mutex_lock(&s->__lock);
02798       if (s->waiting_thread != AST_PTHREADT_NULL)
02799          pthread_kill(s->waiting_thread, SIGURG);
02800       else
02801          /* We have an event to process, but the mansession is
02802           * not waiting for it. We still need to indicate that there
02803           * is an event waiting so that get_input processes the pending
02804           * event instead of polling.
02805           */
02806          s->pending_event = 1;
02807       ast_mutex_unlock(&s->__lock);
02808    }
02809    AST_LIST_UNLOCK(&sessions);
02810 
02811    return 0;
02812 }
02813 
02814 int ast_manager_unregister(char *action) 
02815 {
02816    struct manager_action *cur, *prev;
02817    struct timespec tv = { 5, };
02818 
02819    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02820       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02821       return -1;
02822    }
02823    cur = prev = first_action;
02824    while (cur) {
02825       if (!strcasecmp(action, cur->action)) {
02826          prev->next = cur->next;
02827          free(cur);
02828          if (option_verbose > 1) 
02829             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02830          ast_rwlock_unlock(&actionlock);
02831          return 0;
02832       }
02833       prev = cur;
02834       cur = cur->next;
02835    }
02836    ast_rwlock_unlock(&actionlock);
02837    return 0;
02838 }
02839 
02840 static int manager_state_cb(char *context, char *exten, int state, void *data)
02841 {
02842    /* Notify managers of change */
02843    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02844    return 0;
02845 }
02846 
02847 static int ast_manager_register_struct(struct manager_action *act)
02848 {
02849    struct manager_action *cur, *prev = NULL;
02850    int ret;
02851    struct timespec tv = { 5, };
02852 
02853    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02854       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02855       return -1;
02856    }
02857    cur = first_action;
02858    while (cur) { /* Walk the list of actions */
02859       ret = strcasecmp(cur->action, act->action);
02860       if (ret == 0) {
02861          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02862          ast_rwlock_unlock(&actionlock);
02863          return -1;
02864       } else if (ret > 0) {
02865          /* Insert these alphabetically */
02866          if (prev) {
02867             act->next = prev->next;
02868             prev->next = act;
02869          } else {
02870             act->next = first_action;
02871             first_action = act;
02872          }
02873          break;
02874       }
02875       prev = cur; 
02876       cur = cur->next;
02877    }
02878    
02879    if (!cur) {
02880       if (prev)
02881          prev->next = act;
02882       else
02883          first_action = act;
02884       act->next = NULL;
02885    }
02886 
02887    if (option_verbose > 1) 
02888       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02889    ast_rwlock_unlock(&actionlock);
02890    return 0;
02891 }
02892 
02893 /*! \brief register a new command with manager, including online help. This is 
02894    the preferred way to register a manager command */
02895 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02896 {
02897    struct manager_action *cur;
02898 
02899    cur = ast_malloc(sizeof(*cur));
02900    if (!cur)
02901       return -1;
02902    
02903    cur->action = action;
02904    cur->authority = auth;
02905    cur->func = func;
02906    cur->synopsis = synopsis;
02907    cur->description = description;
02908    cur->next = NULL;
02909 
02910    if (ast_manager_register_struct(cur)) {
02911       ast_free(cur);
02912       return -1;
02913    }
02914 
02915    return 0;
02916 }
02917 /*! @}
02918  END Doxygen group */
02919 
02920 static struct mansession_session *find_session(uint32_t ident)
02921 {
02922    struct mansession_session *s;
02923 
02924    AST_LIST_LOCK(&sessions);
02925    AST_LIST_TRAVERSE(&sessions, s, list) {
02926       ast_mutex_lock(&s->__lock);
02927       if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
02928          s->inuse++;
02929          break;
02930       }
02931       ast_mutex_unlock(&s->__lock);
02932    }
02933    AST_LIST_UNLOCK(&sessions);
02934 
02935    return s;
02936 }
02937 
02938 int astman_verify_session_readpermissions(uint32_t ident, int perm)
02939 {
02940    int result = 0;
02941    struct mansession_session *s;
02942 
02943    AST_LIST_LOCK(&sessions);
02944    AST_LIST_TRAVERSE(&sessions, s, list) {
02945       ast_mutex_lock(&s->__lock);
02946       if ((s->managerid == ident) && (s->readperm & perm)) {
02947          result = 1;
02948          ast_mutex_unlock(&s->__lock);
02949          break;
02950       }
02951       ast_mutex_unlock(&s->__lock);
02952    }
02953    AST_LIST_UNLOCK(&sessions);
02954    return result;
02955 }
02956 
02957 int astman_verify_session_writepermissions(uint32_t ident, int perm)
02958 {
02959    int result = 0;
02960    struct mansession_session *s;
02961 
02962    AST_LIST_LOCK(&sessions);
02963    AST_LIST_TRAVERSE(&sessions, s, list) {
02964       ast_mutex_lock(&s->__lock);
02965       if ((s->managerid == ident) && (s->writeperm & perm)) {
02966          result = 1;
02967          ast_mutex_unlock(&s->__lock);
02968          break;
02969       }
02970       ast_mutex_unlock(&s->__lock);
02971    }
02972    AST_LIST_UNLOCK(&sessions);
02973    return result;
02974 }
02975 
02976 enum {
02977    FORMAT_RAW,
02978    FORMAT_HTML,
02979    FORMAT_XML,
02980 };
02981 static char *contenttype[] = { "plain", "html", "xml" };
02982 
02983 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02984 {
02985    struct mansession_session *s = NULL;
02986    struct mansession ss = { .session = NULL, };
02987    uint32_t ident = 0;
02988    char workspace[512];
02989    char cookie[128];
02990    size_t len = sizeof(workspace);
02991    int blastaway = 0;
02992    char *c = workspace;
02993    char *retval = NULL;
02994    struct ast_variable *v;
02995    char template[] = "/tmp/ast-http-XXXXXX";
02996 
02997    for (v = params; v; v = v->next) {
02998       if (!strcasecmp(v->name, "mansession_id")) {
02999          sscanf(v->value, "%30x", &ident);
03000          break;
03001       }
03002    }
03003    
03004    if (!(s = find_session(ident))) {
03005       /* Create new session */
03006       if (!(s = ast_calloc(1, sizeof(*s)))) {
03007          *status = 500;
03008          goto generic_callback_out;
03009       }
03010       memcpy(&s->sin, requestor, sizeof(s->sin));
03011       s->fd = -1;
03012       s->waiting_thread = AST_PTHREADT_NULL;
03013       s->send_events = 0;
03014       ast_mutex_init(&s->__lock);
03015       ast_mutex_lock(&s->__lock);
03016       s->inuse = 1;
03017       /*!\note There is approximately a 1 in 1.8E19 chance that the following
03018        * calculation will produce 0, which is an invalid ID, but due to the
03019        * properties of the rand() function (and the constantcy of s), that
03020        * won't happen twice in a row.
03021        */
03022       while ((s->managerid = rand() ^ (unsigned long) s) == 0);
03023       AST_LIST_LOCK(&sessions);
03024       AST_LIST_INSERT_HEAD(&sessions, s, list);
03025       /* Hook into the last spot in the event queue */
03026       s->eventq = master_eventq;
03027       while (s->eventq->next)
03028          s->eventq = s->eventq->next;
03029       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
03030       ast_atomic_fetchadd_int(&num_sessions, 1);
03031       AST_LIST_UNLOCK(&sessions);
03032    }
03033 
03034    /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
03035    time(&s->sessiontimeout);
03036    if (!s->authenticated && (httptimeout > 5))
03037       s->sessiontimeout += 5;
03038    else
03039       s->sessiontimeout += httptimeout;
03040    ss.session = s;
03041    ast_mutex_unlock(&s->__lock);
03042 
03043    if ((ss.fd = mkstemp(template)) > -1) {
03044       unlink(template);
03045    }
03046 
03047    if (s) {
03048       struct message m = { 0 };
03049       char tmp[80];
03050       unsigned int x;
03051       size_t hdrlen;
03052 
03053       for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
03054          hdrlen = strlen(v->name) + strlen(v->value) + 3;
03055          m.headers[m.hdrcount] = alloca(hdrlen);
03056          snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
03057          m.hdrcount = x + 1;
03058       }
03059 
03060       if (process_message(&ss, &m)) {
03061          if (s->authenticated) {
03062             if (option_verbose > 1) {
03063                if (displayconnects) 
03064                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
03065             }
03066             ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
03067          } else {
03068             if (option_verbose > 1) {
03069                if (displayconnects)
03070                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
03071             }
03072             ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
03073          }
03074          s->needdestroy = 1;
03075       }
03076       ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
03077       sprintf(tmp, "%08x", s->managerid);
03078       ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
03079       if (format == FORMAT_HTML)
03080          ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
03081       if (format == FORMAT_XML) {
03082          ast_build_string(&c, &len, "<ajax-response>\n");
03083       } else if (format == FORMAT_HTML) {
03084          ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03085          ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
03086       }
03087       ast_mutex_lock(&s->__lock);
03088       if (ss.fd > -1) {
03089          char *buf;
03090          size_t l;
03091          ssize_t res;
03092 
03093          /* Make sure that our buffer is NULL terminated */
03094          while ((res = write(ss.fd, "", 1)) < 1) {
03095             if (res == -1) {
03096                ast_log(LOG_ERROR, "Failed to terminate manager response output: %s\n", strerror(errno));
03097                break;
03098             }
03099          }
03100 
03101          if (res == 1 && (l = lseek(ss.fd, 0, SEEK_END)) > 0) {
03102             if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, ss.fd, 0))) {
03103                ast_log(LOG_WARNING, "mmap failed.  Manager request output was not processed\n");
03104             } else {
03105                char *tmpbuf;
03106                if (format == FORMAT_XML)
03107                   tmpbuf = xml_translate(buf, params);
03108                else if (format == FORMAT_HTML)
03109                   tmpbuf = html_translate(buf);
03110                else
03111                   tmpbuf = buf;
03112                if (tmpbuf) {
03113                   size_t wlen, tlen;
03114                   if ((retval = malloc((wlen = strlen(workspace)) + (tlen = strlen(tmpbuf)) + 128))) {
03115                      strcpy(retval, workspace);
03116                      strcpy(retval + wlen, tmpbuf);
03117                      c = retval + wlen + tlen;
03118                      /* Leftover space for footer, if any */
03119                      len = 120;
03120                   }
03121                }
03122                if (tmpbuf != buf)
03123                   free(tmpbuf);
03124                free(s->outputstr);
03125                s->outputstr = NULL;
03126                munmap(buf, l);
03127             }
03128          }
03129          close(ss.fd);
03130          ss.fd = -1;
03131       } else if (s->outputstr) {
03132          char *tmp;
03133          if (format == FORMAT_XML)
03134             tmp = xml_translate(s->outputstr->str, params);
03135          else if (format == FORMAT_HTML)
03136             tmp = html_translate(s->outputstr->str);
03137          else
03138             tmp = s->outputstr->str;
03139          if (tmp) {
03140             retval = malloc(strlen(workspace) + strlen(tmp) + 128);
03141             if (retval) {
03142                strcpy(retval, workspace);
03143                strcpy(retval + strlen(retval), tmp);
03144                c = retval + strlen(retval);
03145                len = 120;
03146             }
03147          }
03148          if (tmp != s->outputstr->str)
03149             free(tmp);
03150          free(s->outputstr);
03151          s->outputstr = NULL;
03152       }
03153       ast_mutex_unlock(&s->__lock);
03154       /* Still okay because c would safely be pointing to workspace even
03155          if retval failed to allocate above */
03156       if (format == FORMAT_XML) {
03157          ast_build_string(&c, &len, "</ajax-response>\n");
03158       } else if (format == FORMAT_HTML)
03159          ast_build_string(&c, &len, "</table></body>\r\n");
03160    } else {
03161       *status = 500;
03162       *title = strdup("Server Error");
03163    }
03164    ast_mutex_lock(&s->__lock);
03165    if (s->needdestroy) {
03166       if (s->inuse == 1) {
03167          ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
03168          blastaway = 1;
03169       } else {
03170          ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
03171          if (s->waiting_thread != AST_PTHREADT_NULL)
03172             pthread_kill(s->waiting_thread, SIGURG);
03173          s->inuse--;
03174       }
03175    } else
03176       s->inuse--;
03177    ast_mutex_unlock(&s->__lock);
03178    
03179    if (blastaway)
03180       destroy_session(s);
03181 generic_callback_out:
03182    if (*status != 200)
03183       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
03184    return retval;
03185 }
03186 
03187 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03188 {
03189    return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
03190 }
03191 
03192 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03193 {
03194    return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
03195 }
03196 
03197 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03198 {
03199    return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
03200 }
03201 
03202 struct ast_http_uri rawmanuri = {
03203    .description = "Raw HTTP Manager Event Interface",
03204    .uri = "rawman",
03205    .has_subtree = 0,
03206    .callback = rawman_http_callback,
03207 };
03208 
03209 struct ast_http_uri manageruri = {
03210    .description = "HTML Manager Event Interface",
03211    .uri = "manager",
03212    .has_subtree = 0,
03213    .callback = manager_http_callback,
03214 };
03215 
03216 struct ast_http_uri managerxmluri = {
03217    .description = "XML Manager Event Interface",
03218    .uri = "mxml",
03219    .has_subtree = 0,
03220    .callback = mxml_http_callback,
03221 };
03222 
03223 static int registered = 0;
03224 static int webregged = 0;
03225 
03226 int init_manager(void)
03227 {
03228    struct ast_config *cfg = NULL, *ucfg = NULL;
03229    const char *val;
03230    char *cat = NULL;
03231    int oldportno = portno;
03232    static struct sockaddr_in ba;
03233    int x = 1;
03234    int flags;
03235    int webenabled = DEFAULT_WEBENABLED;
03236    int newhttptimeout = DEFAULT_HTTPTIMEOUT;
03237    struct ast_manager_user *user = NULL;
03238 
03239    if (!registered) {
03240       /* Register default actions */
03241       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
03242       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
03243       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
03244       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
03245       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
03246       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
03247       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
03248       ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
03249       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
03250       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
03251       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
03252       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
03253       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
03254       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
03255       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
03256       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
03257       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
03258       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
03259       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
03260       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
03261       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
03262 
03263       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
03264       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
03265       registered = 1;
03266       /* Append placeholder event so master_eventq never runs dry */
03267       append_event("Event: Placeholder\r\n\r\n", 0);
03268    }
03269 
03270    portno = DEFAULT_MANAGER_PORT;
03271    displayconnects = DEFAULT_DISPLAYCONNECTS;
03272    broken_events_action = DEFAULT_BROKENEVENTSACTION;
03273    block_sockets = DEFAULT_BLOCKSOCKETS;
03274    timestampevents = DEFAULT_TIMESTAMPEVENTS;
03275    httptimeout = DEFAULT_HTTPTIMEOUT;
03276    authtimeout = DEFAULT_AUTHTIMEOUT;
03277    authlimit = DEFAULT_AUTHLIMIT;
03278 
03279    cfg = ast_config_load("manager.conf");
03280    if (!cfg) {
03281       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
03282       return 0;
03283    }
03284    if ((val = ast_variable_retrieve(cfg, "general", "enabled"))) {
03285       manager_enabled = ast_true(val);
03286    }
03287    if ((val = ast_variable_retrieve(cfg, "general", "block-sockets"))) {
03288       block_sockets = ast_true(val);
03289    }
03290    if((val = ast_variable_retrieve(cfg, "general", "webenabled"))) {
03291       webmanager_enabled = ast_true(val);
03292    }
03293    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
03294       if (sscanf(val, "%5d", &portno) != 1) {
03295          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
03296          portno = DEFAULT_MANAGER_PORT;
03297       }
03298    }
03299 
03300    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
03301       displayconnects = ast_true(val);
03302 
03303    if ((val = ast_variable_retrieve(cfg, "general", "brokeneventsaction")))
03304       broken_events_action = ast_true(val);
03305 
03306    if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
03307       timestampevents = ast_true(val);
03308 
03309    if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
03310       newhttptimeout = atoi(val);
03311 
03312    if ((val = ast_variable_retrieve(cfg, "general", "authtimeout"))) {
03313       int timeout = atoi(val);
03314 
03315       if (timeout < 1) {
03316          ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", val);
03317       } else {
03318          authtimeout = timeout;
03319       }
03320    }
03321 
03322    if ((val = ast_variable_retrieve(cfg, "general", "authlimit"))) {
03323       int limit = atoi(val);
03324 
03325       if (limit < 1) {
03326          ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", val);
03327       } else {
03328          authlimit = limit;
03329       }
03330    }
03331 
03332    memset(&ba, 0, sizeof(ba));
03333    ba.sin_family = AF_INET;
03334    ba.sin_port = htons(portno);
03335 
03336    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
03337       if (!inet_aton(val, &ba.sin_addr)) { 
03338          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
03339          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
03340       }
03341    }
03342    
03343 
03344    if ((asock > -1) && ((portno != oldportno) || !manager_enabled)) {
03345 #if 0
03346       /* Can't be done yet */
03347       close(asock);
03348       asock = -1;
03349 #else
03350       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
03351 #endif
03352    }
03353 
03354    AST_LIST_LOCK(&users);
03355 
03356    if ((ucfg = ast_config_load("users.conf"))) {
03357       while ((cat = ast_category_browse(ucfg, cat))) {
03358          int hasmanager = 0;
03359          struct ast_variable *var = NULL;
03360 
03361          if (!strcasecmp(cat, "general")) {
03362             continue;
03363          }
03364 
03365          if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
03366             continue;
03367          }
03368 
03369          /* Look for an existing entry, if none found - create one and add it to the list */
03370          if (!(user = ast_get_manager_by_name_locked(cat))) {
03371             if (!(user = ast_calloc(1, sizeof(*user)))) {
03372                break;
03373             }
03374             /* Copy name over */
03375             ast_copy_string(user->username, cat, sizeof(user->username));
03376             /* Insert into list */
03377             AST_LIST_INSERT_TAIL(&users, user, list);
03378          }
03379 
03380          /* Make sure we keep this user and don't destroy it during cleanup */
03381          user->keep = 1;
03382 
03383          for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
03384             if (!strcasecmp(var->name, "secret")) {
03385                if (user->secret) {
03386                   free(user->secret);
03387                }
03388                user->secret = ast_strdup(var->value);
03389             } else if (!strcasecmp(var->name, "deny") ) {
03390                if (user->deny) {
03391                   free(user->deny);
03392                }
03393                user->deny = ast_strdup(var->value);
03394             } else if (!strcasecmp(var->name, "permit") ) {
03395                if (user->permit) {
03396                   free(user->permit);
03397                }
03398                user->permit = ast_strdup(var->value);
03399             } else if (!strcasecmp(var->name, "read") ) {
03400                if (user->read) {
03401                   free(user->read);
03402                }
03403                user->read = ast_strdup(var->value);
03404             } else if (!strcasecmp(var->name, "write") ) {
03405                if (user->write) {
03406                   free(user->write);
03407                }
03408                user->write = ast_strdup(var->value);
03409             } else if (!strcasecmp(var->name, "displayconnects") ) {
03410                user->displayconnects = ast_true(var->value);
03411             } else if (!strcasecmp(var->name, "hasmanager")) {
03412                /* already handled */
03413             } else {
03414                ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
03415             }
03416          }
03417       }
03418       ast_config_destroy(ucfg);
03419    }
03420 
03421    while ((cat = ast_category_browse(cfg, cat))) {
03422       struct ast_variable *var = NULL;
03423 
03424       if (!strcasecmp(cat, "general"))
03425          continue;
03426 
03427       /* Look for an existing entry, if none found - create one and add it to the list */
03428       if (!(user = ast_get_manager_by_name_locked(cat))) {
03429          if (!(user = ast_calloc(1, sizeof(*user))))
03430             break;
03431          /* Copy name over */
03432          ast_copy_string(user->username, cat, sizeof(user->username));
03433          /* Insert into list */
03434          AST_LIST_INSERT_TAIL(&users, user, list);
03435       }
03436 
03437       /* Make sure we keep this user and don't destroy it during cleanup */
03438       user->keep = 1;
03439 
03440       var = ast_variable_browse(cfg, cat);
03441       while (var) {
03442          if (!strcasecmp(var->name, "secret")) {
03443             if (user->secret)
03444                free(user->secret);
03445             user->secret = ast_strdup(var->value);
03446          } else if (!strcasecmp(var->name, "deny") ) {
03447             if (user->deny)
03448                free(user->deny);
03449             user->deny = ast_strdup(var->value);
03450          } else if (!strcasecmp(var->name, "permit") ) {
03451             if (user->permit)
03452                free(user->permit);
03453             user->permit = ast_strdup(var->value);
03454          }  else if (!strcasecmp(var->name, "read") ) {
03455             if (user->read)
03456                free(user->read);
03457             user->read = ast_strdup(var->value);
03458          }  else if (!strcasecmp(var->name, "write") ) {
03459             if (user->write)
03460                free(user->write);
03461             user->write = ast_strdup(var->value);
03462          }  else if (!strcasecmp(var->name, "displayconnects") )
03463             user->displayconnects = ast_true(var->value);
03464          else
03465             ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
03466          var = var->next;
03467       }
03468    }
03469 
03470    /* Perform cleanup - essentially prune out old users that no longer exist */
03471    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
03472       if (user->keep) {
03473          user->keep = 0;
03474          continue;
03475       }
03476       /* We do not need to keep this user so take them out of the list */
03477       AST_LIST_REMOVE_CURRENT(&users, list);
03478       /* Free their memory now */
03479       if (user->secret)
03480          free(user->secret);
03481       if (user->deny)
03482          free(user->deny);
03483       if (user->permit)
03484          free(user->permit);
03485       if (user->read)
03486          free(user->read);
03487       if (user->write)
03488          free(user->write);
03489       free(user);
03490    }
03491    AST_LIST_TRAVERSE_SAFE_END
03492 
03493    AST_LIST_UNLOCK(&users);
03494 
03495    ast_config_destroy(cfg);
03496    
03497    if (webmanager_enabled && manager_enabled) {
03498       if (!webregged) {
03499          ast_http_uri_link(&rawmanuri);
03500          ast_http_uri_link(&manageruri);
03501          ast_http_uri_link(&managerxmluri);
03502          webregged = 1;
03503       }
03504    } else {
03505       if (webregged) {
03506          ast_http_uri_unlink(&rawmanuri);
03507          ast_http_uri_unlink(&manageruri);
03508          ast_http_uri_unlink(&managerxmluri);
03509          webregged = 0;
03510       }
03511    }
03512 
03513    if (newhttptimeout > 0)
03514       httptimeout = newhttptimeout;
03515 
03516    /* If not enabled, do nothing */
03517    if (!manager_enabled)
03518       return 0;
03519 
03520    if (asock < 0) {
03521       asock = socket(AF_INET, SOCK_STREAM, 0);
03522       if (asock < 0) {
03523          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
03524          return -1;
03525       }
03526       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
03527       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
03528          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
03529          close(asock);
03530          asock = -1;
03531          return -1;
03532       }
03533       if (listen(asock, 2)) {
03534          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
03535          close(asock);
03536          asock = -1;
03537          return -1;
03538       }
03539       flags = fcntl(asock, F_GETFL);
03540       fcntl(asock, F_SETFL, flags | O_NONBLOCK);
03541       if (option_verbose)
03542          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
03543       ast_pthread_create_background(&t, NULL, accept_thread, NULL);
03544    }
03545    return 0;
03546 }
03547 
03548 int reload_manager(void)
03549 {
03550    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
03551    return init_manager();
03552 }

Generated on Sat Aug 6 00:39:30 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7