Thu Oct 11 06:42:08 2012

Asterisk developer's documentation


manager.c

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

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