Thu Dec 17 23:51:16 2009

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

Generated on Thu Dec 17 23:51:17 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7