Wed Oct 14 15:01:56 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: 211596 $")
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 = mkstemp(template);
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    astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
01875    if (!ast_strlen_zero(id))
01876       astman_append(s, "ActionID: %s\r\n", id);
01877    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
01878    ast_cli_command(fd, cmd);  /* XXX need to change this to use a FILE * */
01879    l = lseek(fd, 0, SEEK_END);   /* how many chars available */
01880 
01881    /* This has a potential to overflow the stack.  Hence, use the heap. */
01882    buf = ast_calloc(1, l + 1);
01883    final_buf = ast_calloc(1, l + 1);
01884    if (buf) {
01885       lseek(fd, 0, SEEK_SET);
01886       if (read(fd, buf, l) < 0) {
01887          ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01888       }
01889       buf[l] = '\0';
01890       if (final_buf) {
01891          term_strip(final_buf, buf, l);
01892          final_buf[l] = '\0';
01893       }
01894       astman_append(s, "%s", S_OR(final_buf, buf));
01895       ast_free(buf);
01896    }
01897    close(fd);
01898    unlink(template);
01899    astman_append(s, "--END COMMAND--\r\n\r\n");
01900    if (final_buf)
01901       ast_free(final_buf);
01902    return 0;
01903 }
01904 
01905 static void *fast_originate(void *data)
01906 {
01907    struct fast_originate_helper *in = data;
01908    int res;
01909    int reason = 0;
01910    struct ast_channel *chan = NULL;
01911    char requested_channel[AST_CHANNEL_NAME];
01912 
01913    if (!ast_strlen_zero(in->app)) {
01914       res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
01915          S_OR(in->cid_num, NULL), 
01916          S_OR(in->cid_name, NULL),
01917          in->vars, in->account, &chan);
01918    } else {
01919       res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
01920          S_OR(in->cid_num, NULL), 
01921          S_OR(in->cid_name, NULL),
01922          in->vars, in->account, &chan);
01923    }
01924 
01925    if (!chan)
01926       snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);   
01927    /* Tell the manager what happened with the channel */
01928    manager_event(EVENT_FLAG_CALL, "OriginateResponse",
01929       "%s%s"
01930       "Response: %s\r\n"
01931       "Channel: %s\r\n"
01932       "Context: %s\r\n"
01933       "Exten: %s\r\n"
01934       "Reason: %d\r\n"
01935       "Uniqueid: %s\r\n"
01936       "CallerID: %s\r\n"      /* This parameter is deprecated and will be removed post-1.4 */
01937       "CallerIDNum: %s\r\n"
01938       "CallerIDName: %s\r\n",
01939       in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", 
01940       chan ? chan->name : requested_channel, in->context, in->exten, reason, 
01941       chan ? chan->uniqueid : "<null>",
01942       S_OR(in->cid_num, "<unknown>"),
01943       S_OR(in->cid_num, "<unknown>"),
01944       S_OR(in->cid_name, "<unknown>")
01945       );
01946 
01947    /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
01948    if (chan)
01949       ast_channel_unlock(chan);
01950    free(in);
01951    return NULL;
01952 }
01953 
01954 static char mandescr_originate[] = 
01955 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
01956 "  Application/Data\n"
01957 "Variables: (Names marked with * are required)\n"
01958 "  *Channel: Channel name to call\n"
01959 "  Exten: Extension to use (requires 'Context' and 'Priority')\n"
01960 "  Context: Context to use (requires 'Exten' and 'Priority')\n"
01961 "  Priority: Priority to use (requires 'Exten' and 'Context')\n"
01962 "  Application: Application to use\n"
01963 "  Data: Data to use (requires 'Application')\n"
01964 "  Timeout: How long to wait for call to be answered (in ms)\n"
01965 "  CallerID: Caller ID to be set on the outgoing channel\n"
01966 "  Variable: Channel variable to set, multiple Variable: headers are allowed\n"
01967 "  Account: Account code\n"
01968 "  Async: Set to 'true' for fast origination\n";
01969 
01970 static int action_originate(struct mansession *s, const struct message *m)
01971 {
01972    const char *name = astman_get_header(m, "Channel");
01973    const char *exten = astman_get_header(m, "Exten");
01974    const char *context = astman_get_header(m, "Context");
01975    const char *priority = astman_get_header(m, "Priority");
01976    const char *timeout = astman_get_header(m, "Timeout");
01977    const char *callerid = astman_get_header(m, "CallerID");
01978    const char *account = astman_get_header(m, "Account");
01979    const char *app = astman_get_header(m, "Application");
01980    const char *appdata = astman_get_header(m, "Data");
01981    const char *async = astman_get_header(m, "Async");
01982    const char *id = astman_get_header(m, "ActionID");
01983    const char *codecs = astman_get_header(m, "Codecs");
01984    struct ast_variable *vars = astman_get_variables(m);
01985    char *tech, *data;
01986    char *l = NULL, *n = NULL;
01987    int pi = 0;
01988    int res;
01989    int to = 30000;
01990    int reason = 0;
01991    char tmp[256];
01992    char tmp2[256];
01993    int format = AST_FORMAT_SLINEAR;
01994    
01995    pthread_t th;
01996    pthread_attr_t attr;
01997    if (ast_strlen_zero(name)) {
01998       astman_send_error(s, m, "Channel not specified");
01999       return 0;
02000    }
02001    if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
02002       if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
02003          astman_send_error(s, m, "Invalid priority");
02004          return 0;
02005       }
02006    }
02007    if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
02008       astman_send_error(s, m, "Invalid timeout");
02009       return 0;
02010    }
02011    ast_copy_string(tmp, name, sizeof(tmp));
02012    tech = tmp;
02013    data = strchr(tmp, '/');
02014    if (!data) {
02015       astman_send_error(s, m, "Invalid channel");
02016       return 0;
02017    }
02018    *data++ = '\0';
02019    ast_copy_string(tmp2, callerid, sizeof(tmp2));
02020    ast_callerid_parse(tmp2, &n, &l);
02021    if (n) {
02022       if (ast_strlen_zero(n))
02023          n = NULL;
02024    }
02025    if (l) {
02026       ast_shrink_phone_number(l);
02027       if (ast_strlen_zero(l))
02028          l = NULL;
02029    }
02030    if (!ast_strlen_zero(codecs)) {
02031       format = 0;
02032       ast_parse_allow_disallow(NULL, &format, codecs, 1);
02033    }
02034    if (ast_true(async)) {
02035       struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
02036       if (!fast) {
02037          res = -1;
02038       } else {
02039          if (!ast_strlen_zero(id))
02040             snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id);
02041          ast_copy_string(fast->tech, tech, sizeof(fast->tech));
02042             ast_copy_string(fast->data, data, sizeof(fast->data));
02043          ast_copy_string(fast->app, app, sizeof(fast->app));
02044          ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
02045          if (l)
02046             ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
02047          if (n)
02048             ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
02049          fast->vars = vars;   
02050          ast_copy_string(fast->context, context, sizeof(fast->context));
02051          ast_copy_string(fast->exten, exten, sizeof(fast->exten));
02052          ast_copy_string(fast->account, account, sizeof(fast->account));
02053          fast->format = format;
02054          fast->timeout = to;
02055          fast->priority = pi;
02056          pthread_attr_init(&attr);
02057          pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02058          if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
02059             ast_free(fast);
02060             res = -1;
02061          } else {
02062             res = 0;
02063          }
02064          pthread_attr_destroy(&attr);
02065       }
02066    } else if (!ast_strlen_zero(app)) {
02067          res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
02068       } else {
02069       if (exten && context && pi)
02070             res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
02071       else {
02072          astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
02073          return 0;
02074       }
02075    }   
02076    if (!res)
02077       astman_send_ack(s, m, "Originate successfully queued");
02078    else
02079       astman_send_error(s, m, "Originate failed");
02080    return 0;
02081 }
02082 
02083 /*! \brief Help text for manager command mailboxstatus
02084  */
02085 static char mandescr_mailboxstatus[] = 
02086 "Description: Checks a voicemail account for status.\n"
02087 "Variables: (Names marked with * are required)\n"
02088 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02089 "  ActionID: Optional ActionID for message matching.\n"
02090 "Returns number of messages.\n"
02091 "  Message: Mailbox Status\n"
02092 "  Mailbox: <mailboxid>\n"
02093 "  Waiting: <count>\n"
02094 "\n";
02095 
02096 static int action_mailboxstatus(struct mansession *s, const struct message *m)
02097 {
02098    const char *mailbox = astman_get_header(m, "Mailbox");
02099    const char *id = astman_get_header(m,"ActionID");
02100    char idText[256] = "";
02101    int ret;
02102    if (ast_strlen_zero(mailbox)) {
02103       astman_send_error(s, m, "Mailbox not specified");
02104       return 0;
02105    }
02106         if (!ast_strlen_zero(id))
02107                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02108    ret = ast_app_has_voicemail(mailbox, NULL);
02109    astman_append(s, "Response: Success\r\n"
02110                "%s"
02111                "Message: Mailbox Status\r\n"
02112                "Mailbox: %s\r\n"
02113                "Waiting: %d\r\n\r\n", idText, mailbox, ret);
02114    return 0;
02115 }
02116 
02117 static char mandescr_mailboxcount[] = 
02118 "Description: Checks a voicemail account for new messages.\n"
02119 "Variables: (Names marked with * are required)\n"
02120 "  *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
02121 "  ActionID: Optional ActionID for message matching.\n"
02122 "Returns number of new and old messages.\n"
02123 "  Message: Mailbox Message Count\n"
02124 "  Mailbox: <mailboxid>\n"
02125 "  NewMessages: <count>\n"
02126 "  OldMessages: <count>\n"
02127 "\n";
02128 static int action_mailboxcount(struct mansession *s, const struct message *m)
02129 {
02130    const char *mailbox = astman_get_header(m, "Mailbox");
02131    const char *id = astman_get_header(m,"ActionID");
02132    char idText[256] = "";
02133    int newmsgs = 0, oldmsgs = 0;
02134    if (ast_strlen_zero(mailbox)) {
02135       astman_send_error(s, m, "Mailbox not specified");
02136       return 0;
02137    }
02138    ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
02139    if (!ast_strlen_zero(id)) {
02140       snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
02141    }
02142    astman_append(s, "Response: Success\r\n"
02143                "%s"
02144                "Message: Mailbox Message Count\r\n"
02145                "Mailbox: %s\r\n"
02146                "NewMessages: %d\r\n"
02147                "OldMessages: %d\r\n" 
02148                "\r\n",
02149                 idText,mailbox, newmsgs, oldmsgs);
02150    return 0;
02151 }
02152 
02153 static char mandescr_extensionstate[] = 
02154 "Description: Report the extension state for given extension.\n"
02155 "  If the extension has a hint, will use devicestate to check\n"
02156 "  the status of the device connected to the extension.\n"
02157 "Variables: (Names marked with * are required)\n"
02158 "  *Exten: Extension to check state on\n"
02159 "  *Context: Context for extension\n"
02160 "  ActionId: Optional ID for this transaction\n"
02161 "Will return an \"Extension Status\" message.\n"
02162 "The response will include the hint for the extension and the status.\n";
02163 
02164 static int action_extensionstate(struct mansession *s, const struct message *m)
02165 {
02166    const char *exten = astman_get_header(m, "Exten");
02167    const char *context = astman_get_header(m, "Context");
02168    const char *id = astman_get_header(m,"ActionID");
02169    char idText[256] = "";
02170    char hint[256] = "";
02171    int status;
02172    if (ast_strlen_zero(exten)) {
02173       astman_send_error(s, m, "Extension not specified");
02174       return 0;
02175    }
02176    if (ast_strlen_zero(context))
02177       context = "default";
02178    status = ast_extension_state(NULL, context, exten);
02179    ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
02180         if (!ast_strlen_zero(id)) {
02181                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02182         }
02183    astman_append(s, "Response: Success\r\n"
02184                     "%s"
02185                "Message: Extension Status\r\n"
02186                "Exten: %s\r\n"
02187                "Context: %s\r\n"
02188                "Hint: %s\r\n"
02189                "Status: %d\r\n\r\n",
02190                idText,exten, context, hint, status);
02191    return 0;
02192 }
02193 
02194 static char mandescr_timeout[] = 
02195 "Description: Hangup a channel after a certain time.\n"
02196 "Variables: (Names marked with * are required)\n"
02197 "  *Channel: Channel name to hangup\n"
02198 "  *Timeout: Maximum duration of the call (sec)\n"
02199 "Acknowledges set time with 'Timeout Set' message\n";
02200 
02201 static int action_timeout(struct mansession *s, const struct message *m)
02202 {
02203    struct ast_channel *c = NULL;
02204    const char *name = astman_get_header(m, "Channel");
02205    int timeout = atoi(astman_get_header(m, "Timeout"));
02206    if (ast_strlen_zero(name)) {
02207       astman_send_error(s, m, "No channel specified");
02208       return 0;
02209    }
02210    if (!timeout) {
02211       astman_send_error(s, m, "No timeout specified");
02212       return 0;
02213    }
02214    c = ast_get_channel_by_name_locked(name);
02215    if (!c) {
02216       astman_send_error(s, m, "No such channel");
02217       return 0;
02218    }
02219    ast_channel_setwhentohangup(c, timeout);
02220    ast_channel_unlock(c);
02221    astman_send_ack(s, m, "Timeout Set");
02222    return 0;
02223 }
02224 
02225 static int process_events(struct mansession *s)
02226 {
02227    struct eventqent *eqe;
02228    int ret = 0;
02229    ast_mutex_lock(&s->session->__lock);
02230    if (!s->session->eventq)
02231       s->session->eventq = master_eventq;
02232    while(s->session->eventq->next) {
02233       eqe = s->session->eventq->next;
02234       if ((s->session->authenticated && (s->session->readperm & eqe->category) == eqe->category) &&
02235                ((s->session->send_events & eqe->category) == eqe->category)) {
02236          if (s->fd > -1) {
02237             if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->session->writetimeout) < 0)
02238                ret = -1;
02239          } else if (!s->session->outputstr && !(s->session->outputstr = ast_calloc(1, sizeof(*s->session->outputstr)))) 
02240             ret = -1;
02241          else 
02242             ast_dynamic_str_append(&s->session->outputstr, 0, "%s", eqe->eventdata);
02243       }
02244       unuse_eventqent(s->session->eventq);
02245       s->session->eventq = eqe;
02246    }
02247    ast_mutex_unlock(&s->session->__lock);
02248    return ret;
02249 }
02250 
02251 static char mandescr_userevent[] =
02252 "Description: Send an event to manager sessions.\n"
02253 "Variables: (Names marked with * are required)\n"
02254 "       *UserEvent: EventStringToSend\n"
02255 "       Header1: Content1\n"
02256 "       HeaderN: ContentN\n";
02257 
02258 static int action_userevent(struct mansession *s, const struct message *m)
02259 {
02260    const char *event = astman_get_header(m, "UserEvent");
02261    char body[2048] = "";
02262    int x, bodylen = 0, xlen;
02263    for (x = 0; x < m->hdrcount; x++) {
02264       if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
02265          if (sizeof(body) < bodylen + (xlen = strlen(m->headers[x])) + 3) {
02266             ast_log(LOG_WARNING, "UserEvent exceeds our buffer length.  Truncating.\n");
02267             break;
02268          }
02269          ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
02270          bodylen += xlen;
02271          ast_copy_string(body + bodylen, "\r\n", 3);
02272          bodylen += 2;
02273       }
02274    }
02275 
02276    manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
02277    return 0;
02278 }
02279 
02280 static char mandescr_coresettings[] =
02281 "Description: Query for Core PBX settings.\n"
02282 "Variables: (Names marked with * are optional)\n"
02283 "       *ActionID: ActionID of this transaction\n";
02284 
02285 /*! \brief Show PBX core settings information */
02286 static int action_coresettings(struct mansession *s, const struct message *m)
02287 {
02288    const char *actionid = astman_get_header(m, "ActionID");
02289    char idText[150] = "";
02290 
02291    if (!ast_strlen_zero(actionid))
02292       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02293 
02294    astman_append(s, "Response: Success\r\n"
02295          "%s"
02296          "AMIversion: %s\r\n"
02297          "AsteriskVersion: %s\r\n"
02298          "SystemName: %s\r\n"
02299          "CoreMaxCalls: %d\r\n"
02300          "CoreMaxLoadAvg: %f\r\n"
02301          "CoreRunUser: %s\r\n"
02302          "CoreRunGroup: %s\r\n"
02303          "CoreMaxFilehandles: %d\r\n" 
02304          "CoreRealTimeEnabled: %s\r\n"
02305          "CoreCDRenabled: %s\r\n"
02306          "CoreHTTPenabled: %s\r\n"
02307          "\r\n",
02308          idText,
02309          AMI_VERSION,
02310          ASTERISK_VERSION, 
02311          ast_config_AST_SYSTEM_NAME,
02312          option_maxcalls,
02313          option_maxload,
02314          ast_config_AST_RUN_USER,
02315          ast_config_AST_RUN_GROUP,
02316          option_maxfiles,
02317          ast_realtime_enabled() ? "Yes" : "No",
02318          check_cdr_enabled() ? "Yes" : "No",
02319          check_webmanager_enabled() ? "Yes" : "No"
02320          );
02321    return 0;
02322 }
02323 
02324 static char mandescr_corestatus[] =
02325 "Description: Query for Core PBX status.\n"
02326 "Variables: (Names marked with * are optional)\n"
02327 "       *ActionID: ActionID of this transaction\n";
02328 
02329 /*! \brief Show PBX core status information */
02330 static int action_corestatus(struct mansession *s, const struct message *m)
02331 {
02332    const char *actionid = astman_get_header(m, "ActionID");
02333    char idText[150];
02334    char startuptime[150];
02335    char reloadtime[150];
02336    struct tm tm;
02337 
02338    if (!ast_strlen_zero(actionid))
02339       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
02340 
02341    ast_localtime(&ast_startuptime, &tm, NULL);
02342    strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
02343    ast_localtime(&ast_lastreloadtime, &tm, NULL);
02344    strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
02345 
02346    astman_append(s, "Response: Success\r\n"
02347          "%s"
02348          "CoreStartupTime: %s\r\n"
02349          "CoreReloadTime: %s\r\n"
02350          "CoreCurrentCalls: %d\r\n"
02351          "\r\n",
02352          idText,
02353          startuptime,
02354          reloadtime,
02355          ast_active_channels()
02356          );
02357    return 0;
02358 }
02359 
02360 static int process_message(struct mansession *s, const struct message *m)
02361 {
02362    char action[80] = "";
02363    struct manager_action *tmp;
02364    const char *id = astman_get_header(m,"ActionID");
02365    char idText[256] = "";
02366    int ret = 0;
02367 
02368    ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
02369    if (option_debug)
02370       ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
02371 
02372    if (ast_strlen_zero(action)) {
02373       astman_send_error(s, m, "Missing action in request");
02374       return 0;
02375    }
02376    if (!ast_strlen_zero(id)) {
02377       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
02378    }
02379    if (!s->session->authenticated) {
02380       if (!strcasecmp(action, "Challenge")) {
02381          const char *authtype = astman_get_header(m, "AuthType");
02382 
02383          if (!strcasecmp(authtype, "MD5")) {
02384             if (ast_strlen_zero(s->session->challenge))
02385                snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
02386             astman_append(s, "Response: Success\r\n"
02387                   "%s"
02388                   "Challenge: %s\r\n\r\n",
02389                   idText, s->session->challenge);
02390             return 0;
02391          } else {
02392             astman_send_error(s, m, "Must specify AuthType");
02393             return 0;
02394          }
02395       } else if (!strcasecmp(action, "Login")) {
02396          if (authenticate(s, m)) {
02397             sleep(1);
02398             astman_send_error(s, m, "Authentication failed");
02399             return -1;
02400          } else {
02401             s->session->authenticated = 1;
02402             if (option_verbose > 1) {
02403                if (displayconnects) {
02404                   ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", 
02405                      (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02406                }
02407             }
02408             ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", 
02409                (s->session->sessiontimeout ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
02410             astman_send_ack(s, m, "Authentication accepted");
02411          }
02412       } else if (!strcasecmp(action, "Logoff")) {
02413          astman_send_ack(s, m, "See ya");
02414          return -1;
02415       } else
02416          astman_send_error(s, m, "Authentication Required");
02417    } else {
02418       if (!strcasecmp(action, "Login"))
02419          astman_send_ack(s, m, "Already logged in");
02420       else {
02421          ast_rwlock_rdlock(&actionlock);
02422          for (tmp = first_action; tmp; tmp = tmp->next) {      
02423             if (strcasecmp(action, tmp->action))
02424                continue;
02425             if ((s->session->writeperm & tmp->authority) == tmp->authority) {
02426                if (tmp->func(s, m))
02427                   ret = -1;
02428             } else
02429                astman_send_error(s, m, "Permission denied");
02430             break;
02431          }
02432          ast_rwlock_unlock(&actionlock);
02433          if (!tmp)
02434             astman_send_error(s, m, "Invalid/unknown command");
02435       }
02436    }
02437    if (ret)
02438       return ret;
02439    return process_events(s);
02440 }
02441 
02442 static int get_input(struct mansession_session *s, char *output)
02443 {
02444    /* output must have at least sizeof(s->inbuf) space */
02445    int res;
02446    int x;
02447    struct pollfd fds[1];
02448    for (x = 1; x < s->inlen; x++) {
02449       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
02450          /* Copy output data up to and including \r\n */
02451          memcpy(output, s->inbuf, x + 1);
02452          /* Add trailing \0 */
02453          output[x+1] = '\0';
02454          /* Move remaining data back to the front */
02455          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
02456          s->inlen -= (x + 1);
02457          return 1;
02458       }
02459    } 
02460    if (s->inlen >= sizeof(s->inbuf) - 1) {
02461       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
02462       s->inlen = 0;
02463    }
02464    fds[0].fd = s->fd;
02465    fds[0].events = POLLIN;
02466    do {
02467       ast_mutex_lock(&s->__lock);
02468       if (s->pending_event) {
02469          s->pending_event = 0;
02470          ast_mutex_unlock(&s->__lock);
02471          return 0;
02472       }
02473       s->waiting_thread = pthread_self();
02474       ast_mutex_unlock(&s->__lock);
02475 
02476       res = ast_poll(fds, 1, -1);
02477 
02478       ast_mutex_lock(&s->__lock);
02479       s->waiting_thread = AST_PTHREADT_NULL;
02480       ast_mutex_unlock(&s->__lock);
02481       if (res < 0) {
02482          if (errno == EINTR || errno == EAGAIN) {
02483             return 0;
02484          }
02485          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02486          return -1;
02487       } else if (res > 0) {
02488          ast_mutex_lock(&s->__lock);
02489          res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
02490          ast_mutex_unlock(&s->__lock);
02491          if (res < 1)
02492             return -1;
02493          break;
02494       }
02495    } while(1);
02496    s->inlen += res;
02497    s->inbuf[s->inlen] = '\0';
02498    return 0;
02499 }
02500 
02501 static int do_message(struct mansession *s)
02502 {
02503    struct message m = { 0 };
02504    char header_buf[sizeof(s->session->inbuf)] = { '\0' };
02505    int res;
02506 
02507    for (;;) {
02508       /* Check if any events are pending and do them if needed */
02509       if (s->session->eventq->next) {
02510          if (process_events(s))
02511             return -1;
02512       }
02513       res = get_input(s->session, header_buf);
02514       if (res == 0) {
02515          continue;
02516       } else if (res > 0) {
02517          /* Strip trailing \r\n */
02518          if (strlen(header_buf) < 2)
02519             continue;
02520          header_buf[strlen(header_buf) - 2] = '\0';
02521          if (ast_strlen_zero(header_buf))
02522             return process_message(s, &m) ? -1 : 0;
02523          else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
02524             m.headers[m.hdrcount++] = ast_strdupa(header_buf);
02525       } else {
02526          return res;
02527       }
02528    }
02529 }
02530 
02531 static void *session_do(void *data)
02532 {
02533    struct mansession_session *session = data;
02534    int res;
02535    struct mansession s = { .session = session, .fd = session->fd };
02536 
02537    astman_append(&s, "Asterisk Call Manager/1.0\r\n");
02538    for (;;) {
02539       if ((res = do_message(&s)) < 0)
02540          break;
02541    }
02542    if (session->authenticated) {
02543       if (option_verbose > 1) {
02544          if (displayconnects) 
02545             ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02546       }
02547       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
02548    } else {
02549       if (option_verbose > 1) {
02550          if (displayconnects)
02551             ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
02552       }
02553       ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr));
02554    }
02555 
02556    /* It is possible under certain circumstances for this session thread
02557       to complete its work and exit *before* the thread that created it
02558       has finished executing the ast_pthread_create_background() function.
02559       If this occurs, some versions of glibc appear to act in a buggy
02560       fashion and attempt to write data into memory that it thinks belongs
02561       to the thread but is in fact not owned by the thread (or may have
02562       been freed completely).
02563 
02564       Causing this thread to yield to other threads at least one time
02565       appears to work around this bug.
02566    */
02567    usleep(1);
02568 
02569    destroy_session(session);
02570    return NULL;
02571 }
02572 
02573 static void *accept_thread(void *ignore)
02574 {
02575    int as;
02576    struct sockaddr_in sin;
02577    socklen_t sinlen;
02578    struct eventqent *eqe;
02579    struct mansession_session *s;
02580    struct protoent *p;
02581    int arg = 1;
02582    int flags;
02583    pthread_attr_t attr;
02584    time_t now;
02585    struct pollfd pfds[1];
02586 
02587    pthread_attr_init(&attr);
02588    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02589 
02590    for (;;) {
02591       time(&now);
02592       AST_LIST_LOCK(&sessions);
02593       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
02594          if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
02595             AST_LIST_REMOVE_CURRENT(&sessions, list);
02596             num_sessions--;
02597             if (s->authenticated && (option_verbose > 1) && displayconnects) {
02598                ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
02599                   s->username, ast_inet_ntoa(s->sin.sin_addr));
02600             }
02601             free_session(s);
02602             break;   
02603          }
02604       }
02605       AST_LIST_TRAVERSE_SAFE_END
02606       /* Purge master event queue of old, unused events, but make sure we
02607          always keep at least one in the queue */
02608       eqe = master_eventq;
02609       while (master_eventq->next && !master_eventq->usecount) {
02610          eqe = master_eventq;
02611          master_eventq = master_eventq->next;
02612          free(eqe);
02613       }
02614       AST_LIST_UNLOCK(&sessions);
02615 
02616       sinlen = sizeof(sin);
02617       pfds[0].fd = asock;
02618       pfds[0].events = POLLIN;
02619       /* Wait for something to happen, but timeout every few seconds so
02620          we can ditch any old manager sessions */
02621       if (ast_poll(pfds, 1, 5000) < 1)
02622          continue;
02623       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
02624       if (as < 0) {
02625          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02626          continue;
02627       }
02628       p = getprotobyname("tcp");
02629       if (p) {
02630          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02631             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02632          }
02633       }
02634       if (!(s = ast_calloc(1, sizeof(*s))))
02635          continue;
02636 
02637       memcpy(&s->sin, &sin, sizeof(sin));
02638       s->writetimeout = 100;
02639       s->waiting_thread = AST_PTHREADT_NULL;
02640 
02641       if (!block_sockets) {
02642          /* For safety, make sure socket is non-blocking */
02643          flags = fcntl(as, F_GETFL);
02644          fcntl(as, F_SETFL, flags | O_NONBLOCK);
02645       } else {
02646          flags = fcntl(as, F_GETFL);
02647          fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
02648       }
02649       ast_mutex_init(&s->__lock);
02650       s->fd = as;
02651       s->send_events = -1;
02652       AST_LIST_LOCK(&sessions);
02653       AST_LIST_INSERT_HEAD(&sessions, s, list);
02654       num_sessions++;
02655       /* Find the last place in the master event queue and hook ourselves
02656          in there */
02657       s->eventq = master_eventq;
02658       while(s->eventq->next)
02659          s->eventq = s->eventq->next;
02660       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02661       AST_LIST_UNLOCK(&sessions);
02662       if (ast_pthread_create_background(&s->t, &attr, session_do, s))
02663          destroy_session(s);
02664    }
02665    pthread_attr_destroy(&attr);
02666    return NULL;
02667 }
02668 
02669 static int append_event(const char *str, int category)
02670 {
02671    struct eventqent *tmp, *prev = NULL;
02672    tmp = ast_malloc(sizeof(*tmp) + strlen(str));
02673 
02674    if (!tmp)
02675       return -1;
02676 
02677    tmp->next = NULL;
02678    tmp->category = category;
02679    strcpy(tmp->eventdata, str);
02680    
02681    if (master_eventq) {
02682       prev = master_eventq;
02683       while (prev->next) 
02684          prev = prev->next;
02685       prev->next = tmp;
02686    } else {
02687       master_eventq = tmp;
02688    }
02689    
02690    tmp->usecount = num_sessions;
02691    
02692    return 0;
02693 }
02694 
02695 /*! \brief  manager_event: Send AMI event to client */
02696 int manager_event(int category, const char *event, const char *fmt, ...)
02697 {
02698    struct mansession_session *s;
02699    char auth[80];
02700    va_list ap;
02701    struct timeval now;
02702    struct ast_dynamic_str *buf;
02703 
02704    /* Abort if there aren't any manager sessions */
02705    if (!num_sessions)
02706       return 0;
02707 
02708    if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
02709       return -1;
02710 
02711    ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
02712          "Event: %s\r\nPrivilege: %s\r\n",
02713           event, authority_to_str(category, auth, sizeof(auth)));
02714 
02715    if (timestampevents) {
02716       now = ast_tvnow();
02717       ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
02718             "Timestamp: %ld.%06lu\r\n",
02719              now.tv_sec, (unsigned long) now.tv_usec);
02720    }
02721 
02722    va_start(ap, fmt);
02723    ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
02724    va_end(ap);
02725    
02726    ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");  
02727    
02728    /* Append event to master list and wake up any sleeping sessions */
02729    AST_LIST_LOCK(&sessions);
02730    append_event(buf->str, category);
02731    AST_LIST_TRAVERSE(&sessions, s, list) {
02732       ast_mutex_lock(&s->__lock);
02733       if (s->waiting_thread != AST_PTHREADT_NULL)
02734          pthread_kill(s->waiting_thread, SIGURG);
02735       else
02736          /* We have an event to process, but the mansession is
02737           * not waiting for it. We still need to indicate that there
02738           * is an event waiting so that get_input processes the pending
02739           * event instead of polling.
02740           */
02741          s->pending_event = 1;
02742       ast_mutex_unlock(&s->__lock);
02743    }
02744    AST_LIST_UNLOCK(&sessions);
02745 
02746    return 0;
02747 }
02748 
02749 int ast_manager_unregister(char *action) 
02750 {
02751    struct manager_action *cur, *prev;
02752    struct timespec tv = { 5, };
02753 
02754    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02755       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02756       return -1;
02757    }
02758    cur = prev = first_action;
02759    while (cur) {
02760       if (!strcasecmp(action, cur->action)) {
02761          prev->next = cur->next;
02762          free(cur);
02763          if (option_verbose > 1) 
02764             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
02765          ast_rwlock_unlock(&actionlock);
02766          return 0;
02767       }
02768       prev = cur;
02769       cur = cur->next;
02770    }
02771    ast_rwlock_unlock(&actionlock);
02772    return 0;
02773 }
02774 
02775 static int manager_state_cb(char *context, char *exten, int state, void *data)
02776 {
02777    /* Notify managers of change */
02778    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
02779    return 0;
02780 }
02781 
02782 static int ast_manager_register_struct(struct manager_action *act)
02783 {
02784    struct manager_action *cur, *prev = NULL;
02785    int ret;
02786    struct timespec tv = { 5, };
02787 
02788    if (ast_rwlock_timedwrlock(&actionlock, &tv)) {
02789       ast_log(LOG_ERROR, "Could not obtain lock on manager list\n");
02790       return -1;
02791    }
02792    cur = first_action;
02793    while (cur) { /* Walk the list of actions */
02794       ret = strcasecmp(cur->action, act->action);
02795       if (ret == 0) {
02796          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
02797          ast_rwlock_unlock(&actionlock);
02798          return -1;
02799       } else if (ret > 0) {
02800          /* Insert these alphabetically */
02801          if (prev) {
02802             act->next = prev->next;
02803             prev->next = act;
02804          } else {
02805             act->next = first_action;
02806             first_action = act;
02807          }
02808          break;
02809       }
02810       prev = cur; 
02811       cur = cur->next;
02812    }
02813    
02814    if (!cur) {
02815       if (prev)
02816          prev->next = act;
02817       else
02818          first_action = act;
02819       act->next = NULL;
02820    }
02821 
02822    if (option_verbose > 1) 
02823       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
02824    ast_rwlock_unlock(&actionlock);
02825    return 0;
02826 }
02827 
02828 /*! \brief register a new command with manager, including online help. This is 
02829    the preferred way to register a manager command */
02830 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
02831 {
02832    struct manager_action *cur;
02833 
02834    cur = ast_malloc(sizeof(*cur));
02835    if (!cur)
02836       return -1;
02837    
02838    cur->action = action;
02839    cur->authority = auth;
02840    cur->func = func;
02841    cur->synopsis = synopsis;
02842    cur->description = description;
02843    cur->next = NULL;
02844 
02845    if (ast_manager_register_struct(cur)) {
02846       ast_free(cur);
02847       return -1;
02848    }
02849 
02850    return 0;
02851 }
02852 /*! @}
02853  END Doxygen group */
02854 
02855 static struct mansession_session *find_session(uint32_t ident)
02856 {
02857    struct mansession_session *s;
02858 
02859    AST_LIST_LOCK(&sessions);
02860    AST_LIST_TRAVERSE(&sessions, s, list) {
02861       ast_mutex_lock(&s->__lock);
02862       if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
02863          s->inuse++;
02864          break;
02865       }
02866       ast_mutex_unlock(&s->__lock);
02867    }
02868    AST_LIST_UNLOCK(&sessions);
02869 
02870    return s;
02871 }
02872 
02873 int astman_verify_session_readpermissions(uint32_t ident, int perm)
02874 {
02875    int result = 0;
02876    struct mansession_session *s;
02877 
02878    AST_LIST_LOCK(&sessions);
02879    AST_LIST_TRAVERSE(&sessions, s, list) {
02880       ast_mutex_lock(&s->__lock);
02881       if ((s->managerid == ident) && (s->readperm & perm)) {
02882          result = 1;
02883          ast_mutex_unlock(&s->__lock);
02884          break;
02885       }
02886       ast_mutex_unlock(&s->__lock);
02887    }
02888    AST_LIST_UNLOCK(&sessions);
02889    return result;
02890 }
02891 
02892 int astman_verify_session_writepermissions(uint32_t ident, int perm)
02893 {
02894    int result = 0;
02895    struct mansession_session *s;
02896 
02897    AST_LIST_LOCK(&sessions);
02898    AST_LIST_TRAVERSE(&sessions, s, list) {
02899       ast_mutex_lock(&s->__lock);
02900       if ((s->managerid == ident) && (s->writeperm & perm)) {
02901          result = 1;
02902          ast_mutex_unlock(&s->__lock);
02903          break;
02904       }
02905       ast_mutex_unlock(&s->__lock);
02906    }
02907    AST_LIST_UNLOCK(&sessions);
02908    return result;
02909 }
02910 
02911 enum {
02912    FORMAT_RAW,
02913    FORMAT_HTML,
02914    FORMAT_XML,
02915 };
02916 static char *contenttype[] = { "plain", "html", "xml" };
02917 
02918 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
02919 {
02920    struct mansession_session *s = NULL;
02921    struct mansession ss = { .session = NULL, };
02922    uint32_t ident = 0;
02923    char workspace[512];
02924    char cookie[128];
02925    size_t len = sizeof(workspace);
02926    int blastaway = 0;
02927    char *c = workspace;
02928    char *retval = NULL;
02929    struct ast_variable *v;
02930 
02931    for (v = params; v; v = v->next) {
02932       if (!strcasecmp(v->name, "mansession_id")) {
02933          sscanf(v->value, "%30x", &ident);
02934          break;
02935       }
02936    }
02937    
02938    if (!(s = find_session(ident))) {
02939       /* Create new session */
02940       if (!(s = ast_calloc(1, sizeof(*s)))) {
02941          *status = 500;
02942          goto generic_callback_out;
02943       }
02944       memcpy(&s->sin, requestor, sizeof(s->sin));
02945       s->fd = -1;
02946       s->waiting_thread = AST_PTHREADT_NULL;
02947       s->send_events = 0;
02948       ast_mutex_init(&s->__lock);
02949       ast_mutex_lock(&s->__lock);
02950       s->inuse = 1;
02951       /*!\note There is approximately a 1 in 1.8E19 chance that the following
02952        * calculation will produce 0, which is an invalid ID, but due to the
02953        * properties of the rand() function (and the constantcy of s), that
02954        * won't happen twice in a row.
02955        */
02956       while ((s->managerid = rand() ^ (unsigned long) s) == 0);
02957       AST_LIST_LOCK(&sessions);
02958       AST_LIST_INSERT_HEAD(&sessions, s, list);
02959       /* Hook into the last spot in the event queue */
02960       s->eventq = master_eventq;
02961       while (s->eventq->next)
02962          s->eventq = s->eventq->next;
02963       ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
02964       ast_atomic_fetchadd_int(&num_sessions, 1);
02965       AST_LIST_UNLOCK(&sessions);
02966    }
02967 
02968    /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
02969    time(&s->sessiontimeout);
02970    if (!s->authenticated && (httptimeout > 5))
02971       s->sessiontimeout += 5;
02972    else
02973       s->sessiontimeout += httptimeout;
02974    ss.session = s;
02975    ast_mutex_unlock(&s->__lock);
02976 
02977    ss.f = tmpfile();
02978    ss.fd = fileno(ss.f);
02979 
02980    if (s) {
02981       struct message m = { 0 };
02982       char tmp[80];
02983       unsigned int x;
02984       size_t hdrlen;
02985 
02986       for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
02987          hdrlen = strlen(v->name) + strlen(v->value) + 3;
02988          m.headers[m.hdrcount] = alloca(hdrlen);
02989          snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
02990          m.hdrcount = x + 1;
02991       }
02992 
02993       if (process_message(&ss, &m)) {
02994          if (s->authenticated) {
02995             if (option_verbose > 1) {
02996                if (displayconnects) 
02997                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
02998             }
02999             ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
03000          } else {
03001             if (option_verbose > 1) {
03002                if (displayconnects)
03003                   ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
03004             }
03005             ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
03006          }
03007          s->needdestroy = 1;
03008       }
03009       ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
03010       sprintf(tmp, "%08x", s->managerid);
03011       ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
03012       if (format == FORMAT_HTML)
03013          ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
03014       if (format == FORMAT_XML) {
03015          ast_build_string(&c, &len, "<ajax-response>\n");
03016       } else if (format == FORMAT_HTML) {
03017          ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
03018          ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
03019       }
03020       ast_mutex_lock(&s->__lock);
03021       if (ss.fd > -1) {
03022          char *buf;
03023          size_t l = lseek(ss.fd, 0, SEEK_END);
03024          if (l) {
03025             if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, ss.fd, 0))) {
03026                ast_log(LOG_WARNING, "mmap failed.  Manager request output was not processed\n");
03027             } else {
03028                char *tmpbuf;
03029                if (format == FORMAT_XML)
03030                   tmpbuf = xml_translate(buf, params);
03031                else if (format == FORMAT_HTML)
03032                   tmpbuf = html_translate(buf);
03033                else
03034                   tmpbuf = buf;
03035                if (tmpbuf) {
03036                   size_t wlen, tlen;
03037                   if ((retval = malloc((wlen = strlen(workspace)) + (tlen = strlen(tmpbuf)) + 128))) {
03038                      strcpy(retval, workspace);
03039                      strcpy(retval + wlen, tmpbuf);
03040                      c = retval + wlen + tlen;
03041                      /* Leftover space for footer, if any */
03042                      len = 120;
03043                   }
03044                }
03045                if (tmpbuf != buf)
03046                   free(tmpbuf);
03047                free(s->outputstr);
03048                s->outputstr = NULL;
03049                munmap(buf, l);
03050             }
03051          }
03052          fclose(ss.f);
03053          ss.f = NULL;
03054          ss.fd = -1;
03055       } else if (s->outputstr) {
03056          char *tmp;
03057          if (format == FORMAT_XML)
03058             tmp = xml_translate(s->outputstr->str, params);
03059          else if (format == FORMAT_HTML)
03060             tmp = html_translate(s->outputstr->str);
03061          else
03062             tmp = s->outputstr->str;
03063          if (tmp) {
03064             retval = malloc(strlen(workspace) + strlen(tmp) + 128);
03065             if (retval) {
03066                strcpy(retval, workspace);
03067                strcpy(retval + strlen(retval), tmp);
03068                c = retval + strlen(retval);
03069                len = 120;
03070             }
03071          }
03072          if (tmp != s->outputstr->str)
03073             free(tmp);
03074          free(s->outputstr);
03075          s->outputstr = NULL;
03076       }
03077       ast_mutex_unlock(&s->__lock);
03078       /* Still okay because c would safely be pointing to workspace even
03079          if retval failed to allocate above */
03080       if (format == FORMAT_XML) {
03081          ast_build_string(&c, &len, "</ajax-response>\n");
03082       } else if (format == FORMAT_HTML)
03083          ast_build_string(&c, &len, "</table></body>\r\n");
03084    } else {
03085       *status = 500;
03086       *title = strdup("Server Error");
03087    }
03088    ast_mutex_lock(&s->__lock);
03089    if (s->needdestroy) {
03090       if (s->inuse == 1) {
03091          ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
03092          blastaway = 1;
03093       } else {
03094          ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
03095          if (s->waiting_thread != AST_PTHREADT_NULL)
03096             pthread_kill(s->waiting_thread, SIGURG);
03097          s->inuse--;
03098       }
03099    } else
03100       s->inuse--;
03101    ast_mutex_unlock(&s->__lock);
03102    
03103    if (blastaway)
03104       destroy_session(s);
03105 generic_callback_out:
03106    if (*status != 200)
03107       return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
03108    return retval;
03109 }
03110 
03111 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03112 {
03113    return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
03114 }
03115 
03116 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03117 {
03118    return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
03119 }
03120 
03121 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
03122 {
03123    return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
03124 }
03125 
03126 struct ast_http_uri rawmanuri = {
03127    .description = "Raw HTTP Manager Event Interface",
03128    .uri = "rawman",
03129    .has_subtree = 0,
03130    .callback = rawman_http_callback,
03131 };
03132 
03133 struct ast_http_uri manageruri = {
03134    .description = "HTML Manager Event Interface",
03135    .uri = "manager",
03136    .has_subtree = 0,
03137    .callback = manager_http_callback,
03138 };
03139 
03140 struct ast_http_uri managerxmluri = {
03141    .description = "XML Manager Event Interface",
03142    .uri = "mxml",
03143    .has_subtree = 0,
03144    .callback = mxml_http_callback,
03145 };
03146 
03147 static int registered = 0;
03148 static int webregged = 0;
03149 
03150 int init_manager(void)
03151 {
03152    struct ast_config *cfg = NULL, *ucfg = NULL;
03153    const char *val;
03154    char *cat = NULL;
03155    int oldportno = portno;
03156    static struct sockaddr_in ba;
03157    int x = 1;
03158    int flags;
03159    int newhttptimeout = 60;
03160    struct ast_manager_user *user = NULL;
03161 
03162    if (!registered) {
03163       /* Register default actions */
03164       ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
03165       ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
03166       ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
03167       ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
03168       ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
03169       ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
03170       ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
03171       ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
03172       ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
03173       ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
03174       ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer );
03175       ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
03176       ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
03177       ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
03178       ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
03179       ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
03180       ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
03181       ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
03182       ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
03183       ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
03184       ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
03185       ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
03186 
03187       ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
03188       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
03189       registered = 1;
03190       /* Append placeholder event so master_eventq never runs dry */
03191       append_event("Event: Placeholder\r\n\r\n", 0);
03192    }
03193    portno = DEFAULT_MANAGER_PORT;
03194    displayconnects = 1;
03195    cfg = ast_config_load("manager.conf");
03196    if (!cfg) {
03197       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
03198       return 0;
03199    }
03200    if ((val = ast_variable_retrieve(cfg, "general", "enabled"))) {
03201       manager_enabled = ast_true(val);
03202    }
03203    if ((val = ast_variable_retrieve(cfg, "general", "block-sockets"))) {
03204       block_sockets = ast_true(val);
03205    }
03206    if((val = ast_variable_retrieve(cfg, "general", "webenabled"))) {
03207       webmanager_enabled = ast_true(val);
03208    }
03209    if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
03210       if (sscanf(val, "%5d", &portno) != 1) {
03211          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
03212          portno = DEFAULT_MANAGER_PORT;
03213       }
03214    }
03215 
03216    if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
03217       displayconnects = ast_true(val);
03218    }
03219    if ((val = ast_variable_retrieve(cfg, "general", "timestampevents"))) {
03220       timestampevents = ast_true(val);
03221    }
03222    if ((val = ast_variable_retrieve(cfg, "general", "httptimeout"))) {
03223       newhttptimeout = atoi(val);
03224    }
03225 
03226    memset(&ba, 0, sizeof(ba));
03227    ba.sin_family = AF_INET;
03228    ba.sin_port = htons(portno);
03229 
03230    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
03231       if (!inet_aton(val, &ba.sin_addr)) { 
03232          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
03233          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
03234       }
03235    }
03236    
03237 
03238    if ((asock > -1) && ((portno != oldportno) || !manager_enabled)) {
03239 #if 0
03240       /* Can't be done yet */
03241       close(asock);
03242       asock = -1;
03243 #else
03244       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
03245 #endif
03246    }
03247 
03248    AST_LIST_LOCK(&users);
03249 
03250    if ((ucfg = ast_config_load("users.conf"))) {
03251       while ((cat = ast_category_browse(ucfg, cat))) {
03252          int hasmanager = 0;
03253          struct ast_variable *var = NULL;
03254 
03255          if (!strcasecmp(cat, "general")) {
03256             continue;
03257          }
03258 
03259          if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
03260             continue;
03261          }
03262 
03263          /* Look for an existing entry, if none found - create one and add it to the list */
03264          if (!(user = ast_get_manager_by_name_locked(cat))) {
03265             if (!(user = ast_calloc(1, sizeof(*user)))) {
03266                break;
03267             }
03268             /* Copy name over */
03269             ast_copy_string(user->username, cat, sizeof(user->username));
03270             /* Insert into list */
03271             AST_LIST_INSERT_TAIL(&users, user, list);
03272          }
03273 
03274          /* Make sure we keep this user and don't destroy it during cleanup */
03275          user->keep = 1;
03276 
03277          for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
03278             if (!strcasecmp(var->name, "secret")) {
03279                if (user->secret) {
03280                   free(user->secret);
03281                }
03282                user->secret = ast_strdup(var->value);
03283             } else if (!strcasecmp(var->name, "deny") ) {
03284                if (user->deny) {
03285                   free(user->deny);
03286                }
03287                user->deny = ast_strdup(var->value);
03288             } else if (!strcasecmp(var->name, "permit") ) {
03289                if (user->permit) {
03290                   free(user->permit);
03291                }
03292                user->permit = ast_strdup(var->value);
03293             } else if (!strcasecmp(var->name, "read") ) {
03294                if (user->read) {
03295                   free(user->read);
03296                }
03297                user->read = ast_strdup(var->value);
03298             } else if (!strcasecmp(var->name, "write") ) {
03299                if (user->write) {
03300                   free(user->write);
03301                }
03302                user->write = ast_strdup(var->value);
03303             } else if (!strcasecmp(var->name, "displayconnects") ) {
03304                user->displayconnects = ast_true(var->value);
03305             } else if (!strcasecmp(var->name, "hasmanager")) {
03306                /* already handled */
03307             } else {
03308                ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
03309             }
03310          }
03311       }
03312       ast_config_destroy(ucfg);
03313    }
03314 
03315    while ((cat = ast_category_browse(cfg, cat))) {
03316       struct ast_variable *var = NULL;
03317 
03318       if (!strcasecmp(cat, "general"))
03319          continue;
03320 
03321       /* Look for an existing entry, if none found - create one and add it to the list */
03322       if (!(user = ast_get_manager_by_name_locked(cat))) {
03323          if (!(user = ast_calloc(1, sizeof(*user))))
03324             break;
03325          /* Copy name over */
03326          ast_copy_string(user->username, cat, sizeof(user->username));
03327          /* Insert into list */
03328          AST_LIST_INSERT_TAIL(&users, user, list);
03329       }
03330 
03331       /* Make sure we keep this user and don't destroy it during cleanup */
03332       user->keep = 1;
03333 
03334       var = ast_variable_browse(cfg, cat);
03335       while (var) {
03336          if (!strcasecmp(var->name, "secret")) {
03337             if (user->secret)
03338                free(user->secret);
03339             user->secret = ast_strdup(var->value);
03340          } else if (!strcasecmp(var->name, "deny") ) {
03341             if (user->deny)
03342                free(user->deny);
03343             user->deny = ast_strdup(var->value);
03344          } else if (!strcasecmp(var->name, "permit") ) {
03345             if (user->permit)
03346                free(user->permit);
03347             user->permit = ast_strdup(var->value);
03348          }  else if (!strcasecmp(var->name, "read") ) {
03349             if (user->read)
03350                free(user->read);
03351             user->read = ast_strdup(var->value);
03352          }  else if (!strcasecmp(var->name, "write") ) {
03353             if (user->write)
03354                free(user->write);
03355             user->write = ast_strdup(var->value);
03356          }  else if (!strcasecmp(var->name, "displayconnects") )
03357             user->displayconnects = ast_true(var->value);
03358          else
03359             ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
03360          var = var->next;
03361       }
03362    }
03363 
03364    /* Perform cleanup - essentially prune out old users that no longer exist */
03365    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
03366       if (user->keep) {
03367          user->keep = 0;
03368          continue;
03369       }
03370       /* We do not need to keep this user so take them out of the list */
03371       AST_LIST_REMOVE_CURRENT(&users, list);
03372       /* Free their memory now */
03373       if (user->secret)
03374          free(user->secret);
03375       if (user->deny)
03376          free(user->deny);
03377       if (user->permit)
03378          free(user->permit);
03379       if (user->read)
03380          free(user->read);
03381       if (user->write)
03382          free(user->write);
03383       free(user);
03384    }
03385    AST_LIST_TRAVERSE_SAFE_END
03386 
03387    AST_LIST_UNLOCK(&users);
03388 
03389    ast_config_destroy(cfg);
03390    
03391    if (webmanager_enabled && manager_enabled) {
03392       if (!webregged) {
03393          ast_http_uri_link(&rawmanuri);
03394          ast_http_uri_link(&manageruri);
03395          ast_http_uri_link(&managerxmluri);
03396          webregged = 1;
03397       }
03398    } else {
03399       if (webregged) {
03400          ast_http_uri_unlink(&rawmanuri);
03401          ast_http_uri_unlink(&manageruri);
03402          ast_http_uri_unlink(&managerxmluri);
03403          webregged = 0;
03404       }
03405    }
03406 
03407    if (newhttptimeout > 0)
03408       httptimeout = newhttptimeout;
03409 
03410    /* If not enabled, do nothing */
03411    if (!manager_enabled)
03412       return 0;
03413 
03414    if (asock < 0) {
03415       asock = socket(AF_INET, SOCK_STREAM, 0);
03416       if (asock < 0) {
03417          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
03418          return -1;
03419       }
03420       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
03421       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
03422          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
03423          close(asock);
03424          asock = -1;
03425          return -1;
03426       }
03427       if (listen(asock, 2)) {
03428          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
03429          close(asock);
03430          asock = -1;
03431          return -1;
03432       }
03433       flags = fcntl(asock, F_GETFL);
03434       fcntl(asock, F_SETFL, flags | O_NONBLOCK);
03435       if (option_verbose)
03436          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
03437       ast_pthread_create_background(&t, NULL, accept_thread, NULL);
03438    }
03439    return 0;
03440 }
03441 
03442 int reload_manager(void)
03443 {
03444    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
03445    return init_manager();
03446 }

Generated on Wed Oct 14 15:01:56 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7